Skip to content

Add milestone_title filter to Epics Finder

Eugenia Grieff requested to merge 218621-roadmaps-filter-by-milestone-be into master

What does this MR do?

Related to #218621 (closed)

This MR adds the option to filter epics by a milestone title.

  • Given that Epics are not directly associated with milestones, we need to look for epic issues that contain the given milestone instead.
  • When filtering epics we are at a group level so we need to fetch all accessible projects in this group to support filtering by project milestones as well. This is being done by using related groups fetched with #permissioned_related_groups and their projects. the collection of groups will change depending on the params include_ancestor_groups and include_descendant_groups so this will affect the fetched milestones as well. If these params aren't present, descendant groups will be included by default.
  • As Roadmaps fetch epics using the GraphQL, it's necessary to add milestone_title as an argument to EpicsResolver to be able to filter by a milestone in that page. The result will include descendant groups by default.

Example GraphQL query using milestone filter

GraphQL query
query {
  group(fullPath: "top-level-group/base-level-group") {
    epics(milestoneTitle: "Sub Level Project Milestone"){
      edges {
        node {
          title
          issues {
            edges {
              node {
                title
              }
            }
          }
        }
      }
    }
  }
}
{
  "data": {
    "group": {
      "epics": {
        "edges": [
          {
            "node": {
              "title": "Sub Level Epic",
              "issues": {
                "edges": [
                  {
                    "node": {
                      "title": "Sub Level Issue 2"
                    }
                  },
                  {
                    "node": {
                      "title": "Sub Level Issue"
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  }
}

Query Plans

Query for EpicsFinder using milestone_title filter
EpicsFinder.new(user, group_id: 9970, milestone_title: '13.3').execute
SELECT epics.*
FROM epics
INNER JOIN epic_issues
    ON epic_issues.epic_id = epics.id
INNER JOIN issues
    ON issues.id = epic_issues.issue_id
WHERE epics.group_id IN (WITH RECURSIVE base_and_descendants AS (
    (SELECT namespaces.*
    FROM namespaces
    WHERE namespaces.type = 'Group'
            AND namespaces.id = 9970)
	  UNION
        (SELECT namespaces.*
        FROM namespaces, base_and_descendants
        WHERE namespaces.type = 'Group'
                AND namespaces.parent_id = base_and_descendants.id))
        SELECT namespaces.id
        FROM base_and_descendants AS namespaces)
            AND issues.milestone_id IN 
    (SELECT milestones.id
    FROM (
        (SELECT milestones.*
        FROM milestones
        WHERE milestones.project_id IN 
            (SELECT projects.id
            FROM projects
            LEFT JOIN project_features
                ON projects.id = project_features.project_id
            WHERE projects.namespace_id IN (WITH RECURSIVE base_and_descendants AS (
                (SELECT namespaces.*
                FROM namespaces
                WHERE namespaces.type = 'Group'
                        AND namespaces.id = 9970)
				   UNION
                    (SELECT namespaces.*
                    FROM namespaces, base_and_descendants
                    WHERE namespaces.type = 'Group'
                            AND namespaces.parent_id = base_and_descendants.id))
                    SELECT namespaces.id
                    FROM base_and_descendants AS namespaces)
                            AND (project_features.issues_access_level > 0
                            OR project_features.issues_access_level IS NULL)))UNION ALL
                        (SELECT milestones.*
                        FROM milestones
                        WHERE milestones.group_id IN (WITH RECURSIVE base_and_descendants AS (
                            (SELECT namespaces.*
                            FROM namespaces
                            WHERE namespaces.type = 'Group'
                                    AND namespaces.id = 9970)
							   UNION
                                (SELECT namespaces.*
                                FROM namespaces, base_and_descendants
                                WHERE namespaces.type = 'Group'
                                        AND namespaces.parent_id = base_and_descendants.id))
                                SELECT namespaces.id
                                FROM base_and_descendants AS namespaces))) milestones
                                WHERE milestones.title = '13.3')
                            ORDER BY  epics.id DESC

https://explain.depesz.com/s/R6fx

Query for EpicsFinder without milestone_title filter
EpicsFinder.new(user, group_id: 9970).execute
SELECT epics.*
FROM epics
WHERE epics.group_id IN (WITH RECURSIVE base_and_descendants AS (
    (SELECT namespaces.*
    FROM namespaces
    WHERE namespaces.type = 'Group'
            AND namespaces.id = 9970)UNION
        (SELECT namespaces.*
        FROM namespaces, base_and_descendants
        WHERE namespaces.type = 'Group'
                AND namespaces.parent_id = base_and_descendants.id))
        SELECT id
        FROM base_and_descendants AS namespaces)
    ORDER BY  epics.id DESC

https://explain.depesz.com/s/R6fx

Does this MR meet the acceptance criteria?

Conformity

Availability and Testing

Security

If this MR contains changes to processing or storing of credentials or tokens, authorization and authentication methods and other items described in the security review guidelines:

  • Label as security and @ mention @gitlab-com/gl-security/appsec
  • The MR includes necessary changes to maintain consistency between UI, API, email, or other methods
  • Security reports checked/validated by a reviewer from the AppSec team

Closes #218621 (closed)

Edited by Eugenia Grieff

Merge request reports