Skip to content

Allow fetching descendant milestones in GraphQL API

Jan Provaznik requested to merge graphql-submilestones into master

What does this MR do?

When include_descendants parameter is passed, also milestones from all subgroups and subprojects are returned but only if visible to user.

Database query

A query to get all milestones in `gitlab-org` group and all its subgroups and subprojects a user can see. Query plan (after warmup): https://explain.depesz.com/s/aWGD
SELECT
    "milestones".*
FROM
    "milestones"
WHERE ("milestones"."project_id" IN (
        SELECT
            "projects"."id"
        FROM ((
                SELECT
                    "projects".*
                FROM
                    "projects"
                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
                                "id"
                            FROM
                                "base_and_descendants" AS "namespaces")
                            AND (EXISTS (
                                    SELECT
                                        1
                                    FROM
                                        "project_authorizations"
                                    WHERE
                                        "project_authorizations"."user_id" = 11
                                        AND (project_authorizations.project_id = projects.id))
                                    OR projects.visibility_level IN (10, 20)))
                        UNION (
                            SELECT
                                "projects".*
                            FROM
                                "projects"
                                INNER JOIN "project_group_links" ON "projects"."id" = "project_group_links"."project_id"
                            WHERE
                                "project_group_links"."group_id" = 9970
                                AND (EXISTS (
                                        SELECT
                                            1
                                        FROM
                                            "project_authorizations"
                                        WHERE
                                            "project_authorizations"."user_id" = 11
                                            AND (project_authorizations.project_id = projects.id))
                                        OR projects.visibility_level IN (10, 20)))) projects
                        LEFT JOIN project_features ON projects.id = project_features.project_id
                    WHERE (("project_features"."issues_access_level" IS NULL
                            OR "project_features"."issues_access_level" IN (20, 30)
                            OR ("project_features"."issues_access_level" = 10
                                AND EXISTS (
                                    SELECT
                                        1
                                    FROM
                                        "project_authorizations"
                                    WHERE
                                        "project_authorizations"."user_id" = 11
                                        AND (project_authorizations.project_id = projects.id)
                                        AND (project_authorizations.access_level >= 10))))
                            OR ("project_features"."merge_requests_access_level" IS NULL
                                OR "project_features"."merge_requests_access_level" IN (20, 30)
                                OR ("project_features"."merge_requests_access_level" = 10
                                    AND EXISTS (
                                        SELECT
                                            1
                                        FROM
                                            "project_authorizations"
                                        WHERE
                                            "project_authorizations"."user_id" = 11
                                            AND (project_authorizations.project_id = projects.id)
                                            AND (project_authorizations.access_level >= 20)))))
                        ORDER BY
                            "projects"."id" DESC)
                        OR "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
                                    "id"
                                FROM
                                    "base_and_descendants" AS "namespaces"
                                WHERE ("namespaces"."visibility_level" IN (10, 20)
                                    OR EXISTS (
                                        SELECT
                                            1
                                        FROM (
                                            SELECT
                                                "namespaces".*
                                            FROM ((
                                                    SELECT
                                                        "namespaces".*
                                                    FROM
                                                        "namespaces"
                                                        INNER JOIN "members" ON "namespaces"."id" = "members"."source_id"
                                                    WHERE
                                                        "members"."type" = 'GroupMember'
                                                        AND "members"."source_type" = 'Namespace'
                                                        AND "namespaces"."type" = 'Group'
                                                        AND "members"."user_id" = 11
                                                        AND "members"."requested_at" IS NULL)
                                                UNION (
                                                    SELECT
                                                        namespaces.*
                                                    FROM
                                                        "projects"
                                                        INNER JOIN "project_authorizations" ON "projects"."id" = "project_authorizations"."project_id"
                                                        INNER JOIN "namespaces" ON "namespaces"."id" = "projects"."namespace_id"
                                                    WHERE
                                                        "project_authorizations"."user_id" = 11)) namespaces
                                            WHERE
                                                "namespaces"."type" = 'Group') authorized
                                        WHERE
                                            authorized. "id" = "namespaces"."id"))))
                    ORDER BY
                        due_date ASC NULLS LAST,
                        title ASC,
                        "milestones"."id" DESC
                    LIMIT 100

Related to #214375 (closed)

Screenshots

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
Edited by Jan Provaznik

Merge request reports