Add graphql mutation for repositioning epics on epic boards
What does this MR do?
It adds the mutation for repositioning epics on the epic boards.
It also creates the missing board positions that are needed for correct positioning.
Background
Epics are sorted on epic boards using EpicBoardPosition
objects. Every epic can have multiple epic_board_positions
, one for every board it belongs to.
We don't create the position records automatically and sort epics on the boards by the epic_board_position.relative_position
and id DESC
if the position is not present.
When we want to position an epic to a specific place in the list we need to create the positioning records so that we can have the order correct. We don't need to create the records for all epics on a board but only for epics that will appear before the epic that is being moved.
This is what this MR do besides the graphql mutation itself.
What this MR do exactly
When an epic is being moved the newly created Boards::Epics::PositionCreator
service is called. The service fetches all the epics on the list the operation runs on and creates position records. It creates the records only for epics that will be on the list before the epic that is being moved.
And this MR also adds the mutation that supports the repositioning behaviour.
Example graphql request
mutation {
epicMoveList(input: {
boardId: "gid://gitlab/Boards::EpicBoard/1",
epicId: "gid://gitlab/Epic/42",
toListId: "gid://gitlab/Boards::EpicList/3",
moveAfterId: "gid://gitlab/Epic/23",
moveBeforeId: "gid://gitlab/Epic/24"
}) {
errors
epic {
id
title
}
}
}
Important db changes & queries
When an epic that is being moved or the epics that will appear before it don't have epic_board_position
records, new records are created. They are created using insert_all
.
For retrieving the epics for filling positioning records (=epics on the same list that have higher id than the epic that will appear before the one being moved) we use Boards::Epics::ListService
Query for retrieving the epics
The ListService
query is best described in the introducing MR
Insert query
Plan: https://console.postgres.ai/shared/97c68c8d-8d44-44c8-b3c2-e7e11e486e97
INSERT INTO "boards_epic_board_positions" ("epic_id",
"epic_board_id",
"relative_position",
"created_at",
"updated_at")
VALUES (4, 1, 1513, '2021-05-03 07:22:32.630208', '2021-05-03 07:22:32.630208'),
(2, 1, 3026, '2021-05-03 07:22:32.630208', '2021-05-03 07:22:32.630208') ON CONFLICT ("epic_board_id", "epic_id") DO NOTHING RETURNING "id"
Select last existing epic board position
#last_for_board_id
SELECT "boards_epic_board_positions".* FROM "boards_epic_board_positions" WHERE "boards_epic_board_positions"."epic_board_id" = 6 AND "boards_epic_board_positions"."relative_position" IS NOT NULL ORDER BY "boards_epic_board_positions"."relative_position" DESC LIMIT 1;
https://console.postgres.ai/gitlab/gitlab-production-tunnel-pg12/sessions/4138/commands/14417
Getting list of epics for a board list ordered by board position
Updated order by boar'ds relative position - https://postgres.ai/console/gitlab/gitlab-production-tunnel-pg12/sessions/4368/commands/15206
SELECT "epics".*
FROM "epics"
LEFT OUTER JOIN "boards_epic_board_positions" ON "boards_epic_board_positions"."epic_id" = "epics"."id"
AND "boards_epic_board_positions"."epic_board_id" = 66
WHERE "epics"."group_id" IN
(SELECT "namespaces"."id"
FROM "namespaces"
WHERE (traversal_ids @> ('{9970}')))
AND "epics"."state_id" = 1
AND (EXISTS
(SELECT "label_links".*
FROM "label_links"
WHERE "label_links"."target_type" = 'Epic'
AND "label_links"."target_id" = "epics"."id"
AND "label_links"."label_id" = 16459909
LIMIT 1))
AND (EXISTS
(SELECT "label_links".*
FROM "label_links"
WHERE "label_links"."target_type" = 'Epic'
AND "label_links"."target_id" = "epics"."id"
AND "label_links"."label_id" = 10690781
LIMIT 1))
AND (EXISTS
(SELECT "label_links".*
FROM "label_links"
WHERE "label_links"."target_type" = 'Epic'
AND "label_links"."target_id" = "epics"."id"
AND "label_links"."label_id" = 16487743
LIMIT 1))
AND (EXISTS
(SELECT "label_links".*
FROM "label_links"
WHERE "label_links"."target_type" = 'Epic'
AND "label_links"."target_id" = "epics"."id"
AND "label_links"."label_id" = 16459857
LIMIT 1))
AND ("boards_epic_board_positions"."epic_board_id" = 66
OR "boards_epic_board_positions"."epic_board_id" IS NULL)
ORDER BY boards_epic_board_positions.relative_position ASC NULLS LAST,
epics.id DESC
LIMIT 10
Does this MR meet the acceptance criteria?
Conformity
-
📋 Does this MR need a changelog?-
I have included a changelog entry. - [] I have not included a changelog entry because it is not user-facing change.
-
-
Documentation (if required) -
Code review guidelines -
Merge request performance guidelines -
Style guides -
Database guides -
Separation of EE specific content
Availability and Testing
-
Review and add/update tests for this feature/bug. Consider all test levels. See the Test Planning Process.
Related to #325794 (closed), #323194 (closed)