Commit f4d14663 authored by Adam Hegyi's avatar Adam Hegyi Committed by Kushal Pandya

Add additional events to customizable CA

This change defines additional events for creating customized cycle
analytics stages.
parent f2876fb0
# frozen_string_literal: true
module EE
module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
extend ActiveSupport::Concern
prepended do
extend ::Gitlab::Utils::StrongMemoize
end
EE_ENUM_MAPPING = {
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueClosed => 3,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstAddedToBoard => 4,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstAssociatedWithMilestone => 5,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstMentionedInCommit => 6,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueLastEdited => 7,
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestClosed => 105,
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestLastEdited => 106
}.freeze
EE_EVENTS = EE_ENUM_MAPPING.keys.freeze
EE_PAIRING_RULES = {
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueCreated => [
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueClosed,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstAddedToBoard,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstAssociatedWithMilestone,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstMentionedInCommit,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueLastEdited
],
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstAddedToBoard => [
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueClosed,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstAssociatedWithMilestone,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstMentionedInCommit,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueLastEdited
],
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstAssociatedWithMilestone => [
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueClosed,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstAddedToBoard,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstMentionedInCommit,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueLastEdited
],
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstMentionedInCommit => [
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueClosed,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstAssociatedWithMilestone,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstAddedToBoard,
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueLastEdited
],
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueClosed => [
::Gitlab::Analytics::CycleAnalytics::StageEvents::IssueLastEdited
],
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestCreated => [
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestClosed,
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestFirstDeployedToProduction,
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestLastBuildStarted,
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestLastBuildFinished,
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestLastEdited
],
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestClosed => [
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestFirstDeployedToProduction,
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestLastEdited
],
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestFirstDeployedToProduction => [
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestLastEdited
],
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestLastBuildStarted => [
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestClosed,
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestFirstDeployedToProduction,
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestLastEdited,
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestMerged
],
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestLastBuildFinished => [
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestClosed,
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestFirstDeployedToProduction,
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestLastEdited,
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestMerged
],
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestMerged => [
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestClosed,
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestFirstDeployedToProduction,
::Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestLastEdited
]
}.freeze
class_methods do
extend ::Gitlab::Utils::Override
override :events
def events
strong_memoize(:events) do
super + EE_EVENTS
end
end
override :pairing_rules
def pairing_rules
strong_memoize(:pairing_rules) do
# merging two hashes with array values
::Gitlab::Analytics::CycleAnalytics::StageEvents::PAIRING_RULES.merge(EE_PAIRING_RULES) do |klass, foss_events, ee_events|
foss_events + ee_events
end
end
end
override :enum_mapping
def enum_mapping
strong_memoize(:enum_mapping) do
super.merge(EE_ENUM_MAPPING)
end
end
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class IssueClosed < StageEvent
def self.name
s_("CycleAnalyticsEvent|Issue closed")
end
def self.identifier
:issue_closed
end
def object_type
Issue
end
def timestamp_projection
issue_table[:closed_at]
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class IssueFirstAddedToBoard < MetricsBasedStageEvent
def self.name
s_("CycleAnalyticsEvent|Issue first added to a board")
end
def self.identifier
:issue_first_added_to_board
end
def object_type
Issue
end
def timestamp_projection
issue_metrics_table[:first_added_to_board_at]
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class IssueFirstAssociatedWithMilestone < MetricsBasedStageEvent
def self.name
s_("CycleAnalyticsEvent|Issue first associated with a milestone")
end
def self.identifier
:issue_first_associated_with_milestone
end
def object_type
Issue
end
def timestamp_projection
issue_metrics_table[:first_associated_with_milestone_at]
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class IssueFirstMentionedInCommit < MetricsBasedStageEvent
def self.name
s_("CycleAnalyticsEvent|Issue first mentioned in a commit")
end
def self.identifier
:issue_first_mentioned_in_commit
end
def object_type
Issue
end
def timestamp_projection
issue_metrics_table[:first_mentioned_in_commit_at]
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class IssueLastEdited < StageEvent
def self.name
s_("CycleAnalyticsEvent|Issue last edited")
end
def self.identifier
:issue_last_edited
end
def object_type
Issue
end
def timestamp_projection
issue_table[:last_edited_at]
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class MergeRequestClosed < MetricsBasedStageEvent
def self.name
s_("CycleAnalyticsEvent|Merge request closed")
end
def self.identifier
:merge_request_closed
end
def object_type
MergeRequest
end
def timestamp_projection
mr_metrics_table[:latest_closed_at]
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class MergeRequestLastEdited < StageEvent
def self.name
s_("CycleAnalyticsEvent|Merge request last edited")
end
def self.identifier
:merge_request_last_edited
end
def object_type
MergeRequest
end
def timestamp_projection
mr_table[:last_edited_at]
end
end
end
end
end
end
......@@ -43,12 +43,15 @@ describe('CustomStageForm', () => {
invalidFeedback: '.invalid-feedback',
};
function selectDropdownOption(_wrapper, dropdown, index) {
_wrapper
function getDropdownOption(_wrapper, dropdown, index) {
return _wrapper
.find(dropdown)
.findAll('option')
.at(index)
.setSelected();
.at(index);
}
function selectDropdownOption(_wrapper, dropdown, index) {
getDropdownOption(_wrapper, dropdown, index).setSelected();
}
describe('Empty form', () => {
......@@ -97,21 +100,6 @@ describe('CustomStageForm', () => {
`<option value="${ev.identifier}">${ev.name}</option>`,
);
});
stopEvents.forEach(ev => {
expect(select.html()).not.toHaveHtml(
`<option value="${ev.identifier}">${ev.name}</option>`,
);
});
});
it('will exclude stop events for the dropdown', () => {
const select = wrapper.find(sel.startEvent);
stopEvents.forEach(ev => {
expect(select.html()).not.toHaveHtml(
`<option value="${ev.identifier}">${ev.name}</option>`,
);
});
});
});
......@@ -163,6 +151,9 @@ describe('CustomStageForm', () => {
});
describe('Stop event', () => {
const index = 2;
const currAllowed = startEvents[index].allowedEndEvents;
beforeEach(() => {
wrapper = createComponent({}, false);
});
......@@ -192,48 +183,51 @@ describe('CustomStageForm', () => {
it('will update the list of stop events when a start event is changed', done => {
let stopOptions = wrapper.find(sel.stopEvent).findAll('option');
const selectedStartEventIndex = 1;
const selectedStartEvent = startEvents[selectedStartEventIndex];
expect(stopOptions.length).toEqual(1);
selectDropdownOption(wrapper, sel.startEvent, 1);
selectDropdownOption(wrapper, sel.startEvent, selectedStartEventIndex);
Vue.nextTick(() => {
stopOptions = wrapper.find(sel.stopEvent).findAll('option');
expect(stopOptions.length).toEqual(2);
stopOptions = wrapper.find(sel.stopEvent);
selectedStartEvent.allowedEndEvents.forEach(identifier => {
expect(stopOptions.html()).toContain(identifier);
});
done();
});
});
it('will only display valid stop events allowed for the selected start event', done => {
it('will display all the valid stop events', done => {
let stopOptions = wrapper.find(sel.stopEvent).findAll('option');
const index = 2;
const selectedStopEventIdentifer = startEvents[index].allowedEndEvents[0];
const selectedStopEvent = stopEvents.find(
ev => ev.identifier === selectedStopEventIdentifer,
);
const possibleEndEvents = stopEvents.filter(ev => currAllowed.includes(ev.identifier));
expect(stopOptions.at(0).html()).toEqual('<option value="">Select stop event</option>');
selectDropdownOption(wrapper, sel.startEvent, index);
Vue.nextTick(() => {
stopOptions = wrapper.find(sel.stopEvent).findAll('option');
stopOptions = wrapper.find(sel.stopEvent);
[
{ name: 'Select stop event', identifier: '' },
{
name: selectedStopEvent.name,
identifier: selectedStopEvent.identifier,
},
].forEach(({ name, identifier }, i) => {
expect(stopOptions.at(i).html()).toEqual(
`<option value="${identifier}">${name}</option>`,
);
possibleEndEvents.forEach(({ name, identifier }) => {
expect(stopOptions.html()).toContain(`<option value="${identifier}">${name}</option>`);
});
done();
});
});
it('will not display stop events that are not in the list of allowed stop events', done => {
let stopOptions = wrapper.find(sel.stopEvent).findAll('option');
const excludedEndEvents = stopEvents.filter(ev => !currAllowed.includes(ev.identifier));
expect(stopOptions.at(0).html()).toEqual('<option value="">Select stop event</option>');
selectDropdownOption(wrapper, sel.startEvent, index);
[
{ name: 'Issue created', identifier: 'issue_created' },
{ name: 'Merge request closed', identifier: 'merge_request_closed' },
].forEach(({ name, identifier }) => {
Vue.nextTick(() => {
stopOptions = wrapper.find(sel.stopEvent);
excludedEndEvents.forEach(({ name, identifier }) => {
expect(wrapper.find(sel.stopEvent).html()).not.toHaveHtml(
`<option value="${identifier}">${name}</option>`,
);
......@@ -378,10 +372,7 @@ describe('CustomStageForm', () => {
describe('with all fields set', () => {
const startEventIndex = 2;
const firstStopEventIdentifier = startEvents[startEventIndex].allowedEndEvents[0];
const stopEventIndex = stopEvents.findIndex(
ev => ev.identifier === firstStopEventIdentifier,
);
const stopEventIndex = 1;
beforeEach(() => {
wrapper = createComponent({}, false);
......@@ -389,7 +380,7 @@ describe('CustomStageForm', () => {
selectDropdownOption(wrapper, sel.startEvent, startEventIndex);
return Vue.nextTick(() => {
selectDropdownOption(wrapper, sel.stopEvent, 1);
selectDropdownOption(wrapper, sel.stopEvent, stopEventIndex);
wrapper.find(sel.name).setValue('Cool stage');
});
});
......@@ -408,13 +399,15 @@ describe('CustomStageForm', () => {
it('`submit` event receives the latest data', () => {
expect(wrapper.emitted().submit).toBeUndefined();
const startEv = startEvents[startEventIndex];
const selectedStopEvent = getDropdownOption(wrapper, sel.stopEvent, stopEventIndex);
const res = [
{
name: 'Cool stage',
startEvent: startEvents[startEventIndex].identifier,
startEvent: startEv.identifier,
startEventLabel: null,
stopEvent: stopEvents[stopEventIndex].identifier,
stopEvent: selectedStopEvent.attributes('value'),
stopEventLabel: null,
},
];
......
......@@ -65,7 +65,13 @@ const { events: rawCustomStageEvents } = getJSONFixture('analytics/cycle_analyti
const camelCasedStageEvents = rawCustomStageEvents.map(deepCamelCase);
export const customStageStartEvents = camelCasedStageEvents.filter(ev => ev.canBeStartEvent);
export const customStageStopEvents = camelCasedStageEvents.filter(ev => !ev.canBeStartEvent);
// find get all the possible stop events
const allowedEndEventIds = new Set(customStageStartEvents.flatMap(e => e.allowedEndEvents));
export const customStageStopEvents = camelCasedStageEvents.filter(ev =>
allowedEndEventIds.has(ev.identifier),
);
// TODO: the shim below should be removed once we have label events seeding
export const labelStartEvent = { ...customStageStartEvents[0], type: 'label' };
......
......@@ -11,7 +11,6 @@ import {
labelStartEvent,
labelStopEvent,
customStageStartEvents as startEvents,
customStageStopEvents as stopEvents,
} from './mock_data';
const labelEvents = [labelStartEvent, labelStopEvent].map(i => i.identifier);
......@@ -23,9 +22,11 @@ describe('Cycle analytics utils', () => {
});
it('will return false for input that is not a start event', () => {
[stopEvents[0], {}, [], null, undefined].forEach(ev => {
[{ identifier: 'fake-event', canBeStartEvent: false }, {}, [], null, undefined].forEach(
ev => {
expect(isStartEvent(ev)).toEqual(false);
});
},
);
});
});
......
......@@ -62,7 +62,7 @@ describe Gitlab::Analytics::CycleAnalytics::DataCollector do
shared_examples 'test various start and end event combinations' do
context 'when `Issue` based stage is given' do
context 'between issue creation time and closing time' do
context 'between issue creation time and issue first mentioned in commit time' do
let(:start_event_identifier) { :issue_created }
let(:end_event_identifier) { :issue_first_mentioned_in_commit }
......@@ -76,6 +76,68 @@ describe Gitlab::Analytics::CycleAnalytics::DataCollector do
it_behaves_like 'custom cycle analytics stage'
end
describe 'between issue creation time and closing time' do
let(:start_event_identifier) { :issue_created }
let(:end_event_identifier) { :issue_closed }
def create_data_for_start_event(example_class)
create(:issue, :opened, project: example_class.project)
end
def create_data_for_end_event(resource, example_class)
resource.close!
end
it_behaves_like 'custom cycle analytics stage'
end
describe 'between issue first mentioned in commit and first associated with milestone time' do
let(:start_event_identifier) { :issue_first_mentioned_in_commit }
let(:end_event_identifier) { :issue_first_associated_with_milestone }
def create_data_for_start_event(example_class)
issue = create(:issue, :opened, project: example_class.project)
issue.metrics.update!(first_mentioned_in_commit_at: Time.now)
issue
end
def create_data_for_end_event(resource, example_class)
resource.metrics.update!(first_associated_with_milestone_at: Time.now)
end
it_behaves_like 'custom cycle analytics stage'
end
describe 'between issue creation time and first added to board time' do
let(:start_event_identifier) { :issue_created }
let(:end_event_identifier) { :issue_first_added_to_board }
def create_data_for_start_event(example_class)
create(:issue, :opened, project: example_class.project)
end
def create_data_for_end_event(resource, example_class)
resource.metrics.update!(first_added_to_board_at: Time.now)
end
it_behaves_like 'custom cycle analytics stage'
end
describe 'between issue creation time and last edit time' do
let(:start_event_identifier) { :issue_created }
let(:end_event_identifier) { :issue_last_edited }
def create_data_for_start_event(example_class)
create(:issue, :opened, project: example_class.project)
end
def create_data_for_end_event(resource, example_class)
resource.update!(last_edited_at: Time.now)
end
it_behaves_like 'custom cycle analytics stage'
end
end
context 'when `MergeRequest` based stage is given' do
......@@ -127,6 +189,36 @@ describe Gitlab::Analytics::CycleAnalytics::DataCollector do
it_behaves_like 'custom cycle analytics stage'
end
describe 'between merge request creation time and close time' do
let(:start_event_identifier) { :merge_request_created }
let(:end_event_identifier) { :merge_request_closed }
def create_data_for_start_event(example_class)
create(:merge_request, source_project: example_class.project, allow_broken: true)
end
def create_data_for_end_event(resource, example_class)
resource.metrics.update!(latest_closed_at: Time.now)
end
it_behaves_like 'custom cycle analytics stage'
end
describe 'between merge request creation time and last edit time' do
let(:start_event_identifier) { :merge_request_created }
let(:end_event_identifier) { :merge_request_last_edited }
def create_data_for_start_event(example_class)
create(:merge_request, source_project: example_class.project, allow_broken: true)
end
def create_data_for_end_event(resource, example_class)
resource.update!(last_edited_at: Time.now)
end
it_behaves_like 'custom cycle analytics stage'
end
end
end
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Analytics::CycleAnalytics::StageEvents::IssueClosed do
it_behaves_like 'cycle analytics event'
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstAddedToBoard do
it_behaves_like 'cycle analytics event'
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstAssociatedWithMilestone do
it_behaves_like 'cycle analytics event'
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Analytics::CycleAnalytics::StageEvents::IssueFirstMentionedInCommit do
it_behaves_like 'cycle analytics event'
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Analytics::CycleAnalytics::StageEvents::IssueLastEdited do
it_behaves_like 'cycle analytics event'
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestClosed do
it_behaves_like 'cycle analytics event'
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestLastEdited do
it_behaves_like 'cycle analytics event'
end
......@@ -47,27 +47,29 @@ module Gitlab
]
}.freeze
def [](identifier)
def self.[](identifier)
events.find { |e| e.identifier.to_s.eql?(identifier.to_s) } || raise(KeyError)
end
# hash for defining ActiveRecord enum: identifier => number
def to_enum
ENUM_MAPPING.each_with_object({}) { |(k, v), hash| hash[k.identifier] = v }
def self.to_enum
enum_mapping.each_with_object({}) { |(k, v), hash| hash[k.identifier] = v }
end
# will be overridden in EE with custom events
def pairing_rules
def self.pairing_rules
PAIRING_RULES
end
# will be overridden in EE with custom events
def events
def self.events
EVENTS
end
module_function :[], :to_enum, :pairing_rules, :events
def self.enum_mapping
ENUM_MAPPING
end
end
end
end
end
Gitlab::Analytics::CycleAnalytics::StageEvents.prepend_if_ee('::EE::Gitlab::Analytics::CycleAnalytics::StageEvents')
......@@ -4,7 +4,7 @@ module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class CodeStageStart < SimpleStageEvent
class CodeStageStart < StageEvent
def self.name
s_("CycleAnalyticsEvent|Issue first mentioned in a commit")
end
......
......@@ -4,7 +4,7 @@ module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class IssueCreated < SimpleStageEvent
class IssueCreated < StageEvent
def self.name
s_("CycleAnalyticsEvent|Issue created")
end
......
......@@ -4,7 +4,7 @@ module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class IssueFirstMentionedInCommit < SimpleStageEvent
class IssueFirstMentionedInCommit < MetricsBasedStageEvent
def self.name
s_("CycleAnalyticsEvent|Issue first mentioned in a commit")
end
......@@ -20,12 +20,6 @@ module Gitlab
def timestamp_projection
issue_metrics_table[:first_mentioned_in_commit_at]
end
# rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query)
query.joins(:metrics)
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
......
......@@ -4,7 +4,7 @@ module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class IssueStageEnd < SimpleStageEvent
class IssueStageEnd < MetricsBasedStageEvent
def self.name
PlanStageStart.name
end
......@@ -26,7 +26,7 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query)
query.joins(:metrics).where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil)))
super.where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil)))
end
# rubocop: enable CodeReuse/ActiveRecord
end
......
......@@ -4,7 +4,7 @@ module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class MergeRequestCreated < SimpleStageEvent
class MergeRequestCreated < StageEvent
def self.name
s_("CycleAnalyticsEvent|Merge request created")
end
......
......@@ -4,7 +4,7 @@ module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class MergeRequestFirstDeployedToProduction < SimpleStageEvent
class MergeRequestFirstDeployedToProduction < MetricsBasedStageEvent
def self.name
s_("CycleAnalyticsEvent|Merge request first deployed to production")
end
......@@ -23,7 +23,7 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query)
query.joins(:metrics).where(timestamp_projection.gteq(mr_table[:created_at]))
super.where(timestamp_projection.gteq(mr_table[:created_at]))
end
# rubocop: enable CodeReuse/ActiveRecord
end
......
......@@ -4,7 +4,7 @@ module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class MergeRequestLastBuildFinished < SimpleStageEvent
class MergeRequestLastBuildFinished < MetricsBasedStageEvent
def self.name
s_("CycleAnalyticsEvent|Merge request last build finish time")
end
......@@ -20,12 +20,6 @@ module Gitlab
def timestamp_projection
mr_metrics_table[:latest_build_finished_at]
end
# rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query)
query.joins(:metrics)
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
......
......@@ -4,7 +4,7 @@ module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class MergeRequestLastBuildStarted < SimpleStageEvent
class MergeRequestLastBuildStarted < MetricsBasedStageEvent
def self.name
s_("CycleAnalyticsEvent|Merge request last build start time")
end
......@@ -20,12 +20,6 @@ module Gitlab
def timestamp_projection
mr_metrics_table[:latest_build_started_at]
end
# rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query)
query.joins(:metrics)
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
......
......@@ -4,7 +4,7 @@ module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class MergeRequestMerged < SimpleStageEvent
class MergeRequestMerged < MetricsBasedStageEvent
def self.name
s_("CycleAnalyticsEvent|Merge request merged")
end
......@@ -20,12 +20,6 @@ module Gitlab
def timestamp_projection
mr_metrics_table[:merged_at]
end
# rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query)
query.joins(:metrics)
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
......
......@@ -4,8 +4,12 @@ module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
# Represents a simple event that usually refers to one database column and does not require additional user input
class SimpleStageEvent < StageEvent
class MetricsBasedStageEvent < StageEvent
# rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query)
query.joins(:metrics)
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
......
......@@ -4,7 +4,7 @@ module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class PlanStageStart < SimpleStageEvent
class PlanStageStart < MetricsBasedStageEvent
def self.name
s_("CycleAnalyticsEvent|Issue first associated with a milestone or issue first added to a board")
end
......@@ -26,8 +26,7 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query)
query
.joins(:metrics)
super
.where(issue_metrics_table[:first_added_to_board_at].not_eq(nil).or(issue_metrics_table[:first_associated_with_milestone_at].not_eq(nil)))
.where(issue_metrics_table[:first_mentioned_in_commit_at].not_eq(nil))
end
......
......@@ -4,7 +4,7 @@ module Gitlab
module Analytics
module CycleAnalytics
module StageEvents
class ProductionStageEnd < SimpleStageEvent
class ProductionStageEnd < StageEvent
def self.name
PlanStageStart.name
end
......
......@@ -4995,15 +4995,30 @@ msgstr ""
msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
msgstr ""
msgid "CycleAnalyticsEvent|Issue closed"
msgstr ""
msgid "CycleAnalyticsEvent|Issue created"
msgstr ""
msgid "CycleAnalyticsEvent|Issue first added to a board"
msgstr ""
msgid "CycleAnalyticsEvent|Issue first associated with a milestone"
msgstr ""
msgid "CycleAnalyticsEvent|Issue first associated with a milestone or issue first added to a board"
msgstr ""
msgid "CycleAnalyticsEvent|Issue first mentioned in a commit"
msgstr ""
msgid "CycleAnalyticsEvent|Issue last edited"
msgstr ""
msgid "CycleAnalyticsEvent|Merge request closed"
msgstr ""
msgid "CycleAnalyticsEvent|Merge request created"
msgstr ""
......@@ -5016,6 +5031,9 @@ msgstr ""
msgid "CycleAnalyticsEvent|Merge request last build start time"
msgstr ""
msgid "CycleAnalyticsEvent|Merge request last edited"
msgstr ""
msgid "CycleAnalyticsEvent|Merge request merged"
msgstr ""
......
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