Add filter argument to linkedItems field
Related to #416776 (closed)
What does this MR do and why?
This MR is part of the implementation of BE: Work Items - Linked (Related) Items (&10993 - closed) and still behind the feature flag :linked_work_items
, disabled by default.
Following Add GraphQL query for linked work items (!126980 - merged) we want to add an option to filter the linked items by the link type.
The options for filtering are: RELATED
, BLOCKS
and BLOCKED_BY
(the last two are only available on GitLab Starter license). This filter can be limited to querying all link types.
Also, this MR adds a new validation for work item links so no more than 100 items can be linked to a single work item.
Screenshots or screen recordings
Screen_Recording_2023-08-17_at_12.34.23
Query plans
In order to query linked work items by type this MR modifies Issue#related_issues
** to add the link type to its query. The type
param is optional and the query remains the same when omitted.
- Existing query (type argument not provided)
SELECT
issues.*,
issue_links.id AS issue_link_id,
issue_links.link_type AS issue_link_type_value,
issue_links.target_id AS issue_link_source_id,
issue_links.created_at AS issue_link_created_at,
issue_links.updated_at AS issue_link_updated_at
FROM
"issues"
INNER JOIN issue_links ON (issue_links.source_id = issues.id
AND issue_links.target_id = 1910)
OR (issue_links.target_id = issues.id
AND issue_links.source_id = 1910)
ORDER BY
issue_link_id
- New queries: type is provided:
-
work_item.related_issues(current_user, type: IssuableLink::TYPE_RELATES_TO)
- query plan
Click to expand query
SELECT
issues.*,
issue_links.id AS issue_link_id,
issue_links.link_type AS issue_link_type_value,
issue_links.target_id AS issue_link_source_id,
issue_links.created_at AS issue_link_created_at,
issue_links.updated_at AS issue_link_updated_at
FROM
"issues"
INNER JOIN issue_links ON (issue_links.source_id = issues.id
AND issue_links.target_id = 1910
AND issue_links.link_type = 0)
OR (issue_links.target_id = issues.id
AND issue_links.source_id = 1910
AND issue_links.link_type = 0)
ORDER BY
issue_link_id
-
work_item.related_issues(current_user, type: IssuableLink::TYPE_BLOCKS)
- query plan
Click to expand query
SELECT
issues.*,
issue_links.id AS issue_link_id,
issue_links.link_type AS issue_link_type_value,
issue_links.target_id AS issue_link_source_id,
issue_links.created_at AS issue_link_created_at,
issue_links.updated_at AS issue_link_updated_at
FROM
"issues"
INNER JOIN issue_links ON issue_links.target_id = issues.id
WHERE
"issue_links"."source_id" = 1910
AND "issue_links"."link_type" = 1
ORDER BY
issue_link_id
-
work_item.related_issues(current_user, type: IssuableLink::TYPE_IS_BLOCKED_BY)
- query plan
Click to expand query
SELECT
issues.*,
issue_links.id AS issue_link_id,
issue_links.link_type AS issue_link_type_value,
issue_links.target_id AS issue_link_source_id,
issue_links.created_at AS issue_link_created_at,
issue_links.updated_at AS issue_link_updated_at
FROM
"issues"
INNER JOIN issue_links ON issue_links.source_id = issues.id
WHERE
"issue_links"."target_id" = 1910
AND "issue_links"."link_type" = 1
ORDER BY
issue_link_id
We are also using one of these queries for a new validation that sets a max number of items that can be linked to a work item. For this, we need to check how many links the work item has without authorizing them:
work_item.unauthorized_linked_issues.size
- query plan
Click to expand query
SELECT
COUNT(*)
FROM
"issues"
INNER JOIN issue_links ON (issue_links.source_id = issues.id
AND issue_links.target_id = 1910)
OR (issue_links.target_id = issues.id
AND issue_links.source_id = 1910)
** Note that work items are stored in the issues table and work item links are stored in issue_links
table so these methods are shared.
How to set up and validate locally
- Enabled feature flag
Feature.enable(:linked_work_items)
- Using the Rails console, create 4 tasks and link them using the 3 different link types (take note of
task
's ID)
Click to expand
author, project = User.first, Project.first
wi_type = WorkItems::Type.find_by(base_type: 'task')
task = WorkItem.create!(title: "Source Task", project: project, author: author, work_item_type: wi_type)
related_task = WorkItem.create!(title: "Related Task", project: project, author: author, work_item_type: wi_type)
blocked_task = WorkItem.create!(title: "Blocked Task", project: project, author: author, work_item_type: wi_type)
blocking_task = WorkItem.create!(title: "Blocking Task", project: project, author: author, work_item_type: wi_type)
WorkItems::RelatedWorkItemLink.create!(source: task, target: related_task, link_type: 'relates_to')
WorkItems::RelatedWorkItemLink.create!(source: task, target: blocked_task, link_type: 'blocks')
WorkItems::RelatedWorkItemLink.create!(source: blocking_task, target: task, link_type: 'blocks')
- Visit
https://gdk.test:3000/-/graphql-explorer
in your GDK running locally and use the following query to fetch the linked tasks
Click to expand
query getLinkedITems {
workItem(id: "gid://gitlab/WorkItem/<TASK_ID>") {
widgets {
...on WorkItemWidgetLinkedItems {
linkedItems(filter: RELATED) {
edges {
node {
linkType
workItem {
title
}
}
}
}
}
}
}
}
- Change the filter argument to
BLOCKS
andBLOCKED_BY
and verify that only the correct link types are returned - Remove the argument and verify that the response includes all link types (see screen recording for response examples).
MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
-
I have evaluated the MR acceptance checklist for this MR.