Skip to content

Support project scoping for timebox report via GraphQL

euko requested to merge 326029-show-project-level-burndown-backend into master

What does this MR do and why?

The MR implements the backend solution for #326029 (closed).

The GraphQL TimeboxReportInterface is updated to accept a namespace fullpath as argument. The authorized projects under the namespace for the requesting user is then used as a scope when generating a timebox report.

How to set up and validate locally

Preparation

  1. Create a group (root) with two projects (private) and a subgroup that has a project
  2. Create an iteration under the root group.
  3. Create an issue for each project and assign it to the iteration.
  4. Add a private user to each project and group

Example setup:

- root-group (create an iteration named `root iteration` at this level)
  - project-x
    - issue x assigned to `root iteration`
  - project-y
    - issue y assigned to `root iteration`
  - subgroup
    - project-z
      - issue z assigned to `root iteration

Validate through GraphiQL

Sample query (vary fullPath argument for report field to test):

query BurnupTimesSeriesData {
  iteration(id: "gid://gitlab/Iteration/1") {
    __typename
    id
    title
    report(fullPath: "root-group/project-y") {
      burnupTimeSeries {
        date
        completedCount
        scopeCount
      }
      stats {
        total {
          count
        }
        complete {
          count
        }
        incomplete {
          count
        }
      }
    }
  }
}

Sample DB query and plan

Milestone

The query is for retrieving %14.8 milestone report for gitlab-org group.

All above three queries should return the same number of rows.

Note that scoping milestone reports would be only available via GraphQL API without UI support.

Iteration

We will compare query plans that could be generated/are currently being generated when visiting the iteration report view for https://gitlab.com/gitlab-org/gitlab/-/cadences/16671/iterations/40933.

query plan # branch fullPath argument query sample/plan returned row count
#1 master none (not supported) https://explain-depesz.postgres.ai/s/J0 32
#2 this branch not given https://console.postgres.ai/gitlab/gitlab-production-tunnel-pg12/sessions/8701/commands/30944 32
#3 this branch fullPath: gitlab-org (group) https://console.postgres.ai/gitlab/gitlab-production-tunnel-pg12/sessions/8701/commands/30945 32
#4 this branch fullPath: gitlab-org/gitlab (project) https://console.postgres.ai/gitlab/gitlab-production-tunnel-pg12/sessions/8701/commands/30946 31
The GraphQL Query for plan #1 (no scope is given. fullPath argument is NOT used or empty "").

query BurnupTimesSeriesData($isIteration: Boolean = true, $weight: Boolean = false) {
  iteration(id: "gid://gitlab/Iteration/40933") @include(if: $isIteration) {
    __typename
    id
    title
    report {
      __typename
      burnupTimeSeries {
        __typename
        date
        completedCount @skip(if: $weight)
        scopeCount @skip(if: $weight)
        completedWeight @include(if: $weight)
        scopeWeight @include(if: $weight)
      }
      stats {
        __typename
        total {
          __typename
          count @skip(if: $weight)
          weight @include(if: $weight)
        }
        complete {
          __typename
          count @skip(if: $weight)
          weight @include(if: $weight)
        }
        incomplete {
          __typename
          count @skip(if: $weight)
          weight @include(if: $weight)
        }
      }
    }
  }
}
The GraphQL query with fullPath: "gitlab-org/gitlab"
query BurnupTimesSeriesData(
  $isIteration: Boolean = true
  $weight: Boolean = false
) {
  iteration(id: "gid://gitlab/Iteration/40933") @include(if: $isIteration) {
    __typename
    id
    title
    report(fullPath: "gitlab-org/gitlab") {
      __typename
      burnupTimeSeries {
        __typename
        date
        completedCount @skip(if: $weight)
        scopeCount @skip(if: $weight)
        completedWeight @include(if: $weight)
        scopeWeight @include(if: $weight)
      }
      stats {
        __typename
        total {
          __typename
          count @skip(if: $weight)
          weight @include(if: $weight)
        }
        complete {
          __typename
          count @skip(if: $weight)
          weight @include(if: $weight)
        }
        incomplete {
          __typename
          count @skip(if: $weight)
          weight @include(if: $weight)
        }
      }
    }
  }
}
  • The query plans #1, #2, #3 and #4 should return the same results (32 rows).
  • The query plan #5 returns less rows than the other plans because the query plan #5's GraphQL query's been scoped to gitlab (id: 278964) project.

The issue that's been excluded from the report is this issue belonging to the project gitlab-org/plan (id: 13453461)

     id     |                          title                          
-----------+---------------------------------------------------------
 100867296 | Plan Frontend Team Fortnightly Iteration 7 - 2022-01-19

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by euko

Merge request reports