From 1a0bf10c60b7a0dc2f0759f3ee46479dcce9832d Mon Sep 17 00:00:00 2001 From: charlieablett Date: Wed, 26 Feb 2020 23:47:54 +1300 Subject: [PATCH] Add GraphQL structure for DescendantWeightSum - Add fields to GraphQL behind feature flag :unfiltered_epic_aggregates - Return placeholders of -1 if the feature flag gets turned on --- .../graphql/reference/gitlab_schema.graphql | 21 ++++ doc/api/graphql/reference/index.md | 10 ++ .../types/epic_descendant_weight_sum_type.rb | 15 +++ ee/app/graphql/types/epic_type.rb | 20 +++- .../epic_descendant_weight_sum_type_spec.rb | 13 +++ ee/spec/graphql/types/epic_type_spec.rb | 2 +- .../group/epic/epic_aggregate_query_spec.rb | 102 ++++++++++++++++++ 7 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 ee/app/graphql/types/epic_descendant_weight_sum_type.rb create mode 100644 ee/spec/graphql/types/epic_descendant_weight_sum_type_spec.rb create mode 100644 ee/spec/requests/api/graphql/group/epic/epic_aggregate_query_spec.rb diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index 816512c83bbd63..e68938e351e654 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -1862,6 +1862,12 @@ type Epic implements Noteable { """ 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 """ @@ -2178,6 +2184,21 @@ type EpicDescendantCount { 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. """ diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 978fbc35125e77..eaf80b1962c431 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -294,6 +294,7 @@ Represents an epic. | `closedAt` | Time | Timestamp of the epic's closure | | `createdAt` | Time | Timestamp of the epic's creation | | `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 | | `downvotes` | Int! | Number of downvotes the epic has received | | `dueDate` | Time | Due date of the epic | @@ -334,6 +335,15 @@ Counts of descendent epics. | `openedEpics` | Int | Number of opened sub-epics | | `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 Relationship between an epic and an issue diff --git a/ee/app/graphql/types/epic_descendant_weight_sum_type.rb b/ee/app/graphql/types/epic_descendant_weight_sum_type.rb new file mode 100644 index 00000000000000..2c24f759c23151 --- /dev/null +++ b/ee/app/graphql/types/epic_descendant_weight_sum_type.rb @@ -0,0 +1,15 @@ +# 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 diff --git a/ee/app/graphql/types/epic_type.rb b/ee/app/graphql/types/epic_type.rb index 74b4d60a9daec8..1e16a98b11f3c7 100644 --- a/ee/app/graphql/types/epic_type.rb +++ b/ee/app/graphql/types/epic_type.rb @@ -121,10 +121,22 @@ class EpicType < BaseObject resolver: Resolvers::EpicIssuesResolver field :descendant_counts, Types::EpicDescendantCountType, null: true, complexity: 10, - description: 'Number of open and closed descendant epics and issues', - resolve: -> (epic, args, ctx) do - Epics::DescendantCountService.new(epic, ctx[:current_user]) - end + description: 'Number of open and closed descendant epics and issues', + resolve: -> (epic, args, ctx) do + Epics::DescendantCountService.new(epic, ctx[:current_user]) + 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, ::Types::HealthStatusEnum, diff --git a/ee/spec/graphql/types/epic_descendant_weight_sum_type_spec.rb b/ee/spec/graphql/types/epic_descendant_weight_sum_type_spec.rb new file mode 100644 index 00000000000000..0e73c5159bb92b --- /dev/null +++ b/ee/spec/graphql/types/epic_descendant_weight_sum_type_spec.rb @@ -0,0 +1,13 @@ +# 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 diff --git a/ee/spec/graphql/types/epic_type_spec.rb b/ee/spec/graphql/types/epic_type_spec.rb index 5cde7f872c4b2a..e90230b732a869 100644 --- a/ee/spec/graphql/types/epic_type_spec.rb +++ b/ee/spec/graphql/types/epic_type_spec.rb @@ -11,7 +11,7 @@ closed_at created_at updated_at children has_children has_issues web_path web_url relation_path reference issues user_permissions notes discussions relative_position subscribed participants - descendant_counts upvotes downvotes health_status + descendant_counts descendant_weight_sum upvotes downvotes health_status ] end diff --git a/ee/spec/requests/api/graphql/group/epic/epic_aggregate_query_spec.rb b/ee/spec/requests/api/graphql/group/epic/epic_aggregate_query_spec.rb new file mode 100644 index 00000000000000..de94aea20790a9 --- /dev/null +++ b/ee/spec/requests/api/graphql/group/epic/epic_aggregate_query_spec.rb @@ -0,0 +1,102 @@ +# 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 -- GitLab