Support project scoping for timebox report via GraphQL
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
- Create a group (root) with two projects (private) and a subgroup that has a project
- Create an iteration under the root group.
- Create an issue for each project and assign it to the iteration.
- 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.
-
Before (on prod): https://console.postgres.ai/gitlab/gitlab-production-tunnel-pg12/sessions/8701/commands/30938
-
After (no scope): https://console.postgres.ai/gitlab/gitlab-production-tunnel-pg12/sessions/8701/commands/30942
-
After (group scope): https://console.postgres.ai/gitlab/gitlab-production-tunnel-pg12/sessions/8701/commands/30941
All above three queries should return the same number of rows.
- After (project scope
gitlab-org/gitlab
) https://console.postgres.ai/gitlab/gitlab-production-tunnel-pg12/sessions/8701/commands/30943
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 |
#1
(no scope is given. fullPath
argument is NOT used or empty ""
).
The GraphQL Query for plan
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)
}
}
}
}
}
fullPath: "gitlab-org/gitlab"
The GraphQL query with 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 togitlab
(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.
-
I have evaluated the MR acceptance checklist for this MR.