Skip to content
Snippets Groups Projects
Commit ed39d61d authored by James Lopez's avatar James Lopez
Browse files

refactor events facade so it uses separate classes and refactor query stuff

parent f9de157e
No related branches found
No related tags found
No related merge requests found
Showing
with 864 additions and 657 deletions
Loading
Loading
@@ -19,7 +19,7 @@ module Projects
end
def test
@options = { from: start_date(events_params), branch: events_params[:branch_name] }
@options[:branch] = events_params[:branch_name]
render_events(events.test_events)
end
Loading
Loading
class CycleAnalytics
include Gitlab::CycleAnalytics::MetricsFetcher
def initialize(project, from:)
@project = project
@from = from
@fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project, from: from, branch: nil)
end
 
def summary
Loading
Loading
@@ -11,45 +10,45 @@ class CycleAnalytics
end
 
def issue
calculate_metric(: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]])
end
 
def plan
calculate_metric(:plan,
@fetcher.calculate_metric(:plan,
[Issue::Metrics.arel_table[:first_associated_with_milestone_at],
Issue::Metrics.arel_table[:first_added_to_board_at]],
Issue::Metrics.arel_table[:first_mentioned_in_commit_at])
end
 
def code
calculate_metric(:code,
@fetcher.calculate_metric(:code,
Issue::Metrics.arel_table[:first_mentioned_in_commit_at],
MergeRequest.arel_table[:created_at])
end
 
def test
calculate_metric(:test,
@fetcher.calculate_metric(:test,
MergeRequest::Metrics.arel_table[:latest_build_started_at],
MergeRequest::Metrics.arel_table[:latest_build_finished_at])
end
 
def review
calculate_metric(:review,
@fetcher.calculate_metric(:review,
MergeRequest.arel_table[:created_at],
MergeRequest::Metrics.arel_table[:merged_at])
end
 
def staging
calculate_metric(:staging,
@fetcher.calculate_metric(:staging,
MergeRequest::Metrics.arel_table[:merged_at],
MergeRequest::Metrics.arel_table[:first_deployed_to_production_at])
end
 
def production
calculate_metric(:production,
@fetcher.calculate_metric(:production,
Issue.arel_table[:created_at],
MergeRequest::Metrics.arel_table[:first_deployed_to_production_at])
end
Loading
Loading
class AnalyticsGenericSerializer < BaseSerializer
entity AnalyticsGenericEntity
def represent(resource, opts = {})
resource.symbolize_keys!
 
Loading
Loading
class AnalyticsGenericEntity < Grape::Entity
class AnalyticsIssueEntity < Grape::Entity
include RequestAwareEntity
include EntityDateHelper
 
expose :title
expose :state, if: ->(_instance, options) { options[:request].entity == :merge_request }
expose :author, using: UserEntity
 
expose :iid do |object|
Loading
Loading
@@ -19,7 +18,7 @@ class AnalyticsGenericEntity < Grape::Entity
end
 
expose :url do |object|
url_to("namespace_project_#{request.entity}".to_sym, id: object[:iid].to_s)
url_to(:namespace_project_issue, id: object[:iid].to_s)
end
 
private
Loading
Loading
class AnalyticsIssueSerializer < AnalyticsGenericSerializer
entity AnalyticsIssueEntity
end
class AnalyticsMergeRequestEntity < AnalyticsIssueEntity
expose :state
expose :url do |object|
url_to(:namespace_project_merge_request, id: object[:iid].to_s)
end
end
class AnalyticsMergeRequestSerializer < AnalyticsGenericSerializer
entity AnalyticsMergeRequestEntity
end
This diff is collapsed.
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
module Gitlab
module CycleAnalytics
class BaseEvent
extend MetricsTables
class << self
attr_reader :stage, :start_time_attrs, :end_time_attrs, :projections
def order
@order || @start_time_attrs
end
def query(base_query); end
def fetch(query)
query.execute(self).map { |event| serialize(event, query) }
end
private
def serialize(event, query)
raise NotImplementedError.new("Expected #{self.name} to implement serialize(event)")
end
end
end
end
end
module Gitlab
module CycleAnalytics
class CodeConfig < BaseConfig
class CodeEvent < BaseEvent
@stage = :code
@start_time_attrs = issue_metrics_table[:first_mentioned_in_commit_at]
 
@end_time_attrs = mr_table[:created_at]
Loading
Loading
@@ -13,6 +14,12 @@ module Gitlab
mr_table[:author_id]]
 
@order = mr_table[:created_at]
def self.serialize(event, query)
event['author'] = User.find(event.delete('author_id'))
AnalyticsMergeRequestSerializer.new(project: query.project).represent(event).as_json
end
end
end
end
module Gitlab
module CycleAnalytics
class ReviewConfig < BaseConfig
class TestEvent < BaseEvent
@start_time_attrs = mr_table[:created_at]
 
@end_time_attrs = mr_metrics_table[:merged_at]
Loading
Loading
Loading
Loading
@@ -3,73 +3,35 @@ module Gitlab
class Events
def initialize(project:, options:)
@project = project
@fetcher = EventsFetcher.new(project: project, options: options)
@query = EventsQuery.new(project: project, options: options)
end
 
def issue_events
@fetcher.fetch(stage: :issue).map { |event| serialize_event(event) }
IssueEvent.fetch(@query)
end
 
def plan_events
@fetcher.fetch(stage: :plan).map do |event|
st_commit = first_time_reference_commit(event.delete('commits'), event)
next unless st_commit
serialize_commit(event, st_commit)
end
PlanEvent.fetch(@query)
end
 
def code_events
@fetcher.fetch(stage: :code).map { |event| serialize_event(event, entity: :merge_request) }
CodeEvent.fetch(@query)
end
 
def test_events
@fetcher.fetch(stage: :test).map { |event| serialize_build_event(event) }
TestEvent.fetch(@query)
end
 
def review_events
@fetcher.fetch(stage: :review).map { |event| serialize_event(event, entity: :merge_request) }
ReviewEvent.fetch(@query)
end
 
def staging_events
@fetcher.fetch(stage: :staging).map { |event| serialize_build_event(event) }
StagingEvent.fetch(@query)
end
 
def production_events
@fetcher.fetch(stage: :production).map { |event| serialize_event(event) }
end
private
def serialize_event(event, entity: :issue)
event['author'] = User.find(event.delete('author_id'))
AnalyticsGenericSerializer.new(project: @project, entity: entity).represent(event).as_json
end
def serialize_build_event(event)
build = ::Ci::Build.find(event['id'])
AnalyticsBuildSerializer.new.represent(build).as_json
end
def first_time_reference_commit(commits, event)
YAML.load(commits).find do |commit|
next unless commit[:committed_date] && event['first_mentioned_in_commit_at']
commit[:committed_date].to_i == DateTime.parse(event['first_mentioned_in_commit_at'].to_s).to_i
end
end
def serialize_commit(event, st_commit)
commit = Commit.new(Gitlab::Git::Commit.new(st_commit), @project)
AnalyticsCommitSerializer.new(project: @project, total_time: event['total_time']).represent(commit).as_json
end
def interval_in_words(diff)
"#{distance_of_time_in_words(diff.to_f)} ago"
ProductionEvent.fetch(@query)
end
end
end
Loading
Loading
Loading
Loading
@@ -6,9 +6,7 @@ module Gitlab
end
 
def fetch(stage:)
@query.execute(stage) do |stage_class, base_query|
stage_class.query(base_query)
end
@query.execute(stage)
end
end
end
Loading
Loading
module Gitlab
module CycleAnalytics
class EventsQuery
include MetricsFetcher
attr_reader :project
 
def initialize(project:, options: {})
@project = project
@from = options[:from]
@branch = options[:branch]
@fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project, from: @from, branch: @branch)
end
 
def execute(stage, &block)
@stage = stage
query = build_query(&block)
def execute(stage_class)
@stage_class = stage_class
 
ActiveRecord::Base.connection.exec_query(query.to_sql)
end
 
private
 
def build_query
base_query = base_query_for(@stage)
diff_fn = subtract_datetimes_diff(base_query, stage_class.start_time_attrs, stage_class.end_time_attrs)
def query
base_query = @fetcher.base_query_for(@stage_class.stage)
diff_fn = @fetcher.subtract_datetimes_diff(base_query, @stage_class.start_time_attrs, @stage_class.end_time_attrs)
 
yield(stage_class, base_query) if block_given?
@stage_class.query(base_query)
 
base_query.project(extract_epoch(diff_fn).as('total_time'), *stage_class.projections).order(stage_class.order.desc)
base_query.project(extract_epoch(diff_fn).as('total_time'), *@stage_class.projections).order(@stage_class.order.desc)
end
 
def extract_epoch(arel_attribute)
Loading
Loading
@@ -32,10 +32,6 @@ 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
module Gitlab
module CycleAnalytics
class IssueConfig < BaseConfig
class IssueEvent < BaseEvent
@stage = :issue
@start_time_attrs = issue_table[:created_at]
 
@end_time_attrs = [issue_metrics_table[:first_associated_with_milestone_at],
Loading
Loading
@@ -11,6 +12,12 @@ module Gitlab
issue_table[:id],
issue_table[:created_at],
issue_table[:author_id]]
def self.serialize(event, query)
event['author'] = User.find(event.delete('author_id'))
AnalyticsIssueSerializer.new(project: query.project).represent(event).as_json
end
end
end
end
module Gitlab
module CycleAnalytics
module MetricsFetcher
class MetricsFetcher
include Gitlab::Database::Median
include Gitlab::Database::DateTime
include MetricsTables
 
DEPLOYMENT_METRIC_STAGES = %i[production staging]
 
private
def initialize(project:, from:, branch:)
@project = project
@project = project
@from = from
@branch = branch
end
 
def calculate_metric(name, start_time_attrs, end_time_attrs)
cte_table = Arel::Table.new("cte_table_for_#{name}")
Loading
Loading
@@ -49,38 +55,6 @@ module Gitlab
 
query
end
def mr_metrics_table
MergeRequest::Metrics.arel_table
end
def mr_table
MergeRequest.arel_table
end
def mr_diff_table
MergeRequestDiff.arel_table
end
def mr_closing_issues_table
MergeRequestsClosingIssues.arel_table
end
def issue_table
Issue.arel_table
end
def issue_metrics_table
Issue::Metrics.arel_table
end
def user_table
User.arel_table
end
def build_table
::CommitStatus.arel_table
end
end
end
end
module Gitlab
module CycleAnalytics
module MetricsTables
def mr_metrics_table
MergeRequest::Metrics.arel_table
end
def mr_table
MergeRequest.arel_table
end
def mr_diff_table
MergeRequestDiff.arel_table
end
def mr_closing_issues_table
MergeRequestsClosingIssues.arel_table
end
def issue_table
Issue.arel_table
end
def issue_metrics_table
Issue::Metrics.arel_table
end
def user_table
User.arel_table
end
def build_table
::CommitStatus.arel_table
end
end
end
end
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
module Gitlab
module CycleAnalytics
class PlanEvent < BaseEvent
@stage = :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]]
class << self
def query(base_query)
base_query.join(mr_diff_table).on(mr_diff_table[:merge_request_id].eq(mr_table[:id]))
end
private
def serialize(event, query)
st_commit = first_time_reference_commit(event.delete('commits'), event)
return unless st_commit
serialize_commit(event, st_commit, query)
end
def first_time_reference_commit(commits, event)
YAML.load(commits).find do |commit|
next unless commit[:committed_date] && event['first_mentioned_in_commit_at']
commit[:committed_date].to_i == DateTime.parse(event['first_mentioned_in_commit_at'].to_s).to_i
end
end
def serialize_commit(event, st_commit, query)
commit = Commit.new(Gitlab::Git::Commit.new(st_commit), @project)
AnalyticsCommitSerializer.new(project: query.project, total_time: event['total_time']).represent(commit).as_json
end
end
end
end
end
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment