Skip to content

Add graphql mutation for repositioning epics on epic boards

Jarka Košanová requested to merge 325794-positioning-iprovements into master

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

Availability and Testing

Related to #325794 (closed), #323194 (closed)

Edited by Jan Provaznik

Merge request reports

Loading