Commit 27ca980b authored by Felipe Artur's avatar Felipe Artur

Support /child_epic quick action when creating epics

Allow to assign child epics with quick actions when creating epics
parent 643b55f6
......@@ -4,7 +4,7 @@ module Epics
class BaseService < IssuableBaseService
extend ::Gitlab::Utils::Override
attr_reader :group, :parent_epic
attr_reader :group, :parent_epic, :child_epic
def initialize(group, current_user, params = {})
@group, @current_user, @params = group, current_user, params
......@@ -21,6 +21,7 @@ module Epics
def set_quick_action_params
@parent_epic = params.delete(:quick_action_assign_to_parent_epic)
@child_epic = params.delete(:quick_action_assign_child_epic)
end
def assign_parent_epic_for(epic)
......@@ -29,6 +30,12 @@ module Epics
EpicLinks::CreateService.new(parent_epic, current_user, { target_issuable: epic }).execute
end
def assign_child_epic_for(epic)
return unless child_epic
EpicLinks::CreateService.new(epic, current_user, { target_issuable: child_epic }).execute
end
def available_labels
@available_labels ||= LabelsFinder.new(
current_user,
......
......@@ -24,6 +24,7 @@ module Epics
def after_create(epic)
assign_parent_epic_for(epic)
assign_child_epic_for(epic)
end
def set_date_params
......
......@@ -22,6 +22,7 @@ module Epics
end
assign_parent_epic_for(epic)
assign_child_epic_for(epic)
epic
end
......
---
title: Support /child quick action when creating epics
merge_request: 53073
author:
type: added
......@@ -15,12 +15,12 @@ module EE
_("Adds %{epic_ref} as child epic.") % { epic_ref: child_epic.to_reference(quick_action_target) } if child_epic
end
types Epic
condition { action_allowed_only_on_update? }
condition { action_allowed? }
params '<&epic | group&epic | Epic URL>'
command :child_epic do |epic_param|
child_epic = extract_epic(epic_param)
@execution_message[:child_epic] = add_child_epic(quick_action_target, child_epic)
@execution_message[:child_epic] = set_child_epic_update(quick_action_target, child_epic)
end
desc _('Remove child epic from an epic')
......@@ -102,12 +102,12 @@ module EE
epic.child?(target_epic.id) || target_epic.child?(epic.id)
end
def add_child_epic(target_epic, child_epic)
def set_child_epic_update(target_epic, child_epic)
return child_error_message(:not_present) unless child_epic.present?
return child_error_message(:already_related) if epics_related?(child_epic, target_epic)
return child_error_message(:no_permission) unless current_user.can?(:read_epic, child_epic)
EpicLinks::CreateService.new(target_epic, current_user, { target_issuable: child_epic }).execute
@updates[:quick_action_assign_child_epic] = child_epic
_("Added %{epic_ref} as a child epic.") % { epic_ref: child_epic.to_reference(target_epic) }
end
......
......@@ -84,12 +84,12 @@ RSpec.describe Epics::CreateService do
end
context 'when description param has quick action' do
context 'for /parent_epic' do
before do
stub_licensed_features(epics: true, subepics: true)
group.add_developer(user)
end
context 'for /parent_epic' do
it 'assigns parent epic' do
parent_epic = create(:epic, group: group)
description = "/parent_epic #{parent_epic.to_reference}"
......@@ -113,6 +113,31 @@ RSpec.describe Epics::CreateService do
end
end
end
context 'for /child_epic' do
it 'sets a child epic' do
child_epic = create(:epic, group: group)
description = "/child_epic #{child_epic.to_reference}"
params = { title: 'New epic with child', description: description }
epic = described_class.new(group, user, params).execute
expect(epic.reload.children).to include(child_epic)
end
context 'when child epic cannot be assigned' do
it 'does not set child epic' do
other_group = create(:group, :private)
child_epic = create(:epic, group: other_group)
description = "/child_epic #{child_epic.to_reference(group)}"
params = { title: 'New epic with child', description: description }
epic = described_class.new(group, user, params).execute
expect(epic.reload.children).to be_empty
end
end
end
end
end
end
......
......@@ -308,6 +308,11 @@ RSpec.describe Epics::UpdateService do
end
context 'with quick actions in the description' do
before do
stub_licensed_features(epics: true, subepics: true)
group.add_developer(user)
end
context 'for /label' do
let(:label) { create(:group_label, group: group) }
......@@ -319,11 +324,6 @@ RSpec.describe Epics::UpdateService do
end
context 'for /parent_epic' do
before do
stub_licensed_features(epics: true, subepics: true)
group.add_developer(user)
end
it 'assigns parent epic' do
parent_epic = create(:epic, group: epic.group)
......@@ -343,6 +343,26 @@ RSpec.describe Epics::UpdateService do
end
end
end
context 'for /child_epic' do
it 'sets a child epic' do
child_epic = create(:epic, group: group)
update_epic(description: "/child_epic #{child_epic.to_reference}")
expect(epic.reload.children).to include(child_epic)
end
context 'when child epic cannot be assigned' do
it 'does not set child epic' do
other_group = create(:group, :private)
child_epic = create(:epic, group: other_group)
update_epic(description: "/child_epic #{child_epic.to_reference(group)}")
expect(epic.reload.children).to be_empty
end
end
end
end
end
end
......@@ -35,6 +35,57 @@ RSpec.describe QuickActions::InterpretService do
end
end
shared_examples 'adds quick action parameter' do |parameter_key, quick_action|
let(:content) { "/#{quick_action} #{referenced_epic&.to_reference(epic)}" }
it 'adds parameter to updates array' do
_, updates = service.execute(content, epic)
expect(updates[parameter_key]).to eq(referenced_epic)
end
end
shared_examples 'does not add quick action parameter' do |parameter_key, quick_action|
let(:content) { "/#{quick_action} #{referenced_epic&.to_reference(epic)}" }
it 'does not add parameter to updates array' do
_, updates = service.execute(content, epic)
expect(updates[parameter_key]).to eq(nil)
end
end
shared_examples 'returns execution messages' do |relation|
context 'when correct epic reference' do
let(:content) { "/#{relation}_epic #{epic2&.to_reference(epic)}" }
let(:explain_action) { relation == :child ? 'Adds' : 'Sets'}
let(:execute_action) { relation == :child ? 'Added' : 'Set'}
let(:article) { relation == :child ? 'a' : 'the'}
it 'returns explain message with epic reference' do
_, explanations = service.explain(content, epic)
expect(explanations)
.to eq(["#{explain_action} #{epic2.group.name}&#{epic2.iid} as #{relation} epic."])
end
it 'returns successful execution message' do
_, _, message = service.execute(content, epic)
expect(message)
.to eq("#{execute_action} #{epic2.group.name}&#{epic2.iid} as #{article} #{relation} epic.")
end
end
context 'when epic reference is wrong' do |relation|
let(:content) { "/#{relation}_epic qwe" }
it 'returns empty explain message' do
_, explanations = service.explain(content, epic)
expect(explanations).to eq([])
end
end
end
describe '#execute' do
let(:merge_request) { create(:merge_request, source_project: project) }
......@@ -537,25 +588,7 @@ RSpec.describe QuickActions::InterpretService do
let(:merge_request) { create(:merge_request, source_project: project) }
let(:epic) { create(:epic, group: group) }
let(:child_epic) { create(:epic, group: group) }
let(:content) { "/child_epic #{child_epic&.to_reference(epic)}" }
shared_examples 'epic relation is not added' do
it 'does not add child epic to epic' do
service.execute(content, epic)
child_epic.reload
expect(child_epic.parent).to be_nil
end
end
shared_examples 'epic relation is added' do
it 'adds child epic relation to the epic' do
service.execute(content, epic)
child_epic.reload
expect(child_epic.parent).to eq(epic)
end
end
let(:referenced_epic) { child_epic }
context 'when subepics are enabled' do
before do
......@@ -563,7 +596,7 @@ RSpec.describe QuickActions::InterpretService do
end
context 'when a user does not have permissions to add epic relations' do
it_behaves_like 'epic relation is not added'
it_behaves_like 'does not add quick action parameter', :quick_action_assign_child_epic, :child_epic
it_behaves_like 'quick action is unavailable', :child_epic do
let(:target) { epic }
end
......@@ -575,7 +608,7 @@ RSpec.describe QuickActions::InterpretService do
another_group.add_developer(current_user)
end
it_behaves_like 'epic relation is added'
it_behaves_like 'adds quick action parameter', :quick_action_assign_child_epic, :child_epic
it_behaves_like 'quick action is available', :child_epic do
let(:target) { epic }
......@@ -590,63 +623,58 @@ RSpec.describe QuickActions::InterpretService do
end
context 'when target epic is not persisted yet' do
let(:target) { build(:epic, group: group) }
let(:epic) { build(:epic, group: group) }
it_behaves_like 'quick action is unavailable', :child_epic
it_behaves_like 'adds quick action parameter', :quick_action_assign_child_epic, :child_epic
end
context 'when passed child epic is nil' do
let(:child_epic) { nil }
it 'does not add child epic to epic' do
expect { service.execute(content, epic) }.not_to change { epic.children.count }
expect { service.execute(content, epic) }.not_to raise_error
end
it_behaves_like 'does not add quick action parameter', :quick_action_assign_child_epic, :child_epic
it 'does not raise error' do
content = "/child_epic "
expect { service.execute(content, epic) }.not_to raise_error
end
end
context 'when child_epic is already linked to an epic' do
let(:another_epic) { create(:epic, group: group) }
let(:referenced_epic) { create(:epic, group: group) }
before do
child_epic.update!(parent: another_epic)
child_epic.update!(parent: referenced_epic)
end
it_behaves_like 'epic relation is added'
it_behaves_like 'quick action is available', :child_epic do
let(:target) { epic }
end
end
context 'when child epic is in a subgroup of parent epic' do
let(:child_epic) { create(:epic, group: subgroup) }
let(:referenced_epic) { create(:epic, group: subgroup) }
it_behaves_like 'epic relation is added'
it_behaves_like 'quick action is available', :child_epic do
let(:target) { epic }
end
end
context 'when child epic is in a parent group of the parent epic' do
let(:child_epic) { create(:epic, group: group) }
let(:referenced_epic) { create(:epic, group: group) }
before do
epic.update!(group: subgroup)
end
it_behaves_like 'epic relation is not added'
it_behaves_like 'quick action is available', :child_epic do
let(:target) { epic }
end
end
context 'when child epic is in a different group than parent epic' do
let(:child_epic) { create(:epic, group: another_group) }
let(:referenced_epic) { create(:epic, group: another_group) }
it_behaves_like 'epic relation is not added'
it_behaves_like 'quick action is available', :child_epic do
let(:target) { epic }
end
......@@ -659,7 +687,7 @@ RSpec.describe QuickActions::InterpretService do
group.add_developer(current_user)
end
it_behaves_like 'epic relation is not added'
it_behaves_like 'does not add quick action parameter', :quick_action_assign_child_epic, :child_epic
it_behaves_like 'quick action is unavailable', :child_epic do
let(:target) { epic }
end
......@@ -1112,47 +1140,6 @@ RSpec.describe QuickActions::InterpretService do
group.add_developer(current_user)
end
shared_examples 'quick action parameters' do |parameter, quick_action|
let(:content) { "/#{quick_action} #{epic2&.to_reference(epic)}" }
it 'adds parameter to updates array' do
_, updates = service.execute(content, epic)
expect(updates[:quick_action_assign_to_parent_epic]).to eq(epic2)
end
end
shared_examples 'returning execution messages' do |relation|
context 'when correct epic reference' do
let(:content) { "/#{relation}_epic #{epic2&.to_reference(epic)}" }
let(:explain_action) { relation == :child ? 'Adds' : 'Sets'}
let(:execute_action) { relation == :child ? 'Added' : 'Set'}
let(:article) { relation == :child ? 'a' : 'the'}
it 'returns explain message with epic reference' do
_, explanations = service.explain(content, epic)
expect(explanations)
.to eq(["#{explain_action} #{epic2.group.name}&#{epic2.iid} as #{relation} epic."])
end
it 'returns successful execution message' do
_, _, message = service.execute(content, epic)
expect(message)
.to eq("#{execute_action} #{epic2.group.name}&#{epic2.iid} as #{article} #{relation} epic.")
end
end
context 'when epic reference is wrong' do |relation|
let(:content) { "/#{relation}_epic qwe" }
it 'returns empty explain message' do
_, explanations = service.explain(content, epic)
expect(explanations).to eq([])
end
end
end
shared_examples 'target epic does not exist' do |relation|
it 'returns unsuccessful execution message' do
_, _, message = service.execute(content, epic)
......@@ -1181,7 +1168,7 @@ RSpec.describe QuickActions::InterpretService do
end
context 'child_epic command' do
it_behaves_like 'returning execution messages', :child
it_behaves_like 'returns execution messages', :child
context 'when epic is already a child epic' do
let(:content) { "/child_epic #{epic2&.to_reference(epic)}" }
......@@ -1270,8 +1257,10 @@ RSpec.describe QuickActions::InterpretService do
end
context 'parent_epic command' do
it_behaves_like 'quick action parameters', :quick_action_assign_to_parent_epic, :parent_epic
it_behaves_like 'returning execution messages', :parent
let(:referenced_epic) { epic2 }
it_behaves_like 'adds quick action parameter', :quick_action_assign_to_parent_epic, :parent_epic
it_behaves_like 'returns execution messages', :parent
context 'when epic is already a parent epic' do
let(:content) { "/parent_epic #{epic2&.to_reference(epic)}" }
......@@ -1301,8 +1290,9 @@ RSpec.describe QuickActions::InterpretService do
context 'when target epic is not persisted yet' do
let(:epic) { build(:epic, group: group) }
let(:referenced_epic) { epic2 }
it_behaves_like 'quick action parameters', :quick_action_assign_to_parent_epic, :parent_epic
it_behaves_like 'adds quick action parameter', :quick_action_assign_to_parent_epic, :parent_epic
end
context 'when user has no permission to read epic' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment