diff --git a/app/controllers/concerns/cycle_analytics_params.rb b/app/controllers/concerns/cycle_analytics_params.rb index 2aaf8f2b45179b3a442b0f5a2fd5e7a5c3c8ccf6..91456561a17e1ae41a562b7b6ff0e9012f4b71e3 100644 --- a/app/controllers/concerns/cycle_analytics_params.rb +++ b/app/controllers/concerns/cycle_analytics_params.rb @@ -1,6 +1,10 @@ module CycleAnalyticsParams extend ActiveSupport::Concern + def options + @options ||= { from: start_date(events_params), current_user: current_user } + end + def start_date(params) params[:start_date] == '30' ? 30.days.ago : 90.days.ago end diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index 13b3eec761fa52645d92aa1abd88fedccbf83659..5e9524b15db9e5c0bb75ec3cef2d792e94cec9dc 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -51,10 +51,6 @@ module Projects @events ||= Gitlab::CycleAnalytics::Events.new(project: project, options: options) end - def options - @options ||= { from: start_date(events_params), current_user: current_user } - end - def events_params return {} unless params[:events].present? diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb index 73fe3c9c4c9279faeb372430bbc0b38c08d362c2..93dbe2819e74c72e1c09685c2651a97c4dee5554 100644 --- a/app/controllers/projects/cycle_analytics_controller.rb +++ b/app/controllers/projects/cycle_analytics_controller.rb @@ -6,7 +6,9 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController before_action :authorize_read_cycle_analytics! def show - @cycle_analytics = ::CycleAnalytics.new(@project, current_user, from: start_date(cycle_analytics_params)) + @cycle_analytics = ::CycleAnalytics.new(@project, options: options) + + @cycle_analytics_no_data = @cycle_analytics.no_stats? respond_to do |format| format.html @@ -25,8 +27,8 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController def cycle_analytics_json { summary: @cycle_analytics.summary, - stats: nil, # TODO - permissions: @cycle_analytics.permissions(user: current_user)# TODO + stats: @cycle_analytics.stats, + permissions: @cycle_analytics.permissions(user: current_user) } end end diff --git a/app/models/cycle_analytics.rb b/app/models/cycle_analytics.rb index 9681d34f2d1d3bbc731aa532866951617b543c51..00e9f7c7d5cae42d07805e75fc6db8c5ad0817fb 100644 --- a/app/models/cycle_analytics.rb +++ b/app/models/cycle_analytics.rb @@ -1,7 +1,7 @@ class CycleAnalytics STAGES = %i[issue plan code test review staging production].freeze - def initialize(project, from:) + def initialize(project, options:) @project = project @options = options end @@ -10,22 +10,28 @@ class CycleAnalytics @summary ||= Gitlab::CycleAnalytics::Summary.new(@project, from: @options[:from]).data end - def method_missing(method_sym, *arguments, &block) - classify_stage(method_sym).new(project: @project, options: @options, stage: method_sym) + def stats + @stats ||= stats_per_stage + end + + def no_stats? + stats.map(&:value).compact.empty? end def permissions(user:) Gitlab::CycleAnalytics::Permissions.get(user: user, project: @project) end - def issue - @fetcher.calculate_metric(:issue, - Issue.arel_table[:created_at], - [Issue::Metrics.arel_table[:first_associated_with_milestone_at], - Issue::Metrics.arel_table[:first_added_to_board_at]]) + private + + def stats_per_stage + STAGES.map do |stage_name| + classify_stage(method_sym).new(project: @project, options: @options, stage: stage_name).median_data + end end - def classify_stage(method_sym) - "Gitlab::CycleAnalytics::#{method_sym.to_s.capitalize}Stage".constantize + def classify_stage(stage_name) + "Gitlab::CycleAnalytics::#{stage_name.to_s.capitalize}Stage".constantize end + end diff --git a/lib/gitlab/cycle_analytics/summary.rb b/lib/gitlab/cycle_analytics/summary.rb index 7d172855a94c05b246fa8d83cb2834229085ffe0..5f0103c9d5a88990960bb71762be8e524f630856 100644 --- a/lib/gitlab/cycle_analytics/summary.rb +++ b/lib/gitlab/cycle_analytics/summary.rb @@ -9,9 +9,9 @@ module Gitlab end def data - [serialize(issue), - serialize(commit), - serialize(deploy)] + [serialize(Summary::Issue.new(project: @project, from: @from)), + serialize(Summary::Commit.new(project: @project, from: @from)), + serialize(Summary::Deploy.new(project: @project, from: @from))] end private @@ -19,18 +19,6 @@ module Gitlab def serialize(summary_object) AnalyticsSummarySerializer.new.represent(summary_object).as_json end - - def issue - Summary::Issue.new(project: @project, from: @from) - end - - def deploy - Summary::Deploy.new(project: @project, from: @from) - end - - def commit - Summary::Commit.new(project: @project, from: @from) - end end end end diff --git a/spec/models/cycle_analytics/code_spec.rb b/spec/models/cycle_analytics/code_spec.rb index 7771785ead3e9182f2145765029a23f18068c455..4838b57e3533370b764a4783ceb151c4e6f240ad 100644 --- a/spec/models/cycle_analytics/code_spec.rb +++ b/spec/models/cycle_analytics/code_spec.rb @@ -6,7 +6,7 @@ describe 'CycleAnalytics#code', feature: true do let(:project) { create(:project) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, user, from: from_date) } + subject { CycleAnalyticsTest.new(project, options: { from: from_date }) } context 'with deployment' do generate_cycle_analytics_spec( @@ -16,10 +16,10 @@ describe 'CycleAnalytics#code', feature: true do -> (context, data) do context.create_commit_referencing_issue(data[:issue]) end]], - end_time_conditions: [["merge request that closes issue is created", - -> (context, data) do - context.create_merge_request_closing_issue(data[:issue]) - end]], + end_time_conditions: [["merge request that closes issue is created", + -> (context, data) do + context.create_merge_request_closing_issue(data[:issue]) + end]], post_fn: -> (context, data) do context.merge_merge_requests_closing_issue(data[:issue]) context.deploy_master @@ -50,10 +50,10 @@ describe 'CycleAnalytics#code', feature: true do -> (context, data) do context.create_commit_referencing_issue(data[:issue]) end]], - end_time_conditions: [["merge request that closes issue is created", - -> (context, data) do - context.create_merge_request_closing_issue(data[:issue]) - end]], + end_time_conditions: [["merge request that closes issue is created", + -> (context, data) do + context.create_merge_request_closing_issue(data[:issue]) + end]], post_fn: -> (context, data) do context.merge_merge_requests_closing_issue(data[:issue]) end) diff --git a/spec/models/cycle_analytics/issue_spec.rb b/spec/models/cycle_analytics/issue_spec.rb index 5ed3d37f2fbfb18601d2c271d88e1612a1f38d4e..ce6e99bbec9d9c571df90b534bafd8de59710322 100644 --- a/spec/models/cycle_analytics/issue_spec.rb +++ b/spec/models/cycle_analytics/issue_spec.rb @@ -6,7 +6,7 @@ describe 'CycleAnalytics#issue', models: true do let(:project) { create(:project) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, user, from: from_date) } + subject { CycleAnalyticsTest.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :issue, diff --git a/spec/models/cycle_analytics/plan_spec.rb b/spec/models/cycle_analytics/plan_spec.rb index baf3e3241a128cfac97440ef60cf3ac27b84a029..bd5a6a77b7a3d32d05adc4331535bc850d7aa2cd 100644 --- a/spec/models/cycle_analytics/plan_spec.rb +++ b/spec/models/cycle_analytics/plan_spec.rb @@ -6,7 +6,7 @@ describe 'CycleAnalytics#plan', feature: true do let(:project) { create(:project) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, user, from: from_date) } + subject { CycleAnalyticsTest.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :plan, diff --git a/spec/models/cycle_analytics/production_spec.rb b/spec/models/cycle_analytics/production_spec.rb index 21b9c6e71507a7e93fcd63c192bcd2dc346ea938..653e203b491d07cffe785e4db7431dab9820a68e 100644 --- a/spec/models/cycle_analytics/production_spec.rb +++ b/spec/models/cycle_analytics/production_spec.rb @@ -6,7 +6,7 @@ describe 'CycleAnalytics#production', feature: true do let(:project) { create(:project) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, user, from: from_date) } + subject { CycleAnalyticsTest.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :production, diff --git a/spec/models/cycle_analytics/review_spec.rb b/spec/models/cycle_analytics/review_spec.rb index 158621d59a43ef0edd38fca5e6230ea12cd554a5..219cd4c02122dc8fa1b2c9e7f43cafd48521e7a6 100644 --- a/spec/models/cycle_analytics/review_spec.rb +++ b/spec/models/cycle_analytics/review_spec.rb @@ -6,7 +6,7 @@ describe 'CycleAnalytics#review', feature: true do let(:project) { create(:project) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, user, from: from_date) } + subject { CycleAnalyticsTest.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :review, diff --git a/spec/models/cycle_analytics/staging_spec.rb b/spec/models/cycle_analytics/staging_spec.rb index dad653964b74cd59b16aae8b5c8426ace1f2b34f..8dffb6b8fe150aeefbd7ade4363161b74e1d0781 100644 --- a/spec/models/cycle_analytics/staging_spec.rb +++ b/spec/models/cycle_analytics/staging_spec.rb @@ -6,7 +6,7 @@ describe 'CycleAnalytics#staging', feature: true do let(:project) { create(:project) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, user, from: from_date) } + subject { CycleAnalyticsTest.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :staging, diff --git a/spec/models/cycle_analytics/summary_spec.rb b/spec/models/cycle_analytics/summary_spec.rb index 725bc68b25f457f1b9b9116d5d5b6e329756d393..1a54c57a27844cea3bbbbba38ef8290ee61fe608 100644 --- a/spec/models/cycle_analytics/summary_spec.rb +++ b/spec/models/cycle_analytics/summary_spec.rb @@ -4,7 +4,7 @@ describe CycleAnalytics::Summary, models: true do let(:project) { create(:project) } let(:from) { Time.now } let(:user) { create(:user, :admin) } - subject { described_class.new(project, user, from: from) } + subject { CycleAnalyticsTest.new(project, options: { from: from_date }) } describe "#new_issues" do it "finds the number of issues created after the 'from date'" do diff --git a/spec/models/cycle_analytics/test_spec.rb b/spec/models/cycle_analytics/test_spec.rb index 2313724e8f38274aba2301bbb98b95d2dc46ab5c..ac1304beca8048226d818b319dde8b578545eee0 100644 --- a/spec/models/cycle_analytics/test_spec.rb +++ b/spec/models/cycle_analytics/test_spec.rb @@ -6,7 +6,7 @@ describe 'CycleAnalytics#test', feature: true do let(:project) { create(:project) } let(:from_date) { 10.days.ago } let(:user) { create(:user, :admin) } - subject { CycleAnalytics.new(project, user, from: from_date) } + subject { CycleAnalyticsTest.new(project, options: { from: from_date }) } generate_cycle_analytics_spec( phase: :test, diff --git a/spec/support/cycle_analytics_helpers/test_generation.rb b/spec/support/cycle_analytics_helpers/test_generation.rb index 8e19a6c92e2e29c7d5b6b1adbe39760da44ddc2e..ab6241616168de78e7cc2ccddbb8552cde096585 100644 --- a/spec/support/cycle_analytics_helpers/test_generation.rb +++ b/spec/support/cycle_analytics_helpers/test_generation.rb @@ -1,8 +1,13 @@ +class CycleAnalyticsTest < CycleAnalytics + def method_missing(method_sym, *arguments, &block) + classify_stage(method_sym).new(project: @project, options: @options, stage: method_sym).median + end +end + # rubocop:disable Metrics/AbcSize # Note: The ABC size is large here because we have a method generating test cases with # multiple nested contexts. This shouldn't count as a violation. - module CycleAnalyticsHelpers module TestGeneration # Generate the most common set of specs that all cycle analytics phases need to have.