Commit c6c1dd31 authored by Jan Provaznik's avatar Jan Provaznik

Merge branch 'issue_292506' into 'master'

Support /parent_epic quick action when creating epic

See merge request gitlab-org/gitlab!52727
parents 224891b3 eae15ddd
......@@ -2,7 +2,9 @@
module Epics
class BaseService < IssuableBaseService
attr_reader :group
extend ::Gitlab::Utils::Override
attr_reader :group, :parent_epic
def initialize(group, current_user, params = {})
@group, @current_user, @params = group, current_user, params
......@@ -10,6 +12,23 @@ module Epics
private
override :handle_quick_actions
def handle_quick_actions(epic)
super
set_quick_action_params
end
def set_quick_action_params
@parent_epic = params.delete(:quick_action_assign_to_parent_epic)
end
def assign_parent_epic_for(epic)
return unless parent_epic
EpicLinks::CreateService.new(parent_epic, current_user, { target_issuable: epic }).execute
end
def available_labels
@available_labels ||= LabelsFinder.new(
current_user,
......
......@@ -6,6 +6,7 @@ module Epics
set_date_params
epic = group.epics.new
create(epic)
end
......@@ -21,6 +22,10 @@ module Epics
end
end
def after_create(epic)
assign_parent_epic_for(epic)
end
def set_date_params
if params[:start_date_fixed] && params[:start_date_is_fixed]
params[:start_date] = params[:start_date_fixed]
......
......@@ -21,6 +21,8 @@ module Epics
epic.reset
end
assign_parent_epic_for(epic)
epic
end
......
---
title: Support /parent_epic quick action when creating new epic
merge_request: 52727
author:
type: added
......@@ -15,7 +15,7 @@ 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? }
condition { action_allowed_only_on_update? }
params '<&epic | group&epic | Epic URL>'
command :child_epic do |epic_param|
child_epic = extract_epic(epic_param)
......@@ -30,7 +30,7 @@ module EE
_("Removes %{epic_ref} from child epics.") % { epic_ref: child_epic.to_reference(quick_action_target) } if child_epic
end
types Epic
condition { action_allowed? }
condition { action_allowed_only_on_update? }
params '<&epic | group&epic | Epic URL>'
command :remove_child_epic do |epic_param|
child_epic = extract_epic(epic_param)
......@@ -57,7 +57,7 @@ module EE
command :parent_epic do |epic_param|
parent_epic = extract_epic(epic_param)
@execution_message[:parent_epic] = set_parent_epic(quick_action_target, parent_epic)
@execution_message[:parent_epic] = set_parent_epic_update(quick_action_target, parent_epic)
end
desc _('Remove parent epic from an epic')
......@@ -67,7 +67,7 @@ module EE
_('Removes parent epic %{epic_ref}.') % { epic_ref: parent_epic.to_reference(quick_action_target) } if parent_epic
end
types Epic
condition { action_allowed? }
condition { action_allowed_only_on_update? }
command :remove_parent_epic do
parent_epic = quick_action_target.parent
......@@ -90,11 +90,14 @@ module EE
end
def action_allowed?
quick_action_target.persisted? &&
quick_action_target.group&.feature_available?(:subepics) &&
quick_action_target.group&.feature_available?(:subepics) &&
current_user.can?(:"admin_#{quick_action_target.to_ability_name}", quick_action_target)
end
def action_allowed_only_on_update?
quick_action_target.persisted? && action_allowed?
end
def epics_related?(epic, target_epic)
epic.child?(target_epic.id) || target_epic.child?(epic.id)
end
......@@ -109,12 +112,12 @@ module EE
_("Added %{epic_ref} as a child epic.") % { epic_ref: child_epic.to_reference(target_epic) }
end
def set_parent_epic(target_epic, parent_epic)
def set_parent_epic_update(target_epic, parent_epic)
return parent_error_message(:not_present) unless parent_epic.present?
return parent_error_message(:already_related) if epics_related?(parent_epic, target_epic)
return parent_error_message(:no_permission) unless current_user.can?(:read_epic, parent_epic)
EpicLinks::CreateService.new(parent_epic, current_user, { target_issuable: target_epic }).execute
@updates[:quick_action_assign_to_parent_epic] = parent_epic
_("Set %{epic_ref} as the parent epic.") % { epic_ref: parent_epic.to_reference(target_epic) }
end
......
......@@ -82,6 +82,38 @@ RSpec.describe Epics::CreateService do
expect(subject.valid?).to be false
end
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
it 'assigns parent epic' do
parent_epic = create(:epic, group: group)
description = "/parent_epic #{parent_epic.to_reference}"
params = { title: 'New epic with parent', description: description }
epic = described_class.new(group, user, params).execute
expect(epic.parent).to eq(parent_epic)
end
context 'when parent epic cannot be assigned' do
it 'does not assign parent epic' do
other_group = create(:group, :private)
parent_epic = create(:epic, group: other_group)
description = "/parent_epic #{parent_epic.to_reference(group)}"
params = { title: 'New epic with parent', description: description }
epic = described_class.new(group, user, params).execute
expect(epic.parent).to eq(nil)
end
end
end
end
end
end
end
......@@ -308,12 +308,40 @@ RSpec.describe Epics::UpdateService do
end
context 'with quick actions in the description' do
let(:label) { create(:group_label, group: group) }
context 'for /label' do
let(:label) { create(:group_label, group: group) }
it 'adds labels to the epic' do
update_epic(description: "/label ~#{label.name}")
expect(epic.label_ids).to contain_exactly(label.id)
end
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)
update_epic(description: "/parent_epic #{parent_epic.to_reference}")
expect(epic.parent).to eq(parent_epic)
end
context 'when parent epic cannot be assigned' do
it 'does not update parent epic' do
other_group = create(:group, :private)
parent_epic = create(:epic, group: other_group)
it 'adds labels to the epic' do
update_epic(description: "/label ~#{label.name}")
update_epic(description: "/parent_epic #{parent_epic.to_reference(group)}")
expect(epic.label_ids).to contain_exactly(label.id)
expect(epic.parent).to eq(nil)
end
end
end
end
end
......
......@@ -1112,7 +1112,17 @@ RSpec.describe QuickActions::InterpretService do
group.add_developer(current_user)
end
shared_examples 'adds epic relation' do |relation|
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'}
......@@ -1171,7 +1181,7 @@ RSpec.describe QuickActions::InterpretService do
end
context 'child_epic command' do
it_behaves_like 'adds epic relation', :child
it_behaves_like 'returning execution messages', :child
context 'when epic is already a child epic' do
let(:content) { "/child_epic #{epic2&.to_reference(epic)}" }
......@@ -1260,7 +1270,8 @@ RSpec.describe QuickActions::InterpretService do
end
context 'parent_epic command' do
it_behaves_like 'adds epic relation', :parent
it_behaves_like 'quick action parameters', :quick_action_assign_to_parent_epic, :parent_epic
it_behaves_like 'returning execution messages', :parent
context 'when epic is already a parent epic' do
let(:content) { "/parent_epic #{epic2&.to_reference(epic)}" }
......@@ -1289,9 +1300,9 @@ 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', :parent_epic
it_behaves_like 'quick action parameters', :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