Skip to content
代码片段 群组 项目
未验证 提交 ab9c8dbc 编辑于 作者: Heinrich Lee Yu's avatar Heinrich Lee Yu
浏览文件

Add mutation to move to start / end of board lists

Updates the GraphQL mutation so that we can move to the start or end of
a board list.

Changelog: added
上级 7187d5a0
No related branches found
No related tags found
无相关合并请求
...@@ -38,10 +38,16 @@ class IssueMoveList < Mutations::Issues::Base ...@@ -38,10 +38,16 @@ class IssueMoveList < Mutations::Issues::Base
required: false, required: false,
description: 'ID of issue that should be placed after the current issue.' description: 'ID of issue that should be placed after the current issue.'
argument :position_in_list, GraphQL::Types::Int,
required: false,
description: "Position of issue within the board list. Positions start at 0. "\
"Use #{::Boards::Issues::MoveService::LIST_END_POSITION} to move to the end of the list."
def ready?(**args) def ready?(**args)
if move_arguments(args).blank? if move_arguments(args).blank?
raise Gitlab::Graphql::Errors::ArgumentError, raise Gitlab::Graphql::Errors::ArgumentError,
'At least one of the arguments fromListId, toListId, afterId or beforeId is required' 'At least one of the arguments ' \
'fromListId, toListId, positionInList, moveAfterId, or moveBeforeId is required'
end end
if move_list_arguments(args).one? if move_list_arguments(args).one?
...@@ -49,6 +55,24 @@ def ready?(**args) ...@@ -49,6 +55,24 @@ def ready?(**args)
'Both fromListId and toListId must be present' 'Both fromListId and toListId must be present'
end end
if args[:position_in_list].present?
if move_list_arguments(args).empty?
raise Gitlab::Graphql::Errors::ArgumentError,
'Both fromListId and toListId are required when positionInList is given'
end
if args[:move_before_id].present? || args[:move_after_id].present?
raise Gitlab::Graphql::Errors::ArgumentError,
'positionInList is mutually exclusive with any of moveBeforeId or moveAfterId'
end
if args[:position_in_list] != ::Boards::Issues::MoveService::LIST_END_POSITION &&
args[:position_in_list] < 0
raise Gitlab::Graphql::Errors::ArgumentError,
"positionInList must be >= 0 or #{::Boards::Issues::MoveService::LIST_END_POSITION}"
end
end
super super
end end
...@@ -77,7 +101,7 @@ def move_list_arguments(args) ...@@ -77,7 +101,7 @@ def move_list_arguments(args)
end end
def move_arguments(args) def move_arguments(args)
args.slice(:from_list_id, :to_list_id, :move_after_id, :move_before_id) args.slice(:from_list_id, :to_list_id, :position_in_list, :move_after_id, :move_before_id)
end end
def error_for(result) def error_for(result)
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
module Boards module Boards
class BaseItemMoveService < Boards::BaseService class BaseItemMoveService < Boards::BaseService
LIST_END_POSITION = -1
def execute(issuable) def execute(issuable)
issuable_modification_params = issuable_params(issuable) issuable_modification_params = issuable_params(issuable)
return if issuable_modification_params.empty? return if issuable_modification_params.empty?
...@@ -32,7 +34,13 @@ def issuable_params(issuable) ...@@ -32,7 +34,13 @@ def issuable_params(issuable)
) )
end end
reposition_ids = move_between_ids(params) move_params = if params[:position_in_list].present?
move_params_from_list_position(params[:position_in_list])
else
params
end
reposition_ids = move_between_ids(move_params)
attrs.merge!(reposition_params(reposition_ids)) if reposition_ids attrs.merge!(reposition_params(reposition_ids)) if reposition_ids
attrs attrs
...@@ -90,6 +98,18 @@ def board_label_ids ...@@ -90,6 +98,18 @@ def board_label_ids
::Label.ids_on_board(board.id) ::Label.ids_on_board(board.id)
end end
def move_params_from_list_position(position)
if position == LIST_END_POSITION
{ move_before_id: moving_to_list_items_relation.reverse_order.pick(:id), move_after_id: nil }
else
item_at_position = moving_to_list_items_relation.offset(position).pick(:id) # rubocop: disable CodeReuse/ActiveRecord
return move_params_from_list_position(LIST_END_POSITION) if item_at_position.nil?
{ move_before_id: nil, move_after_id: item_at_position }
end
end
def move_between_ids(move_params) def move_between_ids(move_params)
ids = [move_params[:move_before_id], move_params[:move_after_id]] ids = [move_params[:move_before_id], move_params[:move_after_id]]
.map(&:to_i) .map(&:to_i)
......
...@@ -54,6 +54,10 @@ def board ...@@ -54,6 +54,10 @@ def board
def update(issue, issue_modification_params) def update(issue, issue_modification_params)
::Issues::UpdateService.new(project: issue.project, current_user: current_user, params: issue_modification_params).execute(issue) ::Issues::UpdateService.new(project: issue.project, current_user: current_user, params: issue_modification_params).execute(issue)
end end
def moving_to_list_items_relation
Boards::Issues::ListService.new(board.resource_parent, current_user, board_id: board.id, id: moving_to_list.id).execute
end
end end
end end
end end
......
...@@ -3038,6 +3038,7 @@ Input type: `IssueMoveListInput` ...@@ -3038,6 +3038,7 @@ Input type: `IssueMoveListInput`
| <a id="mutationissuemovelistiid"></a>`iid` | [`String!`](#string) | IID of the issue to mutate. | | <a id="mutationissuemovelistiid"></a>`iid` | [`String!`](#string) | IID of the issue to mutate. |
| <a id="mutationissuemovelistmoveafterid"></a>`moveAfterId` | [`ID`](#id) | ID of issue that should be placed after the current issue. | | <a id="mutationissuemovelistmoveafterid"></a>`moveAfterId` | [`ID`](#id) | ID of issue that should be placed after the current issue. |
| <a id="mutationissuemovelistmovebeforeid"></a>`moveBeforeId` | [`ID`](#id) | ID of issue that should be placed before the current issue. | | <a id="mutationissuemovelistmovebeforeid"></a>`moveBeforeId` | [`ID`](#id) | ID of issue that should be placed before the current issue. |
| <a id="mutationissuemovelistpositioninlist"></a>`positionInList` | [`Int`](#int) | Position of issue within the board list. Positions start at 0. Use -1 to move to the end of the list. |
| <a id="mutationissuemovelistprojectpath"></a>`projectPath` | [`ID!`](#id) | Project the issue to mutate is in. | | <a id="mutationissuemovelistprojectpath"></a>`projectPath` | [`ID!`](#id) | Project the issue to mutate is in. |
| <a id="mutationissuemovelisttolistid"></a>`toListId` | [`ID`](#id) | ID of the board list that the issue will be moved to. | | <a id="mutationissuemovelisttolistid"></a>`toListId` | [`ID`](#id) | ID of the board list that the issue will be moved to. |
   
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
let(:move_params) { {} } let(:move_params) { {} }
it 'generates an error' do it 'generates an error' do
expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, 'At least one of the arguments fromListId, toListId, afterId or beforeId is required') do expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, 'At least one of the arguments fromListId, toListId, positionInList, moveAfterId, or moveBeforeId is required') do
subject subject
end end
end end
...@@ -71,6 +71,50 @@ ...@@ -71,6 +71,50 @@
end end
end end
context 'when positionInList is given' do
let(:move_params) { { from_list_id: list1.id, to_list_id: list2.id, position_in_list: 0 } }
context 'when fromListId and toListId are missing' do
let(:move_params) { { position_in_list: 0 } }
it 'generates an error' do
expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, 'Both fromListId and toListId are required when positionInList is given') do
subject
end
end
end
context 'when move_before_id is also given' do
let(:move_params) { { from_list_id: list1.id, to_list_id: list2.id, position_in_list: 0, move_before_id: 1 } }
it 'generates an error' do
expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, 'positionInList is mutually exclusive with any of moveBeforeId or moveAfterId') do
subject
end
end
end
context 'when move_after_id is also given' do
let(:move_params) { { from_list_id: list1.id, to_list_id: list2.id, position_in_list: 0, move_after_id: 1 } }
it 'generates an error' do
expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, 'positionInList is mutually exclusive with any of moveBeforeId or moveAfterId') do
subject
end
end
end
context 'when position_in_list is invalid' do
let(:move_params) { { from_list_id: list1.id, to_list_id: list2.id, position_in_list: -5 } }
it 'generates an error' do
expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, "positionInList must be >= 0 or #{Boards::Issues::MoveService::LIST_END_POSITION}") do
subject
end
end
end
end
context 'when user have access to resources' do context 'when user have access to resources' do
it 'moves and repositions issue' do it 'moves and repositions issue' do
subject subject
......
...@@ -100,6 +100,20 @@ ...@@ -100,6 +100,20 @@
expect(response_issue['labels']['edges'][0]['node']['title']).to eq(testing.title) expect(response_issue['labels']['edges'][0]['node']['title']).to eq(testing.title)
end end
end end
context 'when moving an issue using position_in_list' do
let(:issue_move_params) { { from_list_id: list1.id, to_list_id: list2.id, position_in_list: 0 } }
it 'repositions an issue' do
post_graphql_mutation(mutation(params), current_user: current_user)
expect(response).to have_gitlab_http_status(:success)
response_issue = json_response['data'][mutation_result_identifier]['issue']
expect(response_issue['iid']).to eq(issue1.iid.to_s)
expect(response_issue['labels']['edges'][0]['node']['title']).to eq(testing.title)
expect(response_issue['relativePosition']).to be < existing_issue1.relative_position
end
end
end end
context 'when user has no access to resources' do context 'when user has no access to resources' do
......
...@@ -140,6 +140,40 @@ ...@@ -140,6 +140,40 @@
expect(issue2.reload.updated_at.change(usec: 0)).to eq updated_at2.change(usec: 0) expect(issue2.reload.updated_at.change(usec: 0)).to eq updated_at2.change(usec: 0)
end end
context 'when moving to a specific list position' do
before do
[issue1, issue2, issue].each do |issue|
issue.move_to_end && issue.save!
end
end
it 'moves issue to the top of the list' do
described_class.new(parent, user, params.merge({ position_in_list: 0 })).execute(issue)
expect(issue.relative_position).to be < issue1.relative_position
end
it 'moves issue to a position in the middle of the list' do
described_class.new(parent, user, params.merge({ position_in_list: 1 })).execute(issue)
expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position)
end
it 'moves issue to the bottom of the list' do
described_class.new(parent, user, params.merge({ position_in_list: -1 })).execute(issue1)
expect(issue1.relative_position).to be > issue.relative_position
end
context 'when given position is greater than number of issues in the list' do
it 'moves the issue to the bottom of the list' do
described_class.new(parent, user, params.merge({ position_in_list: 5 })).execute(issue1)
expect(issue1.relative_position).to be > issue.relative_position
end
end
end
def reorder_issues(params, issues: []) def reorder_issues(params, issues: [])
issues.each do |issue| issues.each do |issue|
issue.move_to_end && issue.save! issue.move_to_end && issue.save!
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册