Commit cf2dcf04 authored by James Lopez's avatar James Lopez

Refactor all query config stuff into separate classes and added specs

parent cbc9f0cd
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
......@@ -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
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 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
......@@ -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
......@@ -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
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
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 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
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
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
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
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
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
......@@ -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
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
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
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
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
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
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
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
......@@ -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
......@@ -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) }
......
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment