Commit 332ead95 authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Merge branch 'issue-233707-expose-timebox-total-stats' into 'master'

Expose timebox stats explicitly

See merge request gitlab-org/gitlab!46774
parents cf36f02d 5708ff63
...@@ -20806,6 +20806,41 @@ Time represented in ISO 8601 ...@@ -20806,6 +20806,41 @@ Time represented in ISO 8601
""" """
scalar Time scalar Time
"""
Represents the time report stats for timeboxes
"""
type TimeReportStats {
"""
Completed issues metrics
"""
complete: TimeboxMetrics
"""
Incomplete issues metrics
"""
incomplete: TimeboxMetrics
"""
Total issues metrics
"""
total: TimeboxMetrics
}
"""
Represents measured stats metrics for timeboxes
"""
type TimeboxMetrics {
"""
The count metric
"""
count: Int!
"""
The weight metric
"""
weight: Int!
}
""" """
Represents a historically accurate report about the timebox Represents a historically accurate report about the timebox
""" """
...@@ -20814,6 +20849,11 @@ type TimeboxReport { ...@@ -20814,6 +20849,11 @@ type TimeboxReport {
Daily scope and completed totals for burnup charts Daily scope and completed totals for burnup charts
""" """
burnupTimeSeries: [BurnupChartDailyTotals!] burnupTimeSeries: [BurnupChartDailyTotals!]
"""
Represents the time report stats for the timebox
"""
stats: TimeReportStats
} }
interface TimeboxReportInterface { interface TimeboxReportInterface {
......
...@@ -60459,6 +60459,110 @@ ...@@ -60459,6 +60459,110 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "OBJECT",
"name": "TimeReportStats",
"description": "Represents the time report stats for timeboxes",
"fields": [
{
"name": "complete",
"description": "Completed issues metrics",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "TimeboxMetrics",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "incomplete",
"description": "Incomplete issues metrics",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "TimeboxMetrics",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "total",
"description": "Total issues metrics",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "TimeboxMetrics",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "TimeboxMetrics",
"description": "Represents measured stats metrics for timeboxes",
"fields": [
{
"name": "count",
"description": "The count metric",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "weight",
"description": "The weight metric",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "OBJECT", "kind": "OBJECT",
"name": "TimeboxReport", "name": "TimeboxReport",
...@@ -60485,6 +60589,20 @@ ...@@ -60485,6 +60589,20 @@
}, },
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
},
{
"name": "stats",
"description": "Represents the time report stats for the timebox",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "TimeReportStats",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
} }
], ],
"inputFields": null, "inputFields": null,
...@@ -3057,6 +3057,25 @@ Represents a requirement test report. ...@@ -3057,6 +3057,25 @@ Represents a requirement test report.
| `id` | ID! | ID of the test report | | `id` | ID! | ID of the test report |
| `state` | TestReportState! | State of the test report | | `state` | TestReportState! | State of the test report |
### TimeReportStats
Represents the time report stats for timeboxes.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `complete` | TimeboxMetrics | Completed issues metrics |
| `incomplete` | TimeboxMetrics | Incomplete issues metrics |
| `total` | TimeboxMetrics | Total issues metrics |
### TimeboxMetrics
Represents measured stats metrics for timeboxes.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `count` | Int! | The count metric |
| `weight` | Int! | The weight metric |
### TimeboxReport ### TimeboxReport
Represents a historically accurate report about the timebox. Represents a historically accurate report about the timebox.
...@@ -3064,6 +3083,7 @@ Represents a historically accurate report about the timebox. ...@@ -3064,6 +3083,7 @@ Represents a historically accurate report about the timebox.
| Field | Type | Description | | Field | Type | Description |
| ----- | ---- | ----------- | | ----- | ---- | ----------- |
| `burnupTimeSeries` | BurnupChartDailyTotals! => Array | Daily scope and completed totals for burnup charts | | `burnupTimeSeries` | BurnupChartDailyTotals! => Array | Daily scope and completed totals for burnup charts |
| `stats` | TimeReportStats | Represents the time report stats for the timebox |
### Timelog ### Timelog
......
# frozen_string_literal: true
module Types
# rubocop: disable Graphql/AuthorizeTypes
class TimeReportStatsType < BaseObject
graphql_name 'TimeReportStats'
description 'Represents the time report stats for timeboxes'
field :complete, ::Types::TimeboxMetricsType, null: true,
description: 'Completed issues metrics'
field :incomplete, ::Types::TimeboxMetricsType, null: true,
description: 'Incomplete issues metrics'
field :total, ::Types::TimeboxMetricsType, null: true,
description: 'Total issues metrics'
end
end
# frozen_string_literal: true
module Types
# rubocop: disable Graphql/AuthorizeTypes
class TimeboxMetricsType < BaseObject
graphql_name 'TimeboxMetrics'
description 'Represents measured stats metrics for timeboxes'
field :count, GraphQL::INT_TYPE, null: false,
description: 'The count metric'
field :weight, GraphQL::INT_TYPE, null: false,
description: 'The weight metric'
end
end
...@@ -6,6 +6,8 @@ module Types ...@@ -6,6 +6,8 @@ module Types
graphql_name 'TimeboxReport' graphql_name 'TimeboxReport'
description 'Represents a historically accurate report about the timebox' description 'Represents a historically accurate report about the timebox'
field :stats, ::Types::TimeReportStatsType, null: true,
description: 'Represents the time report stats for the timebox'
field :burnup_time_series, [::Types::BurnupChartDailyTotalsType], null: true, field :burnup_time_series, [::Types::BurnupChartDailyTotalsType], null: true,
description: 'Daily scope and completed totals for burnup charts' description: 'Daily scope and completed totals for burnup charts'
end end
......
...@@ -36,7 +36,8 @@ class TimeboxReportService ...@@ -36,7 +36,8 @@ class TimeboxReportService
end end
ServiceResponse.success(payload: { ServiceResponse.success(payload: {
burnup_time_series: chart_data burnup_time_series: chart_data,
stats: build_stats
}) })
end end
...@@ -218,4 +219,24 @@ class TimeboxReportService ...@@ -218,4 +219,24 @@ class TimeboxReportService
raise ArgumentError, 'Cannot handle timebox type' raise ArgumentError, 'Cannot handle timebox type'
end end
end end
def build_stats
stats_data = chart_data.last
return unless stats_data
{
complete: {
count: stats_data[:completed_count],
weight: stats_data[:completed_weight]
},
incomplete: {
count: stats_data[:scope_count] - stats_data[:completed_count],
weight: stats_data[:scope_weight] - stats_data[:completed_weight]
},
total: {
count: stats_data[:scope_count],
weight: stats_data[:scope_weight]
}
}
end
end end
---
title: Expose timebox stats explicitly
merge_request: 46774
author:
type: changed
...@@ -34,7 +34,13 @@ RSpec.describe Resolvers::TimeboxReportResolver do ...@@ -34,7 +34,13 @@ RSpec.describe Resolvers::TimeboxReportResolver do
end end
it 'returns burnup chart data' do it 'returns burnup chart data' do
expect(subject).to eq(burnup_time_series: [ expect(subject).to eq(
stats: {
complete: { count: 0, weight: 0 },
incomplete: { count: 2, weight: 0 },
total: { count: 2, weight: 0 }
},
burnup_time_series: [
{ {
date: start_date + 4.days, date: start_date + 4.days,
scope_count: 1, scope_count: 1,
......
...@@ -56,6 +56,12 @@ RSpec.shared_examples 'timebox chart' do |timebox_type| ...@@ -56,6 +56,12 @@ RSpec.shared_examples 'timebox chart' do |timebox_type|
create(:resource_state_event, issue: issues[3], state: :closed, created_at: timebox_start_date - 6.days) create(:resource_state_event, issue: issues[3], state: :closed, created_at: timebox_start_date - 6.days)
expect(response.success?).to eq(true) expect(response.success?).to eq(true)
expect(response.payload[:stats]).to eq({
complete: { count: 2, weight: 7 },
incomplete: { count: 2, weight: 3 },
total: { count: 4, weight: 10 }
})
expect(response.payload[:burnup_time_series]).to eq([ expect(response.payload[:burnup_time_series]).to eq([
{ {
date: timebox_start_date, date: timebox_start_date,
...@@ -98,6 +104,11 @@ RSpec.shared_examples 'timebox chart' do |timebox_type| ...@@ -98,6 +104,11 @@ RSpec.shared_examples 'timebox chart' do |timebox_type|
create(:"resource_#{timebox_type}_event", issue: issues[0], "#{timebox_type}" => timebox, action: :remove, created_at: timebox_start_date + 21.days) create(:"resource_#{timebox_type}_event", issue: issues[0], "#{timebox_type}" => timebox, action: :remove, created_at: timebox_start_date + 21.days)
expect(response.success?).to eq(true) expect(response.success?).to eq(true)
expect(response.payload[:stats]).to eq({
complete: { count: 0, weight: 0 },
incomplete: { count: 1, weight: 0 },
total: { count: 1, weight: 0 }
})
expect(response.payload[:burnup_time_series]).to eq([ expect(response.payload[:burnup_time_series]).to eq([
{ {
date: timebox_start_date + 4.days, date: timebox_start_date + 4.days,
...@@ -159,6 +170,11 @@ RSpec.shared_examples 'timebox chart' do |timebox_type| ...@@ -159,6 +170,11 @@ RSpec.shared_examples 'timebox chart' do |timebox_type|
create(:resource_state_event, issue: issues[1], state: :closed, created_at: timebox_start_date + 9.days) create(:resource_state_event, issue: issues[1], state: :closed, created_at: timebox_start_date + 9.days)
expect(response.success?).to eq(true) expect(response.success?).to eq(true)
expect(response.payload[:stats]).to eq({
complete: { count: 0, weight: 0 },
incomplete: { count: 1, weight: 2 },
total: { count: 1, weight: 2 }
})
expect(response.payload[:burnup_time_series]).to eq([ expect(response.payload[:burnup_time_series]).to eq([
{ {
date: timebox_start_date, date: timebox_start_date,
...@@ -230,6 +246,11 @@ RSpec.shared_examples 'timebox chart' do |timebox_type| ...@@ -230,6 +246,11 @@ RSpec.shared_examples 'timebox chart' do |timebox_type|
create(:resource_weight_event, issue: issues[0], weight: 10, created_at: timebox_start_date + 5.days) create(:resource_weight_event, issue: issues[0], weight: 10, created_at: timebox_start_date + 5.days)
expect(response.success?).to eq(true) expect(response.success?).to eq(true)
expect(response.payload[:stats]).to eq({
complete: { count: 1, weight: 1 },
incomplete: { count: 0, weight: 0 },
total: { count: 1, weight: 1 }
})
expect(response.payload[:burnup_time_series]).to eq([ expect(response.payload[:burnup_time_series]).to eq([
{ {
date: timebox_start_date, date: timebox_start_date,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment