Commit c3fc527c authored by Jan Provaznik's avatar Jan Provaznik

Merge branch '5164-epic-counts-graphql-only' into 'master'

Add GraphQL structure for DescendantWeightSum

See merge request gitlab-org/gitlab!25980
parents 604809ec 1a0bf10c
...@@ -1862,6 +1862,12 @@ type Epic implements Noteable { ...@@ -1862,6 +1862,12 @@ type Epic implements Noteable {
""" """
descendantCounts: EpicDescendantCount descendantCounts: EpicDescendantCount
"""
Total weight of open and closed descendant epic's issues. Available only when
feature flag unfiltered_epic_aggregates is enabled.
"""
descendantWeightSum: EpicDescendantWeights
""" """
Description of the epic Description of the epic
""" """
...@@ -2178,6 +2184,21 @@ type EpicDescendantCount { ...@@ -2178,6 +2184,21 @@ type EpicDescendantCount {
openedIssues: Int openedIssues: Int
} }
"""
Total weight of open and closed descendant issues
"""
type EpicDescendantWeights {
"""
Total weight of completed (closed) issues in this epic, including epic descendants
"""
closedIssues: Int
"""
Total weight of opened issues in this epic, including epic descendants
"""
openedIssues: Int
}
""" """
An edge in a connection. An edge in a connection.
""" """
......
...@@ -297,6 +297,7 @@ Represents an epic. ...@@ -297,6 +297,7 @@ Represents an epic.
| `closedAt` | Time | Timestamp of the epic's closure | | `closedAt` | Time | Timestamp of the epic's closure |
| `createdAt` | Time | Timestamp of the epic's creation | | `createdAt` | Time | Timestamp of the epic's creation |
| `descendantCounts` | EpicDescendantCount | Number of open and closed descendant epics and issues | | `descendantCounts` | EpicDescendantCount | Number of open and closed descendant epics and issues |
| `descendantWeightSum` | EpicDescendantWeights | Total weight of open and closed descendant epic's issues. Available only when feature flag unfiltered_epic_aggregates is enabled. |
| `description` | String | Description of the epic | | `description` | String | Description of the epic |
| `downvotes` | Int! | Number of downvotes the epic has received | | `downvotes` | Int! | Number of downvotes the epic has received |
| `dueDate` | Time | Due date of the epic | | `dueDate` | Time | Due date of the epic |
...@@ -337,6 +338,15 @@ Counts of descendent epics. ...@@ -337,6 +338,15 @@ Counts of descendent epics.
| `openedEpics` | Int | Number of opened sub-epics | | `openedEpics` | Int | Number of opened sub-epics |
| `openedIssues` | Int | Number of opened epic issues | | `openedIssues` | Int | Number of opened epic issues |
## EpicDescendantWeights
Total weight of open and closed descendant issues
| Name | Type | Description |
| --- | ---- | ---------- |
| `closedIssues` | Int | Total weight of completed (closed) issues in this epic, including epic descendants |
| `openedIssues` | Int | Total weight of opened issues in this epic, including epic descendants |
## EpicIssue ## EpicIssue
Relationship between an epic and an issue Relationship between an epic and an issue
......
# frozen_string_literal: true
module Types
# rubocop: disable Graphql/AuthorizeTypes
class EpicDescendantWeightSumType < BaseObject
graphql_name 'EpicDescendantWeights'
description 'Total weight of open and closed descendant issues'
field :opened_issues, GraphQL::INT_TYPE, null: true,
description: 'Total weight of opened issues in this epic, including epic descendants'
field :closed_issues, GraphQL::INT_TYPE, null: true,
description: 'Total weight of completed (closed) issues in this epic, including epic descendants'
end
# rubocop: enable Graphql/AuthorizeTypes
end
...@@ -121,10 +121,22 @@ module Types ...@@ -121,10 +121,22 @@ module Types
resolver: Resolvers::EpicIssuesResolver resolver: Resolvers::EpicIssuesResolver
field :descendant_counts, Types::EpicDescendantCountType, null: true, complexity: 10, field :descendant_counts, Types::EpicDescendantCountType, null: true, complexity: 10,
description: 'Number of open and closed descendant epics and issues', description: 'Number of open and closed descendant epics and issues',
resolve: -> (epic, args, ctx) do resolve: -> (epic, args, ctx) do
Epics::DescendantCountService.new(epic, ctx[:current_user]) Epics::DescendantCountService.new(epic, ctx[:current_user])
end end
field :descendant_weight_sum, Types::EpicDescendantWeightSumType, null: true, complexity: 10,
description: "Total weight of open and closed descendant epic's issues",
feature_flag: :unfiltered_epic_aggregates
def descendant_weight_sum
OpenStruct.new(
# We shouldn't stop the whole query, so returning -1 for a semi-noisy error
opened_issues: -1,
closed_issues: -1
)
end
field :health_status, field :health_status,
::Types::HealthStatusEnum, ::Types::HealthStatusEnum,
......
# frozen_string_literal: true
require 'spec_helper'
describe GitlabSchema.types['EpicDescendantWeights'] do
it { expect(described_class.graphql_name).to eq('EpicDescendantWeights') }
it 'has specific fields' do
%i[opened_issues closed_issues].each do |field_name|
expect(described_class).to have_graphql_field(field_name)
end
end
end
...@@ -11,7 +11,7 @@ describe GitlabSchema.types['Epic'] do ...@@ -11,7 +11,7 @@ describe GitlabSchema.types['Epic'] do
closed_at created_at updated_at children has_children has_issues closed_at created_at updated_at children has_children has_issues
web_path web_url relation_path reference issues user_permissions web_path web_url relation_path reference issues user_permissions
notes discussions relative_position subscribed participants notes discussions relative_position subscribed participants
descendant_counts upvotes downvotes health_status descendant_counts descendant_weight_sum upvotes downvotes health_status
] ]
end end
......
# frozen_string_literal: true
require 'spec_helper'
describe 'Epic aggregates (count and weight)' do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
let_it_be(:group) { create(:group, :public) }
let_it_be(:parent_epic) { create(:epic, id: 1, group: group, title: 'parent epic') }
let(:query) do
graphql_query_for('group', { fullPath: group.full_path }, query_graphql_field('epics', { iid: parent_epic.iid }, epic_aggregates_query))
end
let(:epic_aggregates_query) do
<<~QUERY
nodes {
descendantWeightSum {
openedIssues
closedIssues
}
descendantCounts {
openedEpics
closedEpics
openedIssues
closedIssues
}
}
QUERY
end
before do
stub_licensed_features(epics: true)
end
context 'with feature flag enabled' do
before do
stub_feature_flags(unfiltered_epic_aggregates: true)
end
it 'returns a placeholder with -1 weights and does not error' do
post_graphql(query, current_user: current_user)
actual_result = graphql_data.dig('group', 'epics', 'nodes').first
expected_result = {
"descendantWeightSum" => {
"openedIssues" => -1,
"closedIssues" => -1
}
}
expect(actual_result).to include expected_result
end
end
context 'with feature flag disabled' do
before do
stub_feature_flags(unfiltered_epic_aggregates: false)
end
context 'when requesting counts' do
let(:epic_aggregates_query) do
<<~QUERY
nodes {
descendantCounts {
openedEpics
closedEpics
openedIssues
closedIssues
}
}
QUERY
end
it 'uses the DescendantCountService' do
expect(Epics::DescendantCountService).to receive(:new)
post_graphql(query, current_user: current_user)
end
end
context 'when requesting weights' do
let(:epic_aggregates_query) do
<<~QUERY
nodes {
descendantWeightSum {
openedIssues
closedIssues
}
}
QUERY
end
it 'returns an error' do
post_graphql(query, current_user: current_user)
expect_graphql_errors_to_include /Field 'descendantWeightSum' doesn't exist on type 'Epic/
end
end
end
end
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