diff options
25 files changed, 296 insertions, 190 deletions
diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index 961183f44c3..2b4db0fd4aa 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -1,67 +1,67 @@ module Projects module CycleAnalytics -class EventsController < Projects::ApplicationController - include CycleAnalyticsParams - - before_action :authorize_read_cycle_analytics! - before_action :authorize_builds!, only: [:test, :staging] - - def issue - render_events(events.issue_events) - end - - def plan - render_events(events.plan_events) - end - - def code - render_events(events.code_events) - end - - def test - @options = { from: start_date(events_params), branch: events_params[:branch_name] } - - render_events(events.test_events) - end - - def review - render_events(events.review_events) - end - - def staging - render_events(events.staging_events) - end - - def production - render_events(events.production_events) - end - - private - - def render_events(events_list) - respond_to do |format| - format.html - format.json { render json: { events: events_list } } + class EventsController < Projects::ApplicationController + include CycleAnalyticsParams + + before_action :authorize_read_cycle_analytics! + before_action :authorize_builds!, only: [:test, :staging] + + def issue + render_events(events.issue_events) + end + + def plan + render_events(events.plan_events) + end + + def code + render_events(events.code_events) + end + + def test + @options = { from: start_date(events_params), branch: events_params[:branch_name] } + + render_events(events.test_events) + end + + def review + render_events(events.review_events) + end + + def staging + render_events(events.staging_events) + end + + def production + render_events(events.production_events) + end + + private + + def render_events(events_list) + respond_to do |format| + format.html + format.json { render json: { events: events_list } } + end + end + + def events + @events ||= Gitlab::CycleAnalytics::Events.new(project: project, options: options) + end + + def options + @options ||= { from: start_date(events_params) } + end + + def events_params + return {} unless params[:events].present? + + params[:events].slice(:start_date, :branch_name) + end + + def authorize_builds! + return access_denied! unless current_user.can?(:read_build, project) + end end end - - def events - @events ||= Gitlab::CycleAnalytics::Events.new(project: project, options: options) - end - - def options - @options ||= { from: start_date(events_params) } - end - - def events_params - return {} unless params[:events].present? - - params[:events].slice(:start_date, :branch_name) - end - - def authorize_builds! - return access_denied! unless current_user.can?(:read_build, project) - end -end - end end diff --git a/app/serializers/entity_date_helper.rb b/app/serializers/entity_date_helper.rb index 0772f652f0c..57817ce1812 100644 --- a/app/serializers/entity_date_helper.rb +++ b/app/serializers/entity_date_helper.rb @@ -4,4 +4,4 @@ module EntityDateHelper def interval_in_words(diff) "#{distance_of_time_in_words(diff.to_f)} ago" end -end
\ No newline at end of file +end diff --git a/lib/gitlab/cycle_analytics/base_config.rb b/lib/gitlab/cycle_analytics/base_config.rb new file mode 100644 index 00000000000..06ae9a15a7a --- /dev/null +++ b/lib/gitlab/cycle_analytics/base_config.rb @@ -0,0 +1,17 @@ +module Gitlab + module CycleAnalytics + class BaseConfig + extend MetricsFetcher + + class << self + attr_reader :start_time_attrs, :end_time_attrs, :projections + end + + def self.order + @order || @start_time_attrs + end + + def self.query(base_query); end + end + end +end diff --git a/lib/gitlab/cycle_analytics/code_config.rb b/lib/gitlab/cycle_analytics/code_config.rb new file mode 100644 index 00000000000..c27419310db --- /dev/null +++ b/lib/gitlab/cycle_analytics/code_config.rb @@ -0,0 +1,18 @@ +module Gitlab + module CycleAnalytics + class CodeConfig < BaseConfig + @start_time_attrs = issue_metrics_table[:first_mentioned_in_commit_at] + + @end_time_attrs = mr_table[:created_at] + + @projections = [mr_table[:title], + mr_table[:iid], + mr_table[:id], + mr_table[:created_at], + mr_table[:state], + mr_table[:author_id]] + + @order = mr_table[:created_at] + end + end +end diff --git a/lib/gitlab/cycle_analytics/events_fetcher.rb b/lib/gitlab/cycle_analytics/events_fetcher.rb index d19ce6e0f6f..714afb88833 100644 --- a/lib/gitlab/cycle_analytics/events_fetcher.rb +++ b/lib/gitlab/cycle_analytics/events_fetcher.rb @@ -8,22 +8,10 @@ module Gitlab end def fetch(stage:) - custom_query = "#{stage}_custom_query".to_sym - - @query.execute(stage) do |base_query| - public_send(custom_query, base_query) if self.respond_to?(custom_query) + @query.execute(stage) do |stage_class, base_query| + stage_class.query(base_query) end end - - def plan_custom_query(base_query) - base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])) - end - - def test_custom_query(base_query) - base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) - end - - alias_method :staging_custom_query, :test_custom_query end end end diff --git a/lib/gitlab/cycle_analytics/events_query.rb b/lib/gitlab/cycle_analytics/events_query.rb index 9d69e267d8d..b61320fc561 100644 --- a/lib/gitlab/cycle_analytics/events_query.rb +++ b/lib/gitlab/cycle_analytics/events_query.rb @@ -11,7 +11,6 @@ module Gitlab def execute(stage, &block) @stage = stage - @config = QueryConfig.get(stage) query = build_query(&block) ActiveRecord::Base.connection.exec_query(query.to_sql) @@ -21,15 +20,11 @@ module Gitlab def build_query base_query = base_query_for(@stage) - diff_fn = subtract_datetimes_diff(@config[:base_query], @config[:start_time_attrs], @config[:end_time_attrs]) + diff_fn = subtract_datetimes_diff(base_query, stage_class.start_time_attrs, stage_class.end_time_attrs) - yield base_query if block_given? + yield(stage_class, base_query) if block_given? - base_query.project(extract_epoch(diff_fn).as('total_time'), *@config[:projections]).order(order.desc) - end - - def order - @config[:order] || @config[:start_time_attrs] + base_query.project(extract_epoch(diff_fn).as('total_time'), *stage_class.projections).order(stage_class.order.desc) end def extract_epoch(arel_attribute) @@ -37,6 +32,10 @@ module Gitlab Arel.sql(%Q{EXTRACT(EPOCH FROM (#{arel_attribute.to_sql}))}) end + + def stage_class + @stage_class ||= "Gitlab::CycleAnalytics::#{@stage.to_s.camelize}Config".constantize + end end end end diff --git a/lib/gitlab/cycle_analytics/issue_config.rb b/lib/gitlab/cycle_analytics/issue_config.rb new file mode 100644 index 00000000000..985ac76feb2 --- /dev/null +++ b/lib/gitlab/cycle_analytics/issue_config.rb @@ -0,0 +1,16 @@ +module Gitlab + module CycleAnalytics + class IssueConfig < BaseConfig + @start_time_attrs = issue_table[:created_at] + + @end_time_attrs = [issue_metrics_table[:first_associated_with_milestone_at], + issue_metrics_table[:first_added_to_board_at]] + + @projections = [issue_table[:title], + issue_table[:iid], + issue_table[:id], + issue_table[:created_at], + issue_table[:author_id]] + end + end +end diff --git a/lib/gitlab/cycle_analytics/plan_config.rb b/lib/gitlab/cycle_analytics/plan_config.rb new file mode 100644 index 00000000000..453ee26b4c9 --- /dev/null +++ b/lib/gitlab/cycle_analytics/plan_config.rb @@ -0,0 +1,17 @@ +module Gitlab + module CycleAnalytics + class PlanConfig < BaseConfig + @start_time_attrs = issue_metrics_table[:first_associated_with_milestone_at] + + @end_time_attrs = [issue_metrics_table[:first_added_to_board_at], + issue_metrics_table[:first_mentioned_in_commit_at]] + + @projections = [mr_diff_table[:st_commits].as('commits'), + issue_metrics_table[:first_mentioned_in_commit_at]] + + def self.query(base_query) + base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id])) + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/production_config.rb b/lib/gitlab/cycle_analytics/production_config.rb new file mode 100644 index 00000000000..dc9f1d5d059 --- /dev/null +++ b/lib/gitlab/cycle_analytics/production_config.rb @@ -0,0 +1,15 @@ +module Gitlab + module CycleAnalytics + class ProductionConfig < BaseConfig + @start_time_attrs = issue_table[:created_at] + + @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] + + @projections = [issue_table[:title], + issue_table[:iid], + issue_table[:id], + issue_table[:created_at], + issue_table[:author_id]] + end + end +end diff --git a/lib/gitlab/cycle_analytics/query_config.rb b/lib/gitlab/cycle_analytics/query_config.rb deleted file mode 100644 index 4fddbad95f2..00000000000 --- a/lib/gitlab/cycle_analytics/query_config.rb +++ /dev/null @@ -1,99 +0,0 @@ -module Gitlab - module CycleAnalytics - class QueryConfig - include MetricsFetcher - - def self.get(*args) - new(*args).get - end - - def initialize(stage) - @stage = stage - end - - def get - public_send(@stage).freeze if self.respond_to?(@stage) - end - - def issue - { - start_time_attrs: issue_table[:created_at], - end_time_attrs: [issue_metrics_table[:first_associated_with_milestone_at], - issue_metrics_table[:first_added_to_board_at]], - projections: [issue_table[:title], - issue_table[:iid], - issue_table[:id], - issue_table[:created_at], - issue_table[:author_id]] - } - end - - def plan - { - start_time_attrs: issue_metrics_table[:first_associated_with_milestone_at], - end_time_attrs: [issue_metrics_table[:first_added_to_board_at], - issue_metrics_table[:first_mentioned_in_commit_at]], - projections: [mr_diff_table[:st_commits].as('commits'), - issue_metrics_table[:first_mentioned_in_commit_at]] - } - end - - def code - { - start_time_attrs: issue_metrics_table[:first_mentioned_in_commit_at], - end_time_attrs: mr_table[:created_at], - projections: [mr_table[:title], - mr_table[:iid], - mr_table[:id], - mr_table[:created_at], - mr_table[:state], - mr_table[:author_id]], - order: mr_table[:created_at] - } - end - - def test - { - start_time_attrs: mr_metrics_table[:latest_build_started_at], - end_time_attrs: mr_metrics_table[:latest_build_finished_at], - projections: [build_table[:id]], - order: build_table[:created_at] - } - end - - def review - { - start_time_attrs: mr_table[:created_at], - end_time_attrs: mr_metrics_table[:merged_at], - projections: [mr_table[:title], - mr_table[:iid], - mr_table[:id], - mr_table[:created_at], - mr_table[:state], - mr_table[:author_id]] - } - end - - def staging - { - start_time_attrs: mr_metrics_table[:merged_at], - end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], - projections: [build_table[:id]], - order: build_table[:created_at] - } - end - - def production - { - start_time_attrs: issue_table[:created_at], - end_time_attrs: mr_metrics_table[:first_deployed_to_production_at], - projections: [issue_table[:title], - issue_table[:iid], - issue_table[:id], - issue_table[:created_at], - issue_table[:author_id]] - } - end - end - end -end diff --git a/lib/gitlab/cycle_analytics/review_config.rb b/lib/gitlab/cycle_analytics/review_config.rb new file mode 100644 index 00000000000..e91dc1ea104 --- /dev/null +++ b/lib/gitlab/cycle_analytics/review_config.rb @@ -0,0 +1,16 @@ +module Gitlab + module CycleAnalytics + class ReviewConfig < BaseConfig + @start_time_attrs = mr_table[:created_at] + + @end_time_attrs = mr_metrics_table[:merged_at] + + @projections = [mr_table[:title], + mr_table[:iid], + mr_table[:id], + mr_table[:created_at], + mr_table[:state], + mr_table[:author_id]] + end + end +end diff --git a/lib/gitlab/cycle_analytics/staging_config.rb b/lib/gitlab/cycle_analytics/staging_config.rb new file mode 100644 index 00000000000..f6f471286a0 --- /dev/null +++ b/lib/gitlab/cycle_analytics/staging_config.rb @@ -0,0 +1,14 @@ +module Gitlab + module CycleAnalytics + class StagingConfig < BaseConfig + @start_time_attrs = mr_metrics_table[:merged_at] + @end_time_attrs = mr_metrics_table[:first_deployed_to_production_at] + @projections = [build_table[:id]] + @order = build_table[:created_at] + + def self.query(base_query) + base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) + end + end + end +end diff --git a/lib/gitlab/cycle_analytics/test_config.rb b/lib/gitlab/cycle_analytics/test_config.rb new file mode 100644 index 00000000000..6cb80f9a62c --- /dev/null +++ b/lib/gitlab/cycle_analytics/test_config.rb @@ -0,0 +1,14 @@ +module Gitlab + module CycleAnalytics + class TestConfig < BaseConfig + @start_time_attrs = mr_metrics_table[:latest_build_started_at] + @end_time_attrs = mr_metrics_table[:latest_build_finished_at] + @projections = [build_table[:id]] + @order = build_table[:created_at] + + def self.query(base_query) + base_query.join(build_table).on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id])) + end + end + end +end diff --git a/spec/lib/gitlab/cycle_analytics/code_config_spec.rb b/spec/lib/gitlab/cycle_analytics/code_config_spec.rb new file mode 100644 index 00000000000..7b89fdd76aa --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/code_config_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_config_spec' + +describe Gitlab::CycleAnalytics::CodeConfig do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).not_to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index d46e70e9ba2..aa0e54582f1 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -9,6 +9,8 @@ describe Gitlab::CycleAnalytics::Events do subject { described_class.new(project: project, options: { from: from_date }) } before do + allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([context]) + setup(context) end @@ -317,6 +319,8 @@ describe Gitlab::CycleAnalytics::Events do def setup(context) milestone = create(:milestone, project: project) context.update(milestone: milestone) - create_merge_request_closing_issue(context) + mr = create_merge_request_closing_issue(context) + + ProcessCommitWorker.new.perform(project.id, user.id, mr.commits.last.sha) end end diff --git a/spec/lib/gitlab/cycle_analytics/issue_config_spec.rb b/spec/lib/gitlab/cycle_analytics/issue_config_spec.rb new file mode 100644 index 00000000000..ff8cd1e8068 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/issue_config_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_config_spec' + +describe Gitlab::CycleAnalytics::IssueConfig do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/plan_config_spec.rb b/spec/lib/gitlab/cycle_analytics/plan_config_spec.rb new file mode 100644 index 00000000000..92698defa5d --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/plan_config_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_config_spec' + +describe Gitlab::CycleAnalytics::PlanConfig do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/production_config_spec.rb b/spec/lib/gitlab/cycle_analytics/production_config_spec.rb new file mode 100644 index 00000000000..92485777334 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/production_config_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_config_spec' + +describe Gitlab::CycleAnalytics::ProductionConfig do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/review_config_spec.rb b/spec/lib/gitlab/cycle_analytics/review_config_spec.rb new file mode 100644 index 00000000000..59cb18ac16d --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/review_config_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_config_spec' + +describe Gitlab::CycleAnalytics::ReviewConfig do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/shared_config_spec.rb b/spec/lib/gitlab/cycle_analytics/shared_config_spec.rb new file mode 100644 index 00000000000..060a59151d8 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/shared_config_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +shared_examples 'default query config' do + it 'has the start attributes' do + expect(described_class.start_time_attrs).not_to be_nil + end + + it 'has the end attributes' do + expect(described_class.end_time_attrs ).not_to be_nil + end + + it 'has the projection attributes' do + expect(described_class.projections).not_to be_nil + end +end diff --git a/spec/lib/gitlab/cycle_analytics/staging_config_spec.rb b/spec/lib/gitlab/cycle_analytics/staging_config_spec.rb new file mode 100644 index 00000000000..f13885d7f48 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/staging_config_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_config_spec' + +describe Gitlab::CycleAnalytics::StagingConfig do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).not_to eq(described_class.start_time_attrs) + end +end diff --git a/spec/lib/gitlab/cycle_analytics/test_config_spec.rb b/spec/lib/gitlab/cycle_analytics/test_config_spec.rb new file mode 100644 index 00000000000..59421b99513 --- /dev/null +++ b/spec/lib/gitlab/cycle_analytics/test_config_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' +require 'lib/gitlab/cycle_analytics/shared_config_spec' + +describe Gitlab::CycleAnalytics::TestConfig do + it_behaves_like 'default query config' + + it 'has the default order' do + expect(described_class.order).not_to eq(described_class.start_time_attrs) + end +end diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index 280aa2152b1..1c78cd368db 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -12,6 +12,8 @@ describe 'cycle analytics events' do deploy_master login_as(user) + + allow_any_instance_of(Gitlab::ReferenceExtractor).to receive(:issues).and_return([context]) end it 'lists the issue events' do @@ -143,6 +145,6 @@ describe 'cycle analytics events' do merge_merge_requests_closing_issue(issue) - Issue::Metrics.update_all(first_mentioned_in_commit_at: mr.commits.last.committed_date) + ProcessCommitWorker.new.perform(project.id, user.id, mr.commits.last.sha) end end diff --git a/spec/serializers/analytics_generic_entity_spec.rb b/spec/serializers/analytics_generic_entity_spec.rb index 7c731da1dd1..3bb15cb9475 100644 --- a/spec/serializers/analytics_generic_entity_spec.rb +++ b/spec/serializers/analytics_generic_entity_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe AnalyticsGenericEntity do let(:user) { create(:user) } - let(:entity_hash) { + let(:entity_hash) do { total_time: "172802.724419", title: "Eos voluptatem inventore in sed.", @@ -11,7 +11,7 @@ describe AnalyticsGenericEntity do created_at: "2016-11-12 15:04:02.948604", author: user, } - } + end let(:project) { create(:empty_project) } let(:request) { EntityRequest.new(project: project, entity: :merge_request) } diff --git a/spec/serializers/analytics_generic_serializer_spec.rb b/spec/serializers/analytics_generic_serializer_spec.rb index 9eeb7bee562..99f2254d22c 100644 --- a/spec/serializers/analytics_generic_serializer_spec.rb +++ b/spec/serializers/analytics_generic_serializer_spec.rb @@ -10,7 +10,7 @@ describe AnalyticsGenericSerializer do let(:user) { create(:user) } let(:json) { serializer.as_json } let(:project) { create(:project) } - let(:resource) { + let(:resource) do { total_time: "172802.724419", title: "Eos voluptatem inventore in sed.", @@ -19,7 +19,7 @@ describe AnalyticsGenericSerializer do created_at: "2016-11-12 15:04:02.948604", author: user, } - } + end context 'when there is a single object provided' do it 'it generates payload for single object' do |