diff options
-rw-r--r-- | app/models/cycle_analytics/group_level.rb | 10 | ||||
-rw-r--r-- | app/serializers/group_analytics_stage_entity.rb | 16 | ||||
-rw-r--r-- | app/serializers/group_analytics_stage_serializer.rb | 5 | ||||
-rw-r--r-- | lib/gitlab/cycle_analytics/base_event_fetcher.rb | 10 | ||||
-rw-r--r-- | lib/gitlab/cycle_analytics/base_stage.rb | 16 | ||||
-rw-r--r-- | spec/models/cycle_analytics/group_level_spec.rb | 35 |
6 files changed, 84 insertions, 8 deletions
diff --git a/app/models/cycle_analytics/group_level.rb b/app/models/cycle_analytics/group_level.rb index a7853aadbb2..2e54346b01c 100644 --- a/app/models/cycle_analytics/group_level.rb +++ b/app/models/cycle_analytics/group_level.rb @@ -13,10 +13,18 @@ module CycleAnalytics current_user: @options[:current_user]).data end - def permissions(user:) + def permissions(user: nil) STAGES.each_with_object({}) do |stage, obj| obj[stage] = true end end + + private + + def stats_per_stage + STAGES.map do |stage_name| + self[stage_name].as_json(serializer: GroupAnalyticsStageSerializer) + end + end end end diff --git a/app/serializers/group_analytics_stage_entity.rb b/app/serializers/group_analytics_stage_entity.rb new file mode 100644 index 00000000000..019a3086f68 --- /dev/null +++ b/app/serializers/group_analytics_stage_entity.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class GroupAnalyticsStageEntity < Grape::Entity + include EntityDateHelper + + expose :title + expose :name + expose :legend + expose :description + + expose :group_median, as: :value do |stage| + # median returns a BatchLoader instance which we first have to unwrap by using to_f + # we use to_f to make sure results below 1 are presented to the end-user + stage.group_median.to_f.nonzero? ? distance_of_time_in_words(stage.group_median) : nil + end +end diff --git a/app/serializers/group_analytics_stage_serializer.rb b/app/serializers/group_analytics_stage_serializer.rb new file mode 100644 index 00000000000..ec448dea602 --- /dev/null +++ b/app/serializers/group_analytics_stage_serializer.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class GroupAnalyticsStageSerializer < BaseSerializer + entity GroupAnalyticsStageEntity +end diff --git a/lib/gitlab/cycle_analytics/base_event_fetcher.rb b/lib/gitlab/cycle_analytics/base_event_fetcher.rb index 44bf8775f42..f2a0a9c4b7b 100644 --- a/lib/gitlab/cycle_analytics/base_event_fetcher.rb +++ b/lib/gitlab/cycle_analytics/base_event_fetcher.rb @@ -9,7 +9,7 @@ module Gitlab MAX_EVENTS = 50 - def initialize(project:, stage:, options:) + def initialize(project: nil, stage:, options:) @project = project @stage = stage @options = options @@ -72,12 +72,16 @@ module Gitlab end def serialization_context - namespace = @group ? @group.name : @project.namespace + namespace = group ? group.name : @project.namespace { namespace: namespace } end def projects - [@project] + group ? group.projects : [@project] + end + + def group + @group ||= @options.fetch(:group, nil) end end end diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb index 9be96780247..6e807b96906 100644 --- a/lib/gitlab/cycle_analytics/base_stage.rb +++ b/lib/gitlab/cycle_analytics/base_stage.rb @@ -5,7 +5,7 @@ module Gitlab class BaseStage include BaseQuery - def initialize(project:, options:) + def initialize(project: nil, options:) @project = project @options = options end @@ -14,8 +14,8 @@ module Gitlab event_fetcher.fetch end - def as_json - AnalyticsStageSerializer.new.represent(self) + def as_json(serializer: AnalyticsStageSerializer) + serializer.new.represent(self) end def title @@ -38,6 +38,10 @@ module Gitlab end end + def group_median + median_query(projects.map(&:id)) + end + def median_query(project_ids) # Build a `SELECT` query. We find the first of the `end_time_attrs` that isn't `NULL` (call this end_time). # Next, we find the first of the start_time_attrs that isn't `NULL` (call this start_time). @@ -73,7 +77,11 @@ module Gitlab end def projects - [@project] + group ? group.projects : [@project] + end + + def group + @group ||= @options.fetch(:group, nil) end end end diff --git a/spec/models/cycle_analytics/group_level_spec.rb b/spec/models/cycle_analytics/group_level_spec.rb new file mode 100644 index 00000000000..4f0502970df --- /dev/null +++ b/spec/models/cycle_analytics/group_level_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe CycleAnalytics::GroupLevel do + let(:group) { create(:group)} + let(:project) { create(:project, :repository, namespace: group) } + let(:from_date) { 10.days.ago } + let(:user) { create(:user, :admin) } + let(:issue) { create(:issue, project: project, created_at: 2.days.ago) } + let(:milestone) { create(:milestone, project: project) } + let(:mr) { create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}") } + let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha, head_pipeline_of: mr) } + + subject { described_class.new(project: nil, options: { from: from_date, group: group }) } + + describe '#permissions' do + it 'returns permissions' do + expect(subject.permissions.values.uniq).to eq([true]) + end + end + + describe '#stats' do + before do + allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([issue]) + + create_cycle(user, project, issue, mr, milestone, pipeline) + deploy_master(user, project) + end + + it 'returns medians for each stage for a specific group' do + expect(subject.no_stats?).to eq(false) + end + end +end |