Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
937f9447
Commit
937f9447
authored
Apr 13, 2021
by
GitLab Bot
Browse files
Options
Browse Files
Download
Plain Diff
Automatic merge of gitlab-org/gitlab master
parents
6a12cd2c
3230030d
Changes
23
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
627 additions
and
533 deletions
+627
-533
app/assets/javascripts/boards/boards_util.js
app/assets/javascripts/boards/boards_util.js
+32
-1
app/assets/javascripts/boards/components/board_list.vue
app/assets/javascripts/boards/components/board_list.vue
+1
-1
app/assets/javascripts/boards/stores/actions.js
app/assets/javascripts/boards/stores/actions.js
+119
-25
app/assets/javascripts/boards/stores/mutation_types.js
app/assets/javascripts/boards/stores/mutation_types.js
+1
-3
app/assets/javascripts/boards/stores/mutations.js
app/assets/javascripts/boards/stores/mutations.js
+2
-31
app/controllers/projects/logs_controller.rb
app/controllers/projects/logs_controller.rb
+1
-1
app/finders/environments_finder.rb
app/finders/environments_finder.rb
+1
-6
app/graphql/resolvers/environments_resolver.rb
app/graphql/resolvers/environments_resolver.rb
+1
-1
app/services/prometheus/create_default_alerts_service.rb
app/services/prometheus/create_default_alerts_service.rb
+1
-1
doc/user/admin_area/img/export_permissions_v13_11.png
doc/user/admin_area/img/export_permissions_v13_11.png
+0
-0
doc/user/admin_area/index.md
doc/user/admin_area/index.md
+2
-0
ee/app/assets/javascripts/boards/components/issues_lane_list.vue
...assets/javascripts/boards/components/issues_lane_list.vue
+1
-1
ee/app/assets/javascripts/boards/stores/actions.js
ee/app/assets/javascripts/boards/stores/actions.js
+27
-43
ee/app/assets/javascripts/boards/stores/mutations.js
ee/app/assets/javascripts/boards/stores/mutations.js
+0
-19
ee/changelogs/unreleased/325778-moving-issue-to-board-list-of-different-type-should-not-remove-.yml
...ue-to-board-list-of-different-type-should-not-remove-.yml
+5
-0
ee/spec/frontend/boards/stores/actions_spec.js
ee/spec/frontend/boards/stores/actions_spec.js
+63
-180
ee/spec/frontend/boards/stores/mutations_spec.js
ee/spec/frontend/boards/stores/mutations_spec.js
+1
-61
lib/api/environments.rb
lib/api/environments.rb
+1
-1
lib/gitlab/alert_management/payload/base.rb
lib/gitlab/alert_management/payload/base.rb
+1
-1
spec/finders/environments_finder_spec.rb
spec/finders/environments_finder_spec.rb
+7
-7
spec/frontend/boards/mock_data.js
spec/frontend/boards/mock_data.js
+36
-0
spec/frontend/boards/stores/actions_spec.js
spec/frontend/boards/stores/actions_spec.js
+322
-84
spec/frontend/boards/stores/mutations_spec.js
spec/frontend/boards/stores/mutations_spec.js
+2
-66
No files found.
app/assets/javascripts/boards/boards_util.js
View file @
937f9447
import
{
sortBy
}
from
'
lodash
'
;
import
{
sortBy
,
cloneDeep
}
from
'
lodash
'
;
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
{
ListType
,
NOT_FILTER
}
from
'
./constants
'
;
...
...
@@ -113,6 +113,37 @@ export function formatIssueInput(issueInput, boardConfig) {
};
}
export
function
shouldCloneCard
(
fromListType
,
toListType
)
{
const
involvesClosed
=
fromListType
===
ListType
.
closed
||
toListType
===
ListType
.
closed
;
const
involvesBacklog
=
fromListType
===
ListType
.
backlog
||
toListType
===
ListType
.
backlog
;
if
(
involvesClosed
||
involvesBacklog
)
{
return
false
;
}
if
(
fromListType
!==
toListType
)
{
return
true
;
}
return
false
;
}
export
function
getMoveData
(
state
,
params
)
{
const
{
boardItems
,
boardItemsByListId
,
boardLists
}
=
state
;
const
{
itemId
,
fromListId
,
toListId
}
=
params
;
const
fromListType
=
boardLists
[
fromListId
].
listType
;
const
toListType
=
boardLists
[
toListId
].
listType
;
return
{
reordering
:
fromListId
===
toListId
,
shouldClone
:
shouldCloneCard
(
fromListType
,
toListType
),
itemNotInToList
:
!
boardItemsByListId
[
toListId
].
includes
(
itemId
),
originalIssue
:
cloneDeep
(
boardItems
[
itemId
]),
originalIndex
:
boardItemsByListId
[
fromListId
].
indexOf
(
itemId
),
...
params
,
};
}
export
function
moveItemListHelper
(
item
,
fromList
,
toList
)
{
const
updatedItem
=
item
;
if
(
...
...
app/assets/javascripts/boards/components/board_list.vue
View file @
937f9447
...
...
@@ -190,7 +190,7 @@ export default {
}
this
.
moveItem
({
itemId
,
itemId
:
Number
(
itemId
)
,
itemIid
,
itemPath
,
fromListId
:
from
.
dataset
.
listId
,
...
...
app/assets/javascripts/boards/stores/actions.js
View file @
937f9447
...
...
@@ -2,6 +2,7 @@ import * as Sentry from '@sentry/browser';
import
{
pick
}
from
'
lodash
'
;
import
createBoardListMutation
from
'
ee_else_ce/boards/graphql/board_list_create.mutation.graphql
'
;
import
boardListsQuery
from
'
ee_else_ce/boards/graphql/board_lists.query.graphql
'
;
import
issueMoveListMutation
from
'
ee_else_ce/boards/graphql/issue_move_list.mutation.graphql
'
;
import
{
BoardType
,
ListType
,
...
...
@@ -23,13 +24,14 @@ import {
formatIssueInput
,
updateListPosition
,
transformNotFilters
,
moveItemListHelper
,
getMoveData
,
}
from
'
../boards_util
'
;
import
boardLabelsQuery
from
'
../graphql/board_labels.query.graphql
'
;
import
destroyBoardListMutation
from
'
../graphql/board_list_destroy.mutation.graphql
'
;
import
updateBoardListMutation
from
'
../graphql/board_list_update.mutation.graphql
'
;
import
groupProjectsQuery
from
'
../graphql/group_projects.query.graphql
'
;
import
issueCreateMutation
from
'
../graphql/issue_create.mutation.graphql
'
;
import
issueMoveListMutation
from
'
../graphql/issue_move_list.mutation.graphql
'
;
import
issueSetDueDateMutation
from
'
../graphql/issue_set_due_date.mutation.graphql
'
;
import
issueSetLabelsMutation
from
'
../graphql/issue_set_labels.mutation.graphql
'
;
import
issueSetMilestoneMutation
from
'
../graphql/issue_set_milestone.mutation.graphql
'
;
...
...
@@ -333,42 +335,134 @@ export default {
dispatch
(
'
moveIssue
'
,
payload
);
},
moveIssue
:
(
{
state
,
commit
},
{
itemId
,
itemIid
,
itemPath
,
fromListId
,
toListId
,
moveBeforeId
,
moveAfterId
},
moveIssue
:
({
dispatch
,
state
},
params
)
=>
{
const
moveData
=
getMoveData
(
state
,
params
);
dispatch
(
'
moveIssueCard
'
,
moveData
);
dispatch
(
'
updateMovedIssue
'
,
moveData
);
dispatch
(
'
updateIssueOrder
'
,
{
moveData
});
},
moveIssueCard
:
({
commit
},
moveData
)
=>
{
const
{
reordering
,
shouldClone
,
itemNotInToList
,
originalIndex
,
itemId
,
fromListId
,
toListId
,
moveBeforeId
,
moveAfterId
,
}
=
moveData
;
commit
(
types
.
REMOVE_BOARD_ITEM_FROM_LIST
,
{
itemId
,
listId
:
fromListId
});
if
(
reordering
)
{
commit
(
types
.
ADD_BOARD_ITEM_TO_LIST
,
{
itemId
,
listId
:
toListId
,
moveBeforeId
,
moveAfterId
,
});
return
;
}
if
(
itemNotInToList
)
{
commit
(
types
.
ADD_BOARD_ITEM_TO_LIST
,
{
itemId
,
listId
:
toListId
,
moveBeforeId
,
moveAfterId
,
});
}
if
(
shouldClone
)
{
commit
(
types
.
ADD_BOARD_ITEM_TO_LIST
,
{
itemId
,
listId
:
fromListId
,
atIndex
:
originalIndex
});
}
},
updateMovedIssue
:
(
{
commit
,
state
:
{
boardItems
,
boardLists
}
},
{
itemId
,
fromListId
,
toListId
},
)
=>
{
const
originalIssue
=
state
.
boardItems
[
itemId
];
const
fromList
=
state
.
boardItemsByListId
[
fromListId
];
const
originalIndex
=
fromList
.
indexOf
(
Number
(
itemId
));
commit
(
types
.
MOVE_ISSUE
,
{
originalIssue
,
fromListId
,
toListId
,
moveBeforeId
,
moveAfterId
});
const
updatedIssue
=
moveItemListHelper
(
boardItems
[
itemId
],
boardLists
[
fromListId
],
boardLists
[
toListId
],
);
co
nst
{
boardId
}
=
state
;
const
[
fullProjectPath
]
=
itemPath
.
split
(
/
[
#
]
/
);
co
mmit
(
types
.
UPDATE_BOARD_ITEM
,
updatedIssue
)
;
},
gqlClient
.
mutate
({
undoMoveIssueCard
:
({
commit
},
moveData
)
=>
{
const
{
reordering
,
shouldClone
,
itemNotInToList
,
itemId
,
fromListId
,
toListId
,
originalIssue
,
originalIndex
,
}
=
moveData
;
commit
(
types
.
UPDATE_BOARD_ITEM
,
originalIssue
);
if
(
reordering
)
{
commit
(
types
.
REMOVE_BOARD_ITEM_FROM_LIST
,
{
itemId
,
listId
:
fromListId
});
commit
(
types
.
ADD_BOARD_ITEM_TO_LIST
,
{
itemId
,
listId
:
fromListId
,
atIndex
:
originalIndex
});
return
;
}
if
(
shouldClone
)
{
commit
(
types
.
REMOVE_BOARD_ITEM_FROM_LIST
,
{
itemId
,
listId
:
fromListId
});
}
if
(
itemNotInToList
)
{
commit
(
types
.
REMOVE_BOARD_ITEM_FROM_LIST
,
{
itemId
,
listId
:
toListId
});
}
commit
(
types
.
ADD_BOARD_ITEM_TO_LIST
,
{
itemId
,
listId
:
fromListId
,
atIndex
:
originalIndex
});
},
updateIssueOrder
:
async
({
commit
,
dispatch
,
state
},
{
moveData
,
mutationVariables
=
{}
})
=>
{
try
{
const
{
itemId
,
fromListId
,
toListId
,
moveBeforeId
,
moveAfterId
}
=
moveData
;
const
{
boardId
,
boardItems
:
{
[
itemId
]:
{
iid
,
referencePath
},
},
}
=
state
;
const
{
data
}
=
await
gqlClient
.
mutate
({
mutation
:
issueMoveListMutation
,
variables
:
{
projectPath
:
fullProjectPath
,
iid
,
projectPath
:
referencePath
.
split
(
/
[
#
]
/
)[
0
],
boardId
:
fullBoardId
(
boardId
),
iid
:
itemIid
,
fromListId
:
getIdFromGraphQLId
(
fromListId
),
toListId
:
getIdFromGraphQLId
(
toListId
),
moveBeforeId
,
moveAfterId
,
// 'mutationVariables' allows EE code to pass in extra parameters.
...
mutationVariables
,
},
})
.
then
(({
data
})
=>
{
if
(
data
?.
issueMoveList
?.
errors
.
length
)
{
throw
new
Error
(
);
}
else
{
const
issue
=
data
.
issueMoveList
?.
issue
;
commit
(
types
.
MOVE_ISSUE_SUCCESS
,
{
issue
});
}
})
.
catch
(()
=>
commit
(
types
.
MOVE_ISSUE_FAILURE
,
{
originalIssue
,
fromListId
,
toListId
,
originalIndex
}
),
})
;
if
(
data
?.
issueMoveList
?.
errors
.
length
||
!
data
.
issueMoveList
)
{
throw
new
Error
(
'
issueMoveList empty
'
);
}
commit
(
types
.
MUTATE_ISSUE_SUCCESS
,
{
issue
:
data
.
issueMoveList
.
issue
});
}
catch
{
commit
(
types
.
SET_ERROR
,
s__
(
'
Boards|An error occurred while moving the issue. Please try again.
'
),
);
dispatch
(
'
undoMoveIssueCard
'
,
moveData
);
}
},
setAssignees
:
({
commit
,
getters
},
assigneeUsernames
)
=>
{
...
...
app/assets/javascripts/boards/stores/mutation_types.js
View file @
937f9447
...
...
@@ -23,12 +23,10 @@ export const RECEIVE_ITEMS_FOR_LIST_SUCCESS = 'RECEIVE_ITEMS_FOR_LIST_SUCCESS';
export
const
REQUEST_ADD_ISSUE
=
'
REQUEST_ADD_ISSUE
'
;
export
const
RECEIVE_ADD_ISSUE_SUCCESS
=
'
RECEIVE_ADD_ISSUE_SUCCESS
'
;
export
const
RECEIVE_ADD_ISSUE_ERROR
=
'
RECEIVE_ADD_ISSUE_ERROR
'
;
export
const
MOVE_ISSUE
=
'
MOVE_ISSUE
'
;
export
const
MOVE_ISSUE_SUCCESS
=
'
MOVE_ISSUE_SUCCESS
'
;
export
const
MOVE_ISSUE_FAILURE
=
'
MOVE_ISSUE_FAILURE
'
;
export
const
UPDATE_BOARD_ITEM
=
'
UPDATE_BOARD_ITEM
'
;
export
const
REMOVE_BOARD_ITEM
=
'
REMOVE_BOARD_ITEM
'
;
export
const
REQUEST_UPDATE_ISSUE
=
'
REQUEST_UPDATE_ISSUE
'
;
export
const
MUTATE_ISSUE_SUCCESS
=
'
MUTATE_ISSUE_SUCCESS
'
;
export
const
RECEIVE_UPDATE_ISSUE_SUCCESS
=
'
RECEIVE_UPDATE_ISSUE_SUCCESS
'
;
export
const
RECEIVE_UPDATE_ISSUE_ERROR
=
'
RECEIVE_UPDATE_ISSUE_ERROR
'
;
export
const
ADD_BOARD_ITEM_TO_LIST
=
'
ADD_BOARD_ITEM_TO_LIST
'
;
...
...
app/assets/javascripts/boards/stores/mutations.js
View file @
937f9447
...
...
@@ -2,7 +2,7 @@ import { pull, union } from 'lodash';
import
Vue
from
'
vue
'
;
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
formatIssue
,
moveItemListHelper
}
from
'
../boards_util
'
;
import
{
formatIssue
}
from
'
../boards_util
'
;
import
{
issuableTypes
}
from
'
../constants
'
;
import
*
as
mutationTypes
from
'
./mutation_types
'
;
...
...
@@ -183,40 +183,11 @@ export default {
notImplemented
();
},
[
mutationTypes
.
MOVE_ISSUE
]:
(
state
,
{
originalIssue
,
fromListId
,
toListId
,
moveBeforeId
,
moveAfterId
},
)
=>
{
const
fromList
=
state
.
boardLists
[
fromListId
];
const
toList
=
state
.
boardLists
[
toListId
];
const
issue
=
moveItemListHelper
(
originalIssue
,
fromList
,
toList
);
Vue
.
set
(
state
.
boardItems
,
issue
.
id
,
issue
);
removeItemFromList
({
state
,
listId
:
fromListId
,
itemId
:
issue
.
id
});
addItemToList
({
state
,
listId
:
toListId
,
itemId
:
issue
.
id
,
moveBeforeId
,
moveAfterId
});
},
[
mutationTypes
.
MOVE_ISSUE_SUCCESS
]:
(
state
,
{
issue
})
=>
{
[
mutationTypes
.
MUTATE_ISSUE_SUCCESS
]:
(
state
,
{
issue
})
=>
{
const
issueId
=
getIdFromGraphQLId
(
issue
.
id
);
Vue
.
set
(
state
.
boardItems
,
issueId
,
formatIssue
({
...
issue
,
id
:
issueId
}));
},
[
mutationTypes
.
MOVE_ISSUE_FAILURE
]:
(
state
,
{
originalIssue
,
fromListId
,
toListId
,
originalIndex
},
)
=>
{
state
.
error
=
s__
(
'
Boards|An error occurred while moving the issue. Please try again.
'
);
Vue
.
set
(
state
.
boardItems
,
originalIssue
.
id
,
originalIssue
);
removeItemFromList
({
state
,
listId
:
toListId
,
itemId
:
originalIssue
.
id
});
addItemToList
({
state
,
listId
:
fromListId
,
itemId
:
originalIssue
.
id
,
atIndex
:
originalIndex
,
});
},
[
mutationTypes
.
REQUEST_UPDATE_ISSUE
]:
()
=>
{
notImplemented
();
},
...
...
app/controllers/projects/logs_controller.rb
View file @
937f9447
...
...
@@ -58,7 +58,7 @@ module Projects
def
environment
strong_memoize
(
:environment
)
do
if
cluster_params
.
key?
(
:environment_name
)
EnvironmentsFinder
.
new
(
project
,
current_user
,
name:
cluster_params
[
:environment_name
]).
find
.
first
EnvironmentsFinder
.
new
(
project
,
current_user
,
name:
cluster_params
[
:environment_name
]).
execute
.
first
else
project
.
default_environment
end
...
...
app/finders/environments_finder.rb
View file @
937f9447
...
...
@@ -9,12 +9,7 @@ class EnvironmentsFinder
@project
,
@current_user
,
@params
=
project
,
current_user
,
params
end
# This method will eventually take the place of `#execute` as an
# efficient way to get relevant environment entries.
# Currently, `#execute` method has a serious technical debt and
# we will likely rework on it in the future.
# See more https://gitlab.com/gitlab-org/gitlab-foss/issues/63381
def
find
def
execute
environments
=
project
.
environments
environments
=
by_name
(
environments
)
environments
=
by_search
(
environments
)
...
...
app/graphql/resolvers/environments_resolver.rb
View file @
937f9447
...
...
@@ -21,7 +21,7 @@ module Resolvers
def
resolve
(
**
args
)
return
unless
project
.
present?
EnvironmentsFinder
.
new
(
project
,
context
[
:current_user
],
args
).
find
EnvironmentsFinder
.
new
(
project
,
context
[
:current_user
],
args
).
execute
rescue
EnvironmentsFinder
::
InvalidStatesError
=>
exception
raise
Gitlab
::
Graphql
::
Errors
::
ArgumentError
,
exception
.
message
end
...
...
app/services/prometheus/create_default_alerts_service.rb
View file @
937f9447
...
...
@@ -84,7 +84,7 @@ module Prometheus
def
environment
strong_memoize
(
:environment
)
do
EnvironmentsFinder
.
new
(
project
,
nil
,
name:
'production'
).
find
.
first
||
EnvironmentsFinder
.
new
(
project
,
nil
,
name:
'production'
).
execute
.
first
||
project
.
environments
.
first
end
end
...
...
doc/user/admin_area/img/export_permissions_v13_11.png
0 → 100644
View file @
937f9447
11.3 KB
doc/user/admin_area/index.md
View file @
937f9447
...
...
@@ -173,6 +173,8 @@ The following data is included in the export:
-
Path
-
Access level (
[
Project
](
../permissions.md#project-members-permissions
)
and
[
Group
](
../permissions.md#group-members-permissions
)
)
![
user permission export button
](
img/export_permissions_v13_11.png
)
#### Users statistics
The
**Users statistics**
page provides an overview of user accounts by role. These statistics are
...
...
ee/app/assets/javascripts/boards/components/issues_lane_list.vue
View file @
937f9447
...
...
@@ -147,7 +147,7 @@ export default {
}
this
.
moveIssue
({
itemId
,
itemId
:
Number
(
itemId
)
,
itemIid
,
itemPath
,
fromListId
:
from
.
dataset
.
listId
,
...
...
ee/app/assets/javascripts/boards/stores/actions.js
View file @
937f9447
...
...
@@ -5,6 +5,7 @@ import {
formatListsPageInfo
,
fullBoardId
,
transformNotFilters
,
getMoveData
,
}
from
'
~/boards/boards_util
'
;
import
{
BoardType
}
from
'
~/boards/constants
'
;
import
eventHub
from
'
~/boards/eventhub
'
;
...
...
@@ -12,7 +13,6 @@ import listsIssuesQuery from '~/boards/graphql/lists_issues.query.graphql';
import
actionsCE
from
'
~/boards/stores/actions
'
;
import
boardsStore
from
'
~/boards/stores/boards_store
'
;
import
*
as
typesCE
from
'
~/boards/stores/mutation_types
'
;
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
createGqClient
,
{
fetchPolicies
}
from
'
~/lib/graphql
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
{
...
...
@@ -37,7 +37,6 @@ import epicsSwimlanesQuery from '../graphql/epics_swimlanes.query.graphql';
import
groupBoardAssigneesQuery
from
'
../graphql/group_board_assignees.query.graphql
'
;
import
groupBoardIterationsQuery
from
'
../graphql/group_board_iterations.query.graphql
'
;
import
groupBoardMilestonesQuery
from
'
../graphql/group_board_milestones.query.graphql
'
;
import
issueMoveListMutation
from
'
../graphql/issue_move_list.mutation.graphql
'
;
import
issueSetEpicMutation
from
'
../graphql/issue_set_epic.mutation.graphql
'
;
import
issueSetWeightMutation
from
'
../graphql/issue_set_weight.mutation.graphql
'
;
import
listUpdateLimitMetricsMutation
from
'
../graphql/list_update_limit_metrics.mutation.graphql
'
;
...
...
@@ -482,50 +481,35 @@ export default {
}
},
moveIssue
:
(
{
state
,
commit
},
{
itemId
,
itemIid
,
itemPath
,
fromListId
,
toListId
,
moveBeforeId
,
moveAfterId
,
epicId
},
)
=>
{
const
originalIssue
=
state
.
boardItems
[
itemId
];
const
fromList
=
state
.
boardItemsByListId
[
fromListId
];
const
originalIndex
=
fromList
.
indexOf
(
Number
(
itemId
));
commit
(
types
.
MOVE_ISSUE
,
{
originalIssue
,
fromListId
,
toListId
,
moveBeforeId
,
moveAfterId
,
epicId
,
moveIssue
:
({
dispatch
,
state
},
params
)
=>
{
const
{
itemId
,
epicId
}
=
params
;
const
moveData
=
getMoveData
(
state
,
params
);
dispatch
(
'
moveIssueCard
'
,
moveData
);
dispatch
(
'
updateMovedIssue
'
,
moveData
);
dispatch
(
'
updateEpicForIssue
'
,
{
itemId
,
epicId
});
dispatch
(
'
updateIssueOrder
'
,
{
moveData
,
mutationVariables
:
{
epicId
},
});
},
const
{
boardId
}
=
state
;
const
[
fullProjectPath
]
=
itemPath
.
split
(
/
[
#
]
/
)
;
updateEpicForIssue
:
({
commit
,
state
:
{
boardItems
}
},
{
itemId
,
epicId
})
=>
{
const
issue
=
boardItems
[
itemId
]
;
gqlClient
.
mutate
({
mutation
:
issueMoveListMutation
,
variables
:
{
projectPath
:
fullProjectPath
,
boardId
:
fullBoardId
(
boardId
),
iid
:
itemIid
,
fromListId
:
getIdFromGraphQLId
(
fromListId
),
toListId
:
getIdFromGraphQLId
(
toListId
),
moveBeforeId
,
moveAfterId
,
epicId
,
},
})
.
then
(({
data
})
=>
{
if
(
data
?.
issueMoveList
?.
errors
.
length
)
{
throw
new
Error
();
}
else
{
const
issue
=
data
.
issueMoveList
?.
issue
;
commit
(
types
.
MOVE_ISSUE_SUCCESS
,
{
issue
});
}
})
.
catch
(()
=>
commit
(
types
.
MOVE_ISSUE_FAILURE
,
{
originalIssue
,
fromListId
,
toListId
,
originalIndex
}),
);
if
(
epicId
===
null
)
{
commit
(
types
.
UPDATE_BOARD_ITEM_BY_ID
,
{
itemId
:
issue
.
id
,
prop
:
'
epic
'
,
value
:
null
,
});
}
else
if
(
epicId
!==
undefined
)
{
commit
(
types
.
UPDATE_BOARD_ITEM_BY_ID
,
{
itemId
:
issue
.
id
,
prop
:
'
epic
'
,
value
:
{
id
:
epicId
},
});
}
},
moveEpic
:
({
state
,
commit
},
{
itemId
,
fromListId
,
toListId
,
moveBeforeId
,
moveAfterId
})
=>
{
...
...
ee/app/assets/javascripts/boards/stores/mutations.js
View file @
937f9447
...
...
@@ -150,25 +150,6 @@ export default {
Vue
.
set
(
state
,
'
epics
'
,
[]);
},
[
mutationTypes
.
MOVE_ISSUE
]:
(
state
,
{
originalIssue
,
fromListId
,
toListId
,
moveBeforeId
,
moveAfterId
,
epicId
},
)
=>
{
const
fromList
=
state
.
boardLists
[
fromListId
];
const
toList
=
state
.
boardLists
[
toListId
];
const
issue
=
moveItemListHelper
(
originalIssue
,
fromList
,
toList
);
if
(
epicId
===
null
)
{
Vue
.
set
(
state
.
boardItems
,
issue
.
id
,
{
...
issue
,
epic
:
null
});
}
else
if
(
epicId
!==
undefined
)
{
Vue
.
set
(
state
.
boardItems
,
issue
.
id
,
{
...
issue
,
epic
:
{
id
:
epicId
}
});
}
removeItemFromList
({
state
,
listId
:
fromListId
,
itemId
:
issue
.
id
});
addItemToList
({
state
,
listId
:
toListId
,
itemId
:
issue
.
id
,
moveBeforeId
,
moveAfterId
});
},
[
mutationTypes
.
MOVE_EPIC
]:
(
state
,
{
originalEpic
,
fromListId
,
toListId
,
moveBeforeId
,
moveAfterId
},
...
...
ee/changelogs/unreleased/325778-moving-issue-to-board-list-of-different-type-should-not-remove-.yml
0 → 100644
View file @
937f9447
---
title
:
Clone issue card on move when necessary in epic swimlanes
merge_request
:
58644
author
:
type
:
fixed
ee/spec/frontend/boards/stores/actions_spec.js
View file @
937f9447
...
...
@@ -9,6 +9,7 @@ import * as types from 'ee/boards/stores/mutation_types';
import
mutations
from
'
ee/boards/stores/mutations
'
;
import
{
TEST_HOST
}
from
'
helpers/test_constants
'
;
import
testAction
from
'
helpers/vuex_action_helper
'
;
import
{
mockMoveIssueParams
,
mockMoveData
,
mockMoveState
}
from
'
jest/boards/mock_data
'
;
import
{
formatBoardLists
,
formatListIssues
}
from
'
~/boards/boards_util
'
;
import
{
issuableTypes
}
from
'
~/boards/constants
'
;
import
listsIssuesQuery
from
'
~/boards/graphql/lists_issues.query.graphql
'
;
...
...
@@ -19,13 +20,10 @@ import {
labels
,
mockLists
,
mockIssue
,
mockIssue2
,
mockIssues
,
mockEpic
,
rawIssue
,
mockMilestones
,
mockAssignees
,
mockEpics
,
}
from
'
../mock_data
'
;
Vue
.
use
(
Vuex
);
...
...
@@ -903,204 +901,89 @@ describe.each`
});
describe
(
'
moveIssue
'
,
()
=>
{
const
epicId
=
'
gid://gitlab/Epic/1
'
;
it
(
'
should dispatch a correct set of actions with epic id
'
,
()
=>
{
const
params
=
mockMoveIssueParams
;
const
listIssues
=
{
'
gid://gitlab/List/1
'
:
[
436
,
437
],
'
gid://gitlab/List/2
'
:
[],
};
const
issues
=
{
436
:
mockIssue
,
437
:
mockIssue2
,
};
const
state
=
{
fullPath
:
'
gitlab-org
'
,
boardId
:
1
,
boardType
:
'
group
'
,
disabled
:
false
,
boardLists
:
mockLists
,
boardItemsByListId
:
listIssues
,
boardItems
:
issues
,
};
it
(
'
should commit MOVE_ISSUE mutation and MOVE_ISSUE_SUCCESS mutation when successful
'
,
(
done
)
=>
{
jest
.
spyOn
(
gqlClient
,
'
mutate
'
).
mockResolvedValue
({
data
:
{
issueMoveList
:
{
issue
:
rawIssue
,
errors
:
[],
},
},
});
const
moveData
=
{
...
mockMoveData
,
epicId
:
'
some-epic-id
'
,
};
testAction
(
actions
.
moveIssue
,
{
itemId
:
'
436
'
,
itemIid
:
mockIssue
.
iid
,
itemPath
:
mockIssue
.
referencePath
,
fromListId
:
'
gid://gitlab/List/1
'
,
toListId
:
'
gid://gitlab/List/2
'
,
epicId
,
testAction
({
action
:
actions
.
moveIssue
,
payload
:
{
...
params
,
epicId
:
'
some-epic-id
'
,
},
state
,
[
state
:
mockMoveState
,
expectedActions
:
[
{
type
:
'
moveIssueCard
'
,
payload
:
moveData
},
{
type
:
'
updateMovedIssue
'
,
payload
:
moveData
},
{
type
:
'
updateEpicForIssue
'
,
payload
:
{
itemId
:
params
.
itemId
,
epicId
:
'
some-epic-id
'
}
},
{
type
:
types
.
MOVE_ISSUE
,
type
:
'
updateIssueOrder
'
,
payload
:
{
originalIssue
:
mockIssue
,
fromListId
:
'
gid://gitlab/List/1
'
,
toListId
:
'
gid://gitlab/List/2
'
,
epicId
,
moveData
,
mutationVariables
:
{
epicId
:
'
some-epic-id
'
,
}
,
},
},
{
type
:
types
.
MOVE_ISSUE_SUCCESS
,
payload
:
{
issue
:
rawIssue
},
},
],
[],
done
,
);
});
it
(
'
should commit MOVE_ISSUE mutation and MOVE_ISSUE_FAILURE mutation when unsuccessful
'
,
(
done
)
=>
{
jest
.
spyOn
(
gqlClient
,
'
mutate
'
).
mockResolvedValue
({
data
:
{
issueMoveList
:
{
issue
:
{},
errors
:
[{
foo
:
'
bar
'
}],
},
},
});
testAction
(
actions
.
moveIssue
,
{
itemId
:
'
436
'
,
itemIid
:
mockIssue
.
iid
,
itemPath
:
mockIssue
.
referencePath
,
fromListId
:
'
gid://gitlab/List/1
'
,
toListId
:
'
gid://gitlab/List/2
'
,
epicId
,
},
state
,
[
{
type
:
types
.
MOVE_ISSUE
,
payload
:
{
originalIssue
:
mockIssue
,
fromListId
:
'
gid://gitlab/List/1
'
,
toListId
:
'
gid://gitlab/List/2
'
,
epicId
,
},
},
{
type
:
types
.
MOVE_ISSUE_FAILURE
,
payload
:
{
originalIssue
:
mockIssue
,
fromListId
:
'
gid://gitlab/List/1
'
,
toListId
:
'
gid://gitlab/List/2
'
,
originalIndex
:
0
,
},
},
],
[],
done
,
);
});
});
describe
(
'
moveEpic
'
,
()
=>
{
const
listEpics
=
{
'
gid://gitlab/List/1
'
:
[
41
,
40
],
'
gid://gitlab/List/2
'
:
[],
};
const
epics
=
{
41
:
mockEpic
,
40
:
mockEpics
[
1
],
};
const
state
=
{
fullPath
:
'
gitlab-org
'
,
boardId
:
1
,
boardType
:
'
group
'
,
disabled
:
false
,
boardLists
:
mockLists
,
boardItemsByListId
:
listEpics
,
boardItems
:
epics
,
issuableType
:
'
epic
'
,
};
describe
(
'
updateEpicForIssue
'
,
()
=>
{
let
commonState
;
it
(
'
should commit MOVE_EPIC mutation mutation when successful
'
,
async
()
=>
{
jest
.
spyOn
(
gqlClient
,
'
mutate
'
).
mockResolvedValue
(
{
data
:
{
epicMoveList
:
{
errors
:
[]
,
beforeEach
(
()
=>
{
commonState
=
{
boardItems
:
{
itemId
:
{
id
:
'
issueId
'
,
},
},
});
await
testAction
({
action
:
actions
.
moveEpic
,
payload
:
{
itemId
:
'
41
'
,
fromListId
:
'
gid://gitlab/List/1
'
,
toListId
:
'
gid://gitlab/List/2
'
,
},
state
,
expectedMutations
:
[
{
type
:
types
.
MOVE_EPIC
,
payload
:
{
originalEpic
:
mockEpic
,
fromListId
:
'
gid://gitlab/List/1
'
,
toListId
:
'
gid://gitlab/List/2
'
,
},
},
],
});
};
});
it
(
'
should commit MOVE_EPIC mutation and MOVE_EPIC_FAILURE mutation when unsuccessful
'
,
async
()
=>
{
jest
.
spyOn
(
gqlClient
,
'
mutate
'
).
mockResolvedValue
({
data
:
{
epicMoveList
:
{
errors
:
[{
foo
:
'
bar
'
}],
it
.
each
([
[
'
with epic id
'
,
{
payload
:
{
itemId
:
'
itemId
'
,
epicId
:
'
epicId
'
,
},
},
});
await
testAction
({
action
:
actions
.
moveEpic
,
payload
:
{
itemId
:
'
41
'
,
fromListId
:
'
gid://gitlab/List/1
'
,
toListId
:
'
gid://gitlab/List/2
'
,
},
state
,
expectedMutations
:
[
{
type
:
types
.
MOVE_EPIC
,
payload
:
{
originalEpic
:
mockEpic
,
fromListId
:
'
gid://gitlab/List/1
'
,
toListId
:
'
gid://gitlab/List/2
'
,
expectedMutations
:
[
{
type
:
types
.
UPDATE_BOARD_ITEM_BY_ID
,
payload
:
{
itemId
:
'
issueId
'
,
prop
:
'
epic
'
,
value
:
{
id
:
'
epicId
'
}
},
},
],
},
],
[
'
with null as epic id
'
,
{
payload
:
{
itemId
:
'
itemId
'
,
epicId
:
null
,
},
{
type
:
types
.
MOVE_EPIC_FAILURE
,
payload
:
{
originalEpic
:
mockEpic
,
fromListId
:
'
gid://gitlab/List/1
'
,
toListId
:
'
gid://gitlab/List/2
'
,
originalIndex
:
0
,
expectedMutations
:
[
{
type
:
types
.
UPDATE_BOARD_ITEM_BY_ID
,
payload
:
{
itemId
:
'
issueId
'
,
prop
:
'
epic
'
,
value
:
null
},
},
},
],
],
},
],
])(
`commits UPDATE_BOARD_ITEM_BY_ID mutation %s`
,
(
_
,
{
payload
,
expectedMutations
})
=>
{
testAction
({
action
:
actions
.
updateEpicForIssue
,
payload
,
state
:
commonState
,
expectedMutations
,
});
});
});
...
...
ee/spec/frontend/boards/stores/mutations_spec.js
View file @
937f9447
import
mutations
from
'
ee/boards/stores/mutations
'
;
import
{
mock
Issue
,
mockIssue2
,
mock
Epics
,
mockEpic
,
mockLists
}
from
'
../mock_data
'
;
import
{
mockEpics
,
mockEpic
,
mockLists
}
from
'
../mock_data
'
;
const
expectNotImplemented
=
(
action
)
=>
{
it
(
'
is not implemented
'
,
()
=>
{
...
...
@@ -7,8 +7,6 @@ const expectNotImplemented = (action) => {
});
};
const
epicId
=
mockEpic
.
id
;
const
initialBoardListsState
=
{
'
gid://gitlab/List/1
'
:
mockLists
[
0
],
'
gid://gitlab/List/2
'
:
mockLists
[
1
],
...
...
@@ -222,64 +220,6 @@ describe('RESET_EPICS', () => {
});
});
describe
(
'
MOVE_ISSUE
'
,
()
=>
{
beforeEach
(()
=>
{
const
listIssues
=
{
'
gid://gitlab/List/1
'
:
[
mockIssue
.
id
,
mockIssue2
.
id
],
'
gid://gitlab/List/2
'
:
[],
};
const
issues
=
{
436
:
mockIssue
,
437
:
mockIssue2
,
};
state
=
{
...
state
,
boardItemsByListId
:
listIssues
,
boardItems
:
issues
,
};
});
it
(
'
updates boardItemsByListId, moving issue between lists and updating epic id on issue
'
,
()
=>
{
expect
(
state
.
boardItems
[
'
437
'
].
epic
.
id
).
toEqual
(
'
gid://gitlab/Epic/40
'
);
mutations
.
MOVE_ISSUE
(
state
,
{
originalIssue
:
mockIssue2
,
fromListId
:
'
gid://gitlab/List/1
'
,
toListId
:
'
gid://gitlab/List/2
'
,
epicId
,
});
const
updatedListIssues
=
{
'
gid://gitlab/List/1
'
:
[
mockIssue
.
id
],
'
gid://gitlab/List/2
'
:
[
mockIssue2
.
id
],
};
expect
(
state
.
boardItemsByListId
).
toEqual
(
updatedListIssues
);
expect
(
state
.
boardItems
[
'
437
'
].
epic
.
id
).
toEqual
(
epicId
);
});
it
(
'
removes epic id from issue when epicId is null
'
,
()
=>
{
expect
(
state
.
boardItems
[
'
437
'
].
epic
.
id
).
toEqual
(
'
gid://gitlab/Epic/40
'
);
mutations
.
MOVE_ISSUE
(
state
,
{
originalIssue
:
mockIssue2
,
fromListId
:
'
gid://gitlab/List/1
'
,
toListId
:
'
gid://gitlab/List/2
'
,
epicId
:
null
,
});
const
updatedListIssues
=
{
'
gid://gitlab/List/1
'
:
[
mockIssue
.
id
],
'
gid://gitlab/List/2
'
:
[
mockIssue2
.
id
],
};
expect
(
state
.
boardItemsByListId
).
toEqual
(
updatedListIssues
);
expect
(
state
.
boardItems
[
'
437
'
].
epic
).
toEqual
(
null
);
});
});
describe
(
'
MOVE_EPIC
'
,
()
=>
{
it
(
'
updates boardItemsByListId, moving epic between lists
'
,
()
=>
{
const
listIssues
=
{
...
...
lib/api/environments.rb
View file @
937f9447
...
...
@@ -26,7 +26,7 @@ module API
get
':id/environments'
do
authorize!
:read_environment
,
user_project
environments
=
::
EnvironmentsFinder
.
new
(
user_project
,
current_user
,
params
).
find
environments
=
::
EnvironmentsFinder
.
new
(
user_project
,
current_user
,
params
).
execute
present
paginate
(
environments
),
with:
Entities
::
Environment
,
current_user:
current_user
end
...
...
lib/gitlab/alert_management/payload/base.rb
View file @
937f9447
...
...
@@ -132,7 +132,7 @@ module Gitlab
EnvironmentsFinder
.
new
(
project
,
nil
,
{
name:
environment_name
})
.
find
.
execute
.
first
end
end
...
...
spec/finders/environments_finder_spec.rb
View file @
937f9447
...
...
@@ -11,37 +11,37 @@ RSpec.describe EnvironmentsFinder do
project
.
add_maintainer
(
user
)
end
describe
'#
find
'
do
describe
'#
execute
'
do
context
'with states parameter'
do
let
(
:stopped_environment
)
{
create
(
:environment
,
:stopped
,
project:
project
)
}
it
'returns environments with the requested state'
do
result
=
described_class
.
new
(
project
,
user
,
states:
'available'
).
find
result
=
described_class
.
new
(
project
,
user
,
states:
'available'
).
execute
expect
(
result
).
to
contain_exactly
(
environment
)
end
it
'returns environments with any of the requested states'
do
result
=
described_class
.
new
(
project
,
user
,
states:
%w(available stopped)
).
find
result
=
described_class
.
new
(
project
,
user
,
states:
%w(available stopped)
).
execute
expect
(
result
).
to
contain_exactly
(
environment
,
stopped_environment
)
end
it
'raises exception when requested state is invalid'
do
expect
{
described_class
.
new
(
project
,
user
,
states:
%w(invalid stopped)
).
find
}.
to
(
expect
{
described_class
.
new
(
project
,
user
,
states:
%w(invalid stopped)
).
execute
}.
to
(
raise_error
(
described_class
::
InvalidStatesError
,
'Requested states are invalid'
)
)
end
context
'works with symbols'
do
it
'returns environments with the requested state'
do
result
=
described_class
.
new
(
project
,
user
,
states: :available
).
find
result
=
described_class
.
new
(
project
,
user
,
states: :available
).
execute
expect
(
result
).
to
contain_exactly
(
environment
)
end
it
'returns environments with any of the requested states'
do
result
=
described_class
.
new
(
project
,
user
,
states:
[
:available
,
:stopped
]).
find
result
=
described_class
.
new
(
project
,
user
,
states:
[
:available
,
:stopped
]).
execute
expect
(
result
).
to
contain_exactly
(
environment
,
stopped_environment
)
end
...
...
@@ -53,7 +53,7 @@ RSpec.describe EnvironmentsFinder do
let
(
:environment3
)
{
create
(
:environment
,
:available
,
name:
'test3'
,
project:
project
)
}
it
'searches environments by name and state'
do
result
=
described_class
.
new
(
project
,
user
,
search:
'test'
,
states: :available
).
find
result
=
described_class
.
new
(
project
,
user
,
search:
'test'
,
states: :available
).
execute
expect
(
result
).
to
contain_exactly
(
environment3
)
end
...
...
spec/frontend/boards/mock_data.js
View file @
937f9447
...
...
@@ -3,6 +3,7 @@
import
{
keyBy
}
from
'
lodash
'
;
import
Vue
from
'
vue
'
;
import
'
~/boards/models/list
'
;
import
{
ListType
}
from
'
~/boards/constants
'
;
import
boardsStore
from
'
~/boards/stores/boards_store
'
;
export
const
boardObj
=
{
...
...
@@ -488,3 +489,38 @@ export const mockBlockedIssue2 = {
blockedByCount
:
4
,
webUrl
:
'
http://gdk.test:3000/gitlab-org/my-project-1/-/issues/0
'
,
};
export
const
mockMoveIssueParams
=
{
itemId
:
1
,
fromListId
:
'
gid://gitlab/List/1
'
,
toListId
:
'
gid://gitlab/List/2
'
,
moveBeforeId
:
undefined
,
moveAfterId
:
undefined
,
};
export
const
mockMoveState
=
{
boardLists
:
{
'
gid://gitlab/List/1
'
:
{
listType
:
ListType
.
backlog
,
},
'
gid://gitlab/List/2
'
:
{
listType
:
ListType
.
closed
,
},
},
boardItems
:
{
[
mockMoveIssueParams
.
itemId
]:
{
foo
:
'
bar
'
},
},
boardItemsByListId
:
{
[
mockMoveIssueParams
.
fromListId
]:
[
mockMoveIssueParams
.
itemId
],
[
mockMoveIssueParams
.
toListId
]:
[],
},
};
export
const
mockMoveData
=
{
reordering
:
false
,
shouldClone
:
false
,
itemNotInToList
:
true
,
originalIndex
:
0
,
originalIssue
:
{
foo
:
'
bar
'
},
...
mockMoveIssueParams
,
};
spec/frontend/boards/stores/actions_spec.js
View file @
937f9447
This diff is collapsed.
Click to expand it.
spec/frontend/boards/stores/mutations_spec.js
View file @
937f9447
...
...
@@ -394,41 +394,7 @@ describe('Board Store Mutations', () => {
expectNotImplemented
(
mutations
.
RECEIVE_ADD_ISSUE_ERROR
);
});
describe
(
'
MOVE_ISSUE
'
,
()
=>
{
it
(
'
updates boardItemsByListId, moving issue between lists
'
,
()
=>
{
const
listIssues
=
{
'
gid://gitlab/List/1
'
:
[
mockIssue
.
id
,
mockIssue2
.
id
],
'
gid://gitlab/List/2
'
:
[],
};
const
issues
=
{
1
:
mockIssue
,
2
:
mockIssue2
,
};
state
=
{
...
state
,
boardItemsByListId
:
listIssues
,
boardLists
:
initialBoardListsState
,
boardItems
:
issues
,
};
mutations
.
MOVE_ISSUE
(
state
,
{
originalIssue
:
mockIssue2
,
fromListId
:
'
gid://gitlab/List/1
'
,
toListId
:
'
gid://gitlab/List/2
'
,
});
const
updatedListIssues
=
{
'
gid://gitlab/List/1
'
:
[
mockIssue
.
id
],
'
gid://gitlab/List/2
'
:
[
mockIssue2
.
id
],
};
expect
(
state
.
boardItemsByListId
).
toEqual
(
updatedListIssues
);
});
});
describe
(
'
MOVE_ISSUE_SUCCESS
'
,
()
=>
{
describe
(
'
MUTATE_ISSUE_SUCCESS
'
,
()
=>
{
it
(
'
updates issue in issues state
'
,
()
=>
{
const
issues
=
{
436
:
{
id
:
rawIssue
.
id
},
...
...
@@ -439,7 +405,7 @@ describe('Board Store Mutations', () => {
boardItems
:
issues
,
};
mutations
.
M
OV
E_ISSUE_SUCCESS
(
state
,
{
mutations
.
M
UTAT
E_ISSUE_SUCCESS
(
state
,
{
issue
:
rawIssue
,
});
...
...
@@ -447,36 +413,6 @@ describe('Board Store Mutations', () => {
});
});
describe
(
'
MOVE_ISSUE_FAILURE
'
,
()
=>
{
it
(
'
updates boardItemsByListId, reverting moving issue between lists, and sets error message
'
,
()
=>
{
const
listIssues
=
{
'
gid://gitlab/List/1
'
:
[
mockIssue
.
id
],
'
gid://gitlab/List/2
'
:
[
mockIssue2
.
id
],
};
state
=
{
...
state
,
boardItemsByListId
:
listIssues
,
boardLists
:
initialBoardListsState
,
};
mutations
.
MOVE_ISSUE_FAILURE
(
state
,
{
originalIssue
:
mockIssue2
,
fromListId
:
'
gid://gitlab/List/1
'
,
toListId
:
'
gid://gitlab/List/2
'
,
originalIndex
:
1
,
});
const
updatedListIssues
=
{
'
gid://gitlab/List/1
'
:
[
mockIssue
.
id
,
mockIssue2
.
id
],
'
gid://gitlab/List/2
'
:
[],
};
expect
(
state
.
boardItemsByListId
).
toEqual
(
updatedListIssues
);
expect
(
state
.
error
).
toEqual
(
'
An error occurred while moving the issue. Please try again.
'
);
});
});
describe
(
'
UPDATE_BOARD_ITEM
'
,
()
=>
{
it
(
'
updates the given issue in state.boardItems
'
,
()
=>
{
const
updatedIssue
=
{
id
:
'
some_gid
'
,
foo
:
'
bar
'
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment