Commit 0d02d9c7 authored by Heinrich Lee Yu's avatar Heinrich Lee Yu Committed by Mayra Cabrera

Track issue health status changes in usage ping

Adds health status change events to the events we track for issues
parent 654f971a
......@@ -15,6 +15,8 @@ module EE
health_status = noteable.health_status&.humanize(capitalize: false)
body = health_status ? "changed health status to **#{health_status}**" : 'removed the health status'
issue_activity_counter.track_issue_health_status_changed_action(author: author) if noteable.is_a?(Issue)
create_note(NoteSummary.new(noteable, project, author, body, action: 'health_status'))
end
......
---
title: Track issue health status changes in usage ping
merge_request: 44417
author:
type: other
# frozen_string_literal: true
module EE
module Gitlab
module UsageDataCounters
module IssueActivityUniqueCounter
extend ActiveSupport::Concern
ISSUE_HEALTH_STATUS_CHANGED = 'g_project_management_issue_health_status_changed'
class_methods do
def track_issue_health_status_changed_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_HEALTH_STATUS_CHANGED, author, time)
end
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_gitlab_redis_shared_state do
let(:user1) { build(:user, id: 1) }
let(:user2) { build(:user, id: 2) }
let(:user3) { build(:user, id: 3) }
let(:time) { Time.zone.now }
context 'for Issue health status changed actions' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_HEALTH_STATUS_CHANGED }
def track_action(params)
described_class.track_issue_health_status_changed_action(**params)
end
end
end
end
......@@ -14,11 +14,11 @@ RSpec.describe ::SystemNotes::IssuablesService do
let(:service) { described_class.new(noteable: noteable, project: project, author: author) }
describe '#change_health_status_note' do
subject { service.change_health_status_note }
context 'when health_status changed' do
let(:noteable) { create(:issue, project: project, title: 'Lorem ipsum', health_status: 'at_risk') }
subject { service.change_health_status_note }
it_behaves_like 'a system note' do
let(:action) { 'health_status' }
end
......@@ -31,8 +31,6 @@ RSpec.describe ::SystemNotes::IssuablesService do
context 'when health_status removed' do
let(:noteable) { create(:issue, project: project, title: 'Lorem ipsum', health_status: nil) }
subject { service.change_health_status_note }
it_behaves_like 'a system note' do
let(:action) { 'health_status' }
end
......@@ -41,6 +39,12 @@ RSpec.describe ::SystemNotes::IssuablesService do
expect(subject.note).to eq 'removed the health status'
end
end
it 'tracks the issue event in usage ping' do
expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_health_status_changed_action).with(author: author)
subject
end
end
describe '#publish_issue_to_status_page' do
......
......@@ -174,3 +174,5 @@ module Gitlab
end
end
end
Gitlab::UsageDataCounters::IssueActivityUniqueCounter.prepend_if_ee('EE::Gitlab::UsageDataCounters::IssueActivityUniqueCounter')
......@@ -315,3 +315,7 @@
category: issues_edit
redis_slot: project_management
aggregation: daily
- name: g_project_management_issue_health_status_changed
category: issues_edit
redis_slot: project_management
aggregation: daily
......@@ -88,7 +88,7 @@ RSpec.describe Gitlab::ExclusiveLeaseHelpers, :clean_gitlab_redis_shared_state d
let(:options) { { retries: 0 } }
it 'never sleeps' do
expect(class_instance).not_to receive(:sleep)
expect_any_instance_of(Gitlab::ExclusiveLeaseHelpers::SleepingLock).not_to receive(:sleep)
expect { subject }.to raise_error('Failed to obtain a lock')
end
......@@ -98,7 +98,7 @@ RSpec.describe Gitlab::ExclusiveLeaseHelpers, :clean_gitlab_redis_shared_state d
let(:options) { { retries: 1, sleep_sec: 0.05.seconds } }
it 'receives the specified argument' do
expect_any_instance_of(Object).to receive(:sleep).with(0.05.seconds).once
expect_any_instance_of(Gitlab::ExclusiveLeaseHelpers::SleepingLock).to receive(:sleep).with(0.05.seconds).once
expect { subject }.to raise_error('Failed to obtain a lock')
end
......@@ -108,8 +108,8 @@ RSpec.describe Gitlab::ExclusiveLeaseHelpers, :clean_gitlab_redis_shared_state d
let(:options) { { retries: 2, sleep_sec: ->(num) { 0.1 + num } } }
it 'receives the specified argument' do
expect_any_instance_of(Object).to receive(:sleep).with(1.1.seconds).once
expect_any_instance_of(Object).to receive(:sleep).with(2.1.seconds).once
expect_any_instance_of(Gitlab::ExclusiveLeaseHelpers::SleepingLock).to receive(:sleep).with(1.1.seconds).once
expect_any_instance_of(Gitlab::ExclusiveLeaseHelpers::SleepingLock).to receive(:sleep).with(2.1.seconds).once
expect { subject }.to raise_error('Failed to obtain a lock')
end
......
......@@ -59,7 +59,7 @@ RSpec.describe Gitlab::ImportExport::AttributesFinder do
end
before do
allow_any_instance_of(Gitlab::ImportExport).to receive(:config_file).and_return(test_config)
allow(Gitlab::ImportExport).to receive(:config_file).and_return(test_config)
end
it 'generates hash from project tree config' do
......
......@@ -12,7 +12,7 @@ RSpec.describe Gitlab::ImportExport::Group::LegacyTreeSaver do
before do
group.add_maintainer(user)
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
allow(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
end
after do
......
......@@ -12,7 +12,7 @@ RSpec.describe Gitlab::ImportExport::Importer do
subject(:importer) { described_class.new(project) }
before do
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(test_path)
allow(Gitlab::ImportExport).to receive(:storage_path).and_return(test_path)
allow_any_instance_of(Gitlab::ImportExport::FileImporter).to receive(:remove_import_file)
stub_uploads_object_storage(FileUploader)
......
......@@ -13,7 +13,7 @@ RSpec.describe Gitlab::ImportExport::LfsRestorer do
subject(:restorer) { described_class.new(project: project, shared: shared) }
before do
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
allow(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
FileUtils.mkdir_p(shared.export_path)
end
......
......@@ -8,42 +8,8 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
let(:user3) { build(:user, id: 3) }
let(:time) { Time.zone.now }
shared_examples 'tracks and counts action' do
before do
stub_application_setting(usage_ping_enabled: true)
end
def count_unique(date_from:, date_to:)
Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: action, start_date: date_from, end_date: date_to)
end
specify do
aggregate_failures do
expect(track_action(author: user1)).to be_truthy
expect(track_action(author: user1)).to be_truthy
expect(track_action(author: user2)).to be_truthy
expect(track_action(author: user3, time: time - 3.days)).to be_truthy
expect(count_unique(date_from: time, date_to: time)).to eq(2)
expect(count_unique(date_from: time - 5.days, date_to: 1.day.since(time))).to eq(3)
end
end
it 'does not track edit actions if author is not present' do
expect(track_action(author: nil)).to be_nil
end
context 'when feature flag track_issue_activity_actions is disabled' do
it 'does not track edit actions' do
stub_feature_flags(track_issue_activity_actions: false)
expect(track_action(author: user1)).to be_nil
end
end
end
context 'for Issue title edit actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_TITLE_CHANGED }
def track_action(params)
......@@ -53,7 +19,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue description edit actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_DESCRIPTION_CHANGED }
def track_action(params)
......@@ -63,7 +29,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue assignee edit actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_ASSIGNEE_CHANGED }
def track_action(params)
......@@ -73,7 +39,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue make confidential actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_MADE_CONFIDENTIAL }
def track_action(params)
......@@ -83,7 +49,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue make visible actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_MADE_VISIBLE }
def track_action(params)
......@@ -93,7 +59,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue created actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_CREATED }
def track_action(params)
......@@ -103,7 +69,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue closed actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_CLOSED }
def track_action(params)
......@@ -113,7 +79,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue reopened actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_REOPENED }
def track_action(params)
......@@ -123,7 +89,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue label changed actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_LABEL_CHANGED }
def track_action(params)
......@@ -133,7 +99,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue cross-referenced actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_CROSS_REFERENCED }
def track_action(params)
......@@ -143,7 +109,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue moved actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_MOVED }
def track_action(params)
......@@ -153,7 +119,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue relate actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_RELATED }
def track_action(params)
......@@ -163,7 +129,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue unrelate actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_UNRELATED }
def track_action(params)
......@@ -173,7 +139,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue marked as duplicate actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_MARKED_AS_DUPLICATE }
def track_action(params)
......@@ -183,7 +149,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue locked actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_LOCKED }
def track_action(params)
......@@ -193,7 +159,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue unlocked actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_UNLOCKED }
def track_action(params)
......@@ -203,7 +169,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue added to epic actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_ADDED_TO_EPIC}
def track_action(params)
......@@ -213,7 +179,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue removed from epic actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_REMOVED_FROM_EPIC}
def track_action(params)
......@@ -223,7 +189,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue changed epic actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_CHANGED_EPIC}
def track_action(params)
......@@ -233,7 +199,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue designs added actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_DESIGNS_ADDED }
def track_action(params)
......@@ -243,7 +209,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue designs modified actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_DESIGNS_MODIFIED }
def track_action(params)
......@@ -253,7 +219,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue designs removed actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_DESIGNS_REMOVED }
def track_action(params)
......@@ -263,7 +229,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue due date changed actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_DUE_DATE_CHANGED }
def track_action(params)
......@@ -273,7 +239,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue time estimate changed actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_TIME_ESTIMATE_CHANGED }
def track_action(params)
......@@ -283,7 +249,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue time spent changed actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_TIME_SPENT_CHANGED }
def track_action(params)
......@@ -293,7 +259,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue comment added actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_COMMENT_ADDED }
def track_action(params)
......@@ -303,7 +269,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue comment edited actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_COMMENT_EDITED }
def track_action(params)
......@@ -313,7 +279,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue comment removed actions' do
it_behaves_like 'tracks and counts action' do
it_behaves_like 'a tracked issue edit event' do
let(:action) { described_class::ISSUE_COMMENT_REMOVED }
def track_action(params)
......
......@@ -26,7 +26,7 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache do
let(:export_path) { "#{Dir.tmpdir}/project_export_spec" }
before do
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
allow(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
allow_next_instance_of(ProjectExportWorker) do |job|
allow(job).to receive(:jid).and_return(SecureRandom.hex(8))
end
......
......@@ -15,7 +15,7 @@ module ImportExport
export_path = [prefix, 'spec', 'fixtures', 'lib', 'gitlab', 'import_export', name].compact
export_path = File.join(*export_path)
allow_any_instance_of(Gitlab::ImportExport).to receive(:export_path) { export_path }
allow(Gitlab::ImportExport).to receive(:export_path) { export_path }
end
def setup_reader(reader)
......
# frozen_string_literal: true
# This patch allows stubbing of prepended methods
# Based on https://github.com/rspec/rspec-mocks/pull/1218
module RSpec
module Mocks
module InstanceMethodStasherForPrependedMethods
private
def method_owned_by_klass?
owner = @klass.instance_method(@method).owner
owner = owner.class unless Module === owner
owner == @klass ||
# When `extend self` is used, and not under any instance of
(owner.singleton_class == @klass && !Mocks.space.any_instance_recorder_for(owner, true)) ||
!method_defined_on_klass?(owner)
end
end
end
end
module RSpec
module Mocks
module MethodDoubleForPrependedMethods
def restore_original_method
return show_frozen_warning if object_singleton_class.frozen?
return unless @method_is_proxied
remove_method_from_definition_target
if @method_stasher.method_is_stashed?
@method_stasher.restore
restore_original_visibility
end
@method_is_proxied = false
end
def restore_original_visibility
method_owner.__send__(@original_visibility, @method_name)
end
private
def method_owner
@method_owner ||= Object.instance_method(:method).bind(object).call(@method_name).owner
end
end
end
end
RSpec::Mocks::InstanceMethodStasher.prepend(RSpec::Mocks::InstanceMethodStasherForPrependedMethods)
RSpec::Mocks::MethodDouble.prepend(RSpec::Mocks::MethodDoubleForPrependedMethods)
# frozen_string_literal: true
RSpec.shared_examples 'a tracked issue edit event' do |event|
before do
stub_application_setting(usage_ping_enabled: true)
end
def count_unique(date_from:, date_to:)
Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: action, start_date: date_from, end_date: date_to)
end
specify do
aggregate_failures do
expect(track_action(author: user1)).to be_truthy
expect(track_action(author: user1)).to be_truthy
expect(track_action(author: user2)).to be_truthy
expect(track_action(author: user3, time: time - 3.days)).to be_truthy
expect(count_unique(date_from: time, date_to: time)).to eq(2)
expect(count_unique(date_from: time - 5.days, date_to: 1.day.since(time))).to eq(3)
end
end
it 'does not track edit actions if author is not present' do
expect(track_action(author: nil)).to be_nil
end
context 'when feature flag track_issue_activity_actions is disabled' do
it 'does not track edit actions' do
stub_feature_flags(track_issue_activity_actions: false)
expect(track_action(author: user1)).to be_nil
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