Commit b2d89761 authored by Adam Hegyi's avatar Adam Hegyi Committed by Tetiana Chupryna

Collect all distinct VSA stages

parent 1c85f188
......@@ -173,8 +173,9 @@ module Analytics
end
def stages
@stages ||= Analytics::CycleAnalytics::GroupStage
.distinct_stages_within_hierarchy(group)
@stages ||= Gitlab::Analytics::CycleAnalytics::DistinctStageLoader
.new(group: group)
.stages
.select { |stage| stage.start_event.object_type == model }
end
......
# frozen_string_literal: true
module Gitlab
module Analytics
module CycleAnalytics
# This class is responsible for loading distinct stage records
# within the group hierarchy. The stage metadata is uniquely identified
# by its stage_event_hash_id column. There can be multiple stages with the
# same metadata (created with different names or within different subgroups).
#
# Example:
#
# Stage 1 (config: issue_created - issue_closed)
# Stage 2 (config: issue_created - issue_deployed_to_production)
# Stage 3 (config: issue_created - issue_closed) # same metadata as #1
#
# Expected results:
#
# Stage 1, Stage 2
#
# The class also adds two "in-memory" stages into the distinct calculation which
# represent the CycleTime and LeadTime metrics. These metrics are calculated as
# pre-defined VSA stages.
class DistinctStageLoader
def initialize(group:)
@group = group
end
def stages
[
*group_stages,
add_stage_event_hash_id(in_memory_lead_time_stage),
add_stage_event_hash_id(in_memory_cycle_time_stage)
].uniq(&:stage_event_hash_id)
end
private
attr_reader :group
def group_stages
@group_stages ||= ::Analytics::CycleAnalytics::GroupStage.distinct_stages_within_hierarchy(group)
end
def in_memory_lead_time_stage
::Analytics::CycleAnalytics::GroupStage.new(
name: 'lead time', # not visible to the user
start_event_identifier: Summary::LeadTime.start_event_identifier,
end_event_identifier: Summary::LeadTime.end_event_identifier,
group: group
)
end
def in_memory_cycle_time_stage
::Analytics::CycleAnalytics::GroupStage.new(
name: 'cycle time',
start_event_identifier: Summary::CycleTime.start_event_identifier,
end_event_identifier: Summary::CycleTime.end_event_identifier,
group: group
)
end
# rubocop: disable CodeReuse/ActiveRecord
def add_stage_event_hash_id(stage)
# find or create the stage event hash
hash_record = ::Analytics::CycleAnalytics::StageEventHash.find_by(hash_sha256: stage.events_hash_code)
stage.stage_event_hash_id = if hash_record
hash_record.id
else
::Analytics::CycleAnalytics::StageEventHash.record_id_by_hash_sha256(stage.events_hash_code)
end
stage
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
end
......@@ -21,11 +21,11 @@ module Gitlab
n_('day', 'days', value)
end
def start_event_identifier
def self.start_event_identifier
raise NotImplementedError, "Expected #{self.name} to implement start_event_identifier"
end
def end_event_identifier
def self.end_event_identifier
raise NotImplementedError, "Expected #{self.name} to implement end_event_identifier"
end
......@@ -36,8 +36,8 @@ module Gitlab
private
def assign_event_identifiers
@stage.start_event_identifier = start_event_identifier
@stage.end_event_identifier = end_event_identifier
@stage.start_event_identifier = self.class.start_event_identifier
@stage.end_event_identifier = self.class.end_event_identifier
end
def data_collector
......
......@@ -9,11 +9,11 @@ module Gitlab
_('Cycle Time')
end
def start_event_identifier
def self.start_event_identifier
:issue_first_mentioned_in_commit
end
def end_event_identifier
def self.end_event_identifier
:issue_closed
end
end
......
......@@ -9,11 +9,11 @@ module Gitlab
_('Lead Time')
end
def start_event_identifier
def self.start_event_identifier
:issue_created
end
def end_event_identifier
def self.end_event_identifier
:issue_closed
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Analytics::CycleAnalytics::DistinctStageLoader do
let_it_be(:group) { create(:group) }
let_it_be(:stage_1) { create(:cycle_analytics_group_stage, group: group, start_event_identifier: :merge_request_created, end_event_identifier: :merge_request_merged) }
let_it_be(:stage_2) { create(:cycle_analytics_group_stage, group: group, start_event_identifier: :issue_created, end_event_identifier: :issue_first_associated_with_milestone) }
let_it_be(:stage_duplicate) { create(:cycle_analytics_group_stage, group: group, start_event_identifier: :issue_created, end_event_identifier: :issue_first_associated_with_milestone) }
let_it_be(:stage_triplicate) { create(:cycle_analytics_group_stage, group: group, start_event_identifier: :issue_created, end_event_identifier: :issue_first_associated_with_milestone) }
subject(:distinct_stages) { described_class.new(group: group).stages }
it 'returns the distinct stages by stage_event_hash_id' do
distinct_stage_hash_ids = subject.map(&:stage_event_hash_id)
expect(distinct_stage_hash_ids).to eq(distinct_stage_hash_ids.uniq)
end
context 'when lead time and cycle time are not defined as stages' do
it 'returns in-memory stages' do
lead_time = distinct_stages.find { |stage| stage.name == 'lead time' }
cycle_time = distinct_stages.find { |stage| stage.name == 'cycle time' }
expect(lead_time).to be_present
expect(cycle_time).to be_present
expect(lead_time.stage_event_hash_id).not_to be_nil
expect(cycle_time.stage_event_hash_id).not_to be_nil
expect(lead_time.stage_event_hash_id).not_to eq(cycle_time.stage_event_hash_id)
end
it 'creates two stage event hash records' do
expect { distinct_stages }.to change { Analytics::CycleAnalytics::StageEventHash.count }.by(2)
end
it 'returns 4 stages' do
expect(distinct_stages.size).to eq(4)
end
end
context 'when lead time and cycle time are persisted stages' do
let_it_be(:cycle_time) do
create(:cycle_analytics_group_stage,
group: group,
start_event_identifier: :issue_created,
end_event_identifier: :issue_first_associated_with_milestone)
end
let_it_be(:lead_tiem) do
create(:cycle_analytics_group_stage,
group: group,
start_event_identifier: :issue_created,
end_event_identifier: :issue_first_associated_with_milestone)
end
it 'does not create extra stage event hash records' do
expect { distinct_stages }.to change { Analytics::CycleAnalytics::StageEventHash.count }
end
end
end
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