Commit 18b29966 authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch 'track-compliance-page-vists' into 'master'

Track unique visitors in Compliance related pages

See merge request gitlab-org/gitlab!38851
parents a784aa2e 06def93b
...@@ -4,9 +4,12 @@ class Admin::AuditLogsController < Admin::ApplicationController ...@@ -4,9 +4,12 @@ class Admin::AuditLogsController < Admin::ApplicationController
include AuditEvents::EnforcesValidDateParams include AuditEvents::EnforcesValidDateParams
include AuditEvents::AuditLogsParams include AuditEvents::AuditLogsParams
include AuditEvents::Sortable include AuditEvents::Sortable
include Analytics::UniqueVisitsHelper
before_action :check_license_admin_audit_log_available! before_action :check_license_admin_audit_log_available!
track_unique_visits :index, target_id: 'i_compliance_audit_events'
PER_PAGE = 25 PER_PAGE = 25
def index def index
......
...@@ -3,11 +3,14 @@ ...@@ -3,11 +3,14 @@
class Admin::CredentialsController < Admin::ApplicationController class Admin::CredentialsController < Admin::ApplicationController
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
include CredentialsInventoryActions include CredentialsInventoryActions
include Analytics::UniqueVisitsHelper
helper_method :credentials_inventory_path, :user_detail_path helper_method :credentials_inventory_path, :user_detail_path
before_action :check_license_credentials_inventory_available!, only: [:index] before_action :check_license_credentials_inventory_available!, only: [:index]
track_unique_visits :index, target_id: 'i_compliance_credential_inventory'
private private
def check_license_credentials_inventory_available! def check_license_credentials_inventory_available!
......
...@@ -7,8 +7,12 @@ module CredentialsInventoryActions ...@@ -7,8 +7,12 @@ module CredentialsInventoryActions
def index def index
@credentials = filter_credentials.page(params[:page]).preload_users.without_count # rubocop:disable Gitlab/ModuleWithInstanceVariables @credentials = filter_credentials.page(params[:page]).preload_users.without_count # rubocop:disable Gitlab/ModuleWithInstanceVariables
respond_to do |format|
format.html do
render 'shared/credentials_inventory/index' render 'shared/credentials_inventory/index'
end end
end
end
private private
......
...@@ -4,10 +4,13 @@ class Groups::AuditEventsController < Groups::ApplicationController ...@@ -4,10 +4,13 @@ class Groups::AuditEventsController < Groups::ApplicationController
include AuditEvents::EnforcesValidDateParams include AuditEvents::EnforcesValidDateParams
include AuditEvents::AuditLogsParams include AuditEvents::AuditLogsParams
include AuditEvents::Sortable include AuditEvents::Sortable
include Analytics::UniqueVisitsHelper
before_action :authorize_admin_group! before_action :authorize_admin_group!
before_action :check_audit_events_available! before_action :check_audit_events_available!
track_unique_visits :index, target_id: 'g_compliance_audit_events'
layout 'group_settings' layout 'group_settings'
def index def index
......
# frozen_string_literal: true # frozen_string_literal: true
class Groups::Security::ComplianceDashboardsController < Groups::ApplicationController class Groups::Security::ComplianceDashboardsController < Groups::ApplicationController
include Groups::SecurityFeaturesHelper include Groups::SecurityFeaturesHelper
include Analytics::UniqueVisitsHelper
layout 'group' layout 'group'
before_action :authorize_compliance_dashboard! before_action :authorize_compliance_dashboard!
track_unique_visits :show, target_id: 'g_compliance_dashboard'
def show def show
@last_page = paginated_merge_requests.last_page? @last_page = paginated_merge_requests.last_page?
@merge_requests = serialize(paginated_merge_requests) @merge_requests = serialize(paginated_merge_requests)
......
---
title: Track unique visitors in Compliance related pages
merge_request: 38851
author:
type: added
...@@ -24,6 +24,11 @@ RSpec.describe Admin::AuditLogsController do ...@@ -24,6 +24,11 @@ RSpec.describe Admin::AuditLogsController do
expect(assigns(:events)).to be_kind_of(Kaminari::PaginatableWithoutCount) expect(assigns(:events)).to be_kind_of(Kaminari::PaginatableWithoutCount)
end end
end end
it_behaves_like 'tracking unique visits', :index do
let(:request_params) { { 'entity_type': 'User' } }
let(:target_id) { 'i_compliance_audit_events' }
end
end end
end end
end end
...@@ -20,6 +20,11 @@ RSpec.describe Admin::CredentialsController do ...@@ -20,6 +20,11 @@ RSpec.describe Admin::CredentialsController do
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
end end
it_behaves_like 'tracking unique visits', :index do
let(:request_params) { {} }
let(:target_id) { 'i_compliance_credential_inventory' }
end
describe 'filtering by type of credential' do describe 'filtering by type of credential' do
let_it_be(:personal_access_tokens) { create_list(:personal_access_token, 2) } let_it_be(:personal_access_tokens) { create_list(:personal_access_token, 2) }
......
...@@ -12,16 +12,17 @@ RSpec.describe Groups::AuditEventsController do ...@@ -12,16 +12,17 @@ RSpec.describe Groups::AuditEventsController do
let(:entity_type) { nil } let(:entity_type) { nil }
let(:entity_id) { nil } let(:entity_id) { nil }
let(:request) do
get :index, params: { group_id: group.to_param, sort: sort, entity_type: entity_type, entity_id: entity_id }
end
context 'authorized' do context 'authorized' do
before do before do
group.add_owner(owner) group.add_owner(owner)
sign_in(owner) sign_in(owner)
end end
context do
let(:request) do
get :index, params: { group_id: group.to_param, sort: sort, entity_type: entity_type, entity_id: entity_id }
end
context 'when audit_events feature is available' do context 'when audit_events feature is available' do
let(:level) { Gitlab::Audit::Levels::Group.new(group: group) } let(:level) { Gitlab::Audit::Levels::Group.new(group: group) }
let(:audit_logs_params) { ActionController::Parameters.new(sort: '', entity_type: '', entity_id: '').permit! } let(:audit_logs_params) { ActionController::Parameters.new(sort: '', entity_type: '', entity_id: '').permit! }
...@@ -118,7 +119,17 @@ RSpec.describe Groups::AuditEventsController do ...@@ -118,7 +119,17 @@ RSpec.describe Groups::AuditEventsController do
end end
end end
it_behaves_like 'tracking unique visits', :index do
let(:request_params) { { group_id: group.to_param, sort: sort, entity_type: entity_type, entity_id: entity_id } }
let(:target_id) { 'g_compliance_audit_events' }
end
end
context 'unauthorized' do context 'unauthorized' do
let(:request) do
get :index, params: { group_id: group.to_param, sort: sort, entity_type: entity_type, entity_id: entity_id }
end
before do before do
stub_licensed_features(audit_events: true) stub_licensed_features(audit_events: true)
sign_in(user) sign_in(user)
......
...@@ -47,6 +47,11 @@ RSpec.describe Groups::Security::ComplianceDashboardsController do ...@@ -47,6 +47,11 @@ RSpec.describe Groups::Security::ComplianceDashboardsController do
expect(assigns(:merge_requests)).not_to be_empty expect(assigns(:merge_requests)).not_to be_empty
end end
end end
it_behaves_like 'tracking unique visits', :show do
let(:request_params) { { group_id: group.to_param } }
let(:target_id) { 'g_compliance_dashboard' }
end
end end
context 'when user is not allowed to access group compliance dashboard' do context 'when user is not allowed to access group compliance dashboard' do
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module Gitlab module Gitlab
module Analytics module Analytics
class UniqueVisits class UniqueVisits
TARGET_IDS = Set[ ANALYTICS_IDS = Set[
'g_analytics_contribution', 'g_analytics_contribution',
'g_analytics_insights', 'g_analytics_insights',
'g_analytics_issues', 'g_analytics_issues',
...@@ -17,6 +17,13 @@ module Gitlab ...@@ -17,6 +17,13 @@ module Gitlab
'p_analytics_repo', 'p_analytics_repo',
'i_analytics_cohorts', 'i_analytics_cohorts',
'i_analytics_dev_ops_score' 'i_analytics_dev_ops_score'
]
COMPLIANCE_IDS = Set[
'g_compliance_dashboard',
'g_compliance_audit_events',
'i_compliance_credential_inventory',
'i_compliance_audit_events'
].freeze ].freeze
KEY_EXPIRY_LENGTH = 12.weeks KEY_EXPIRY_LENGTH = 12.weeks
...@@ -34,8 +41,10 @@ module Gitlab ...@@ -34,8 +41,10 @@ module Gitlab
# @param [Integer] weeks time frame length in weeks # @param [Integer] weeks time frame length in weeks
# @return [Integer] number of unique visitors # @return [Integer] number of unique visitors
def unique_visits_for(targets:, start_week: 7.days.ago, weeks: 1) def unique_visits_for(targets:, start_week: 7.days.ago, weeks: 1)
target_ids = if targets == :any target_ids = if targets == :analytics
TARGET_IDS ANALYTICS_IDS
elsif targets == :compliance
COMPLIANCE_IDS
else else
Array(targets) Array(targets)
end end
...@@ -50,9 +59,12 @@ module Gitlab ...@@ -50,9 +59,12 @@ module Gitlab
private private
def key(target_id, time) def key(target_id, time)
raise "Invalid target id #{target_id}" unless TARGET_IDS.include?(target_id.to_s) target_ids = ANALYTICS_IDS + COMPLIANCE_IDS
raise "Invalid target id #{target_id}" unless target_ids.include?(target_id.to_s)
target_key = target_id.to_s.gsub('analytics', '{analytics}').gsub('compliance', '{compliance}')
target_key = target_id.to_s.gsub('analytics', '{analytics}')
year_week = time.strftime('%G-%V') year_week = time.strftime('%G-%V')
"#{target_key}-#{year_week}" "#{target_key}-#{year_week}"
......
...@@ -37,6 +37,7 @@ module Gitlab ...@@ -37,6 +37,7 @@ module Gitlab
.merge(usage_activity_by_stage) .merge(usage_activity_by_stage)
.merge(usage_activity_by_stage(:usage_activity_by_stage_monthly, last_28_days_time_period)) .merge(usage_activity_by_stage(:usage_activity_by_stage_monthly, last_28_days_time_period))
.merge(analytics_unique_visits_data) .merge(analytics_unique_visits_data)
.merge(compliance_unique_visits_data)
end end
end end
...@@ -581,15 +582,25 @@ module Gitlab ...@@ -581,15 +582,25 @@ module Gitlab
end end
def analytics_unique_visits_data def analytics_unique_visits_data
results = ::Gitlab::Analytics::UniqueVisits::TARGET_IDS.each_with_object({}) do |target_id, hash| results = ::Gitlab::Analytics::UniqueVisits::ANALYTICS_IDS.each_with_object({}) do |target_id, hash|
hash[target_id] = redis_usage_data { unique_visit_service.unique_visits_for(targets: target_id) } hash[target_id] = redis_usage_data { unique_visit_service.unique_visits_for(targets: target_id) }
end end
results['analytics_unique_visits_for_any_target'] = redis_usage_data { unique_visit_service.unique_visits_for(targets: :any) } results['analytics_unique_visits_for_any_target'] = redis_usage_data { unique_visit_service.unique_visits_for(targets: :analytics) }
results['analytics_unique_visits_for_any_target_monthly'] = redis_usage_data { unique_visit_service.unique_visits_for(targets: :any, weeks: 4) } results['analytics_unique_visits_for_any_target_monthly'] = redis_usage_data { unique_visit_service.unique_visits_for(targets: :analytics, weeks: 4) }
{ analytics_unique_visits: results } { analytics_unique_visits: results }
end end
def compliance_unique_visits_data
results = ::Gitlab::Analytics::UniqueVisits::COMPLIANCE_IDS.each_with_object({}) do |target_id, hash|
hash[target_id] = redis_usage_data { unique_visit_service.unique_visits_for(targets: target_id) }
end
results['compliance_unique_visits_for_any_target'] = redis_usage_data { unique_visit_service.unique_visits_for(targets: :compliance) }
results['compliance_unique_visits_for_any_target_monthly'] = redis_usage_data { unique_visit_service.unique_visits_for(targets: :compliance, weeks: 4) }
{ compliance_unique_visits: results }
end
def action_monthly_active_users(time_period) def action_monthly_active_users(time_period)
return {} unless Feature.enabled?(Gitlab::UsageDataCounters::TrackUniqueActions::FEATURE_FLAG) return {} unless Feature.enabled?(Gitlab::UsageDataCounters::TrackUniqueActions::FEATURE_FLAG)
......
...@@ -7,8 +7,11 @@ RSpec.describe Gitlab::Analytics::UniqueVisits, :clean_gitlab_redis_shared_state ...@@ -7,8 +7,11 @@ RSpec.describe Gitlab::Analytics::UniqueVisits, :clean_gitlab_redis_shared_state
let(:target1_id) { 'g_analytics_contribution' } let(:target1_id) { 'g_analytics_contribution' }
let(:target2_id) { 'g_analytics_insights' } let(:target2_id) { 'g_analytics_insights' }
let(:target3_id) { 'g_analytics_issues' } let(:target3_id) { 'g_analytics_issues' }
let(:target4_id) { 'g_compliance_dashboard' }
let(:target5_id) { 'i_compliance_credential_inventory' }
let(:visitor1_id) { 'dfb9d2d2-f56c-4c77-8aeb-6cddc4a1f857' } let(:visitor1_id) { 'dfb9d2d2-f56c-4c77-8aeb-6cddc4a1f857' }
let(:visitor2_id) { '1dd9afb2-a3ee-4de1-8ae3-a405579c8584' } let(:visitor2_id) { '1dd9afb2-a3ee-4de1-8ae3-a405579c8584' }
let(:visitor3_id) { '34rfjuuy-ce56-sa35-ds34-dfer567dfrf2' }
around do |example| around do |example|
# We need to freeze to a reference time # We need to freeze to a reference time
...@@ -29,18 +32,32 @@ RSpec.describe Gitlab::Analytics::UniqueVisits, :clean_gitlab_redis_shared_state ...@@ -29,18 +32,32 @@ RSpec.describe Gitlab::Analytics::UniqueVisits, :clean_gitlab_redis_shared_state
unique_visits.track_visit(visitor1_id, target2_id, 8.days.ago) unique_visits.track_visit(visitor1_id, target2_id, 8.days.ago)
unique_visits.track_visit(visitor1_id, target2_id, 15.days.ago) unique_visits.track_visit(visitor1_id, target2_id, 15.days.ago)
unique_visits.track_visit(visitor3_id, target4_id, 7.days.ago)
unique_visits.track_visit(visitor3_id, target5_id, 15.days.ago)
unique_visits.track_visit(visitor2_id, target5_id, 15.days.ago)
expect(unique_visits.unique_visits_for(targets: target1_id)).to eq(2) expect(unique_visits.unique_visits_for(targets: target1_id)).to eq(2)
expect(unique_visits.unique_visits_for(targets: target2_id)).to eq(1) expect(unique_visits.unique_visits_for(targets: target2_id)).to eq(1)
expect(unique_visits.unique_visits_for(targets: target4_id)).to eq(1)
expect(unique_visits.unique_visits_for(targets: target2_id, start_week: 15.days.ago)).to eq(1) expect(unique_visits.unique_visits_for(targets: target2_id, start_week: 15.days.ago)).to eq(1)
expect(unique_visits.unique_visits_for(targets: target3_id)).to eq(0) expect(unique_visits.unique_visits_for(targets: target3_id)).to eq(0)
expect(unique_visits.unique_visits_for(targets: :any)).to eq(2) expect(unique_visits.unique_visits_for(targets: target5_id, start_week: 15.days.ago)).to eq(2)
expect(unique_visits.unique_visits_for(targets: :any, start_week: 15.days.ago)).to eq(1)
expect(unique_visits.unique_visits_for(targets: :any, start_week: 30.days.ago)).to eq(0) expect(unique_visits.unique_visits_for(targets: :analytics)).to eq(2)
expect(unique_visits.unique_visits_for(targets: :analytics, start_week: 15.days.ago)).to eq(1)
expect(unique_visits.unique_visits_for(targets: :analytics, start_week: 30.days.ago)).to eq(0)
expect(unique_visits.unique_visits_for(targets: :analytics, weeks: 4)).to eq(2)
expect(unique_visits.unique_visits_for(targets: :compliance)).to eq(1)
expect(unique_visits.unique_visits_for(targets: :compliance, start_week: 15.days.ago)).to eq(2)
expect(unique_visits.unique_visits_for(targets: :compliance, start_week: 30.days.ago)).to eq(0)
expect(unique_visits.unique_visits_for(targets: :any, weeks: 4)).to eq(2) expect(unique_visits.unique_visits_for(targets: :compliance, weeks: 4)).to eq(2)
end end
it 'sets the keys in Redis to expire automatically after 12 weeks' do it 'sets the keys in Redis to expire automatically after 12 weeks' do
......
...@@ -958,12 +958,12 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do ...@@ -958,12 +958,12 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
subject { described_class.analytics_unique_visits_data } subject { described_class.analytics_unique_visits_data }
it 'returns the number of unique visits to pages with analytics features' do it 'returns the number of unique visits to pages with analytics features' do
::Gitlab::Analytics::UniqueVisits::TARGET_IDS.each do |target_id| ::Gitlab::Analytics::UniqueVisits::ANALYTICS_IDS.each do |target_id|
expect_any_instance_of(::Gitlab::Analytics::UniqueVisits).to receive(:unique_visits_for).with(targets: target_id).and_return(123) expect_any_instance_of(::Gitlab::Analytics::UniqueVisits).to receive(:unique_visits_for).with(targets: target_id).and_return(123)
end end
expect_any_instance_of(::Gitlab::Analytics::UniqueVisits).to receive(:unique_visits_for).with(targets: :any).and_return(543) expect_any_instance_of(::Gitlab::Analytics::UniqueVisits).to receive(:unique_visits_for).with(targets: :analytics).and_return(543)
expect_any_instance_of(::Gitlab::Analytics::UniqueVisits).to receive(:unique_visits_for).with(targets: :any, weeks: 4).and_return(987) expect_any_instance_of(::Gitlab::Analytics::UniqueVisits).to receive(:unique_visits_for).with(targets: :analytics, weeks: 4).and_return(987)
expect(subject).to eq({ expect(subject).to eq({
analytics_unique_visits: { analytics_unique_visits: {
...@@ -987,6 +987,37 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do ...@@ -987,6 +987,37 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
end end
end end
describe '.compliance_unique_visits_data' do
subject { described_class.compliance_unique_visits_data }
before do
described_class.clear_memoization(:unique_visit_service)
allow_next_instance_of(::Gitlab::Analytics::UniqueVisits) do |instance|
::Gitlab::Analytics::UniqueVisits::COMPLIANCE_IDS.each do |target_id|
allow(instance).to receive(:unique_visits_for).with(targets: target_id).and_return(123)
end
allow(instance).to receive(:unique_visits_for).with(targets: :compliance).and_return(543)
allow(instance).to receive(:unique_visits_for).with(targets: :compliance, weeks: 4).and_return(987)
end
end
it 'returns the number of unique visits to pages with compliance features' do
expect(subject).to eq({
compliance_unique_visits: {
'g_compliance_dashboard' => 123,
'g_compliance_audit_events' => 123,
'i_compliance_credential_inventory' => 123,
'i_compliance_audit_events' => 123,
'compliance_unique_visits_for_any_target' => 543,
'compliance_unique_visits_for_any_target_monthly' => 987
}
})
end
end
describe '.service_desk_counts' do describe '.service_desk_counts' do
subject { described_class.send(:service_desk_counts) } subject { described_class.send(:service_desk_counts) }
......
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