Count wiki page creation
This adds a counter to count page creation, which is reflected in the usage-data we collect. The number created is stored in Redis, avoiding DB access.
This commit is contained in:
parent
66394bd1b7
commit
7320758611
12 changed files with 270 additions and 16 deletions
|
@ -8,6 +8,12 @@ module WikiPages
|
|||
page_data = Gitlab::DataBuilder::WikiPage.build(page, current_user, action)
|
||||
@project.execute_hooks(page_data, :wiki_page_hooks)
|
||||
@project.execute_services(page_data, :wiki_page_hooks)
|
||||
increment_usage(action)
|
||||
end
|
||||
|
||||
# This method throws an error if the action is an unanticipated value.
|
||||
def increment_usage(action)
|
||||
Gitlab::UsageDataCounters::WikiPageCounter.count(action)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
5
changelogs/unreleased/wiki-usage-pings.yml
Normal file
5
changelogs/unreleased/wiki-usage-pings.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Count wiki creation, update and delete events
|
||||
merge_request: 30864
|
||||
author:
|
||||
type: added
|
|
@ -6,7 +6,9 @@ module Gitlab
|
|||
|
||||
class << self
|
||||
def data(force_refresh: false)
|
||||
Rails.cache.fetch('usage_data', force: force_refresh, expires_in: 2.weeks) { uncached_data }
|
||||
Rails.cache.fetch('usage_data', force: force_refresh, expires_in: 2.weeks) do
|
||||
uncached_data
|
||||
end
|
||||
end
|
||||
|
||||
def uncached_data
|
||||
|
@ -128,12 +130,15 @@ module Gitlab
|
|||
}
|
||||
end
|
||||
|
||||
# @return [Hash<Symbol, Integer>]
|
||||
def usage_counters
|
||||
{
|
||||
web_ide_commits: Gitlab::UsageDataCounters::WebIdeCounter.total_commits_count,
|
||||
web_ide_merge_requests: Gitlab::UsageDataCounters::WebIdeCounter.total_merge_requests_count,
|
||||
web_ide_views: Gitlab::UsageDataCounters::WebIdeCounter.total_views_count
|
||||
}
|
||||
usage_data_counters.map(&:totals).reduce({}) { |a, b| a.merge(b) }
|
||||
end
|
||||
|
||||
# @return [Array<#totals>] An array of objects that respond to `#totals`
|
||||
def usage_data_counters
|
||||
[Gitlab::UsageDataCounters::WikiPageCounter,
|
||||
Gitlab::UsageDataCounters::WebIdeCounter]
|
||||
end
|
||||
|
||||
def components_usage_data
|
||||
|
|
|
@ -33,6 +33,14 @@ module Gitlab
|
|||
def total_views_count
|
||||
total_count(VIEWS_COUNT_KEY)
|
||||
end
|
||||
|
||||
def totals
|
||||
{
|
||||
web_ide_commits: total_commits_count,
|
||||
web_ide_views: total_views_count,
|
||||
web_ide_merge_requests: total_merge_requests_count
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
32
lib/gitlab/usage_data_counters/wiki_page_counter.rb
Normal file
32
lib/gitlab/usage_data_counters/wiki_page_counter.rb
Normal file
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab::UsageDataCounters
|
||||
class WikiPageCounter
|
||||
extend RedisCounter
|
||||
|
||||
KNOWN_EVENTS = %w[create update delete].map(&:freeze).freeze
|
||||
|
||||
UnknownEvent = Class.new(StandardError)
|
||||
|
||||
class << self
|
||||
# Each event gets a unique Redis key
|
||||
def redis_key(event)
|
||||
raise UnknownEvent, event unless KNOWN_EVENTS.include?(event.to_s)
|
||||
|
||||
"USAGE_WIKI_PAGES_#{event}".upcase
|
||||
end
|
||||
|
||||
def count(event)
|
||||
increment(redis_key event)
|
||||
end
|
||||
|
||||
def read(event)
|
||||
total_count(redis_key event)
|
||||
end
|
||||
|
||||
def totals
|
||||
KNOWN_EVENTS.map { |e| ["wiki_pages_#{e}".to_sym, read(e)] }.to_h
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -33,4 +33,24 @@ describe Gitlab::UsageDataCounters::WebIdeCounter, :clean_gitlab_redis_shared_st
|
|||
|
||||
it_behaves_like 'counter examples'
|
||||
end
|
||||
|
||||
describe '.totals' do
|
||||
commits = 5
|
||||
merge_requests = 3
|
||||
views = 2
|
||||
|
||||
before do
|
||||
commits.times { described_class.increment_commits_count }
|
||||
merge_requests.times { described_class.increment_merge_requests_count }
|
||||
views.times { described_class.increment_views_count }
|
||||
end
|
||||
|
||||
it 'can report all totals' do
|
||||
expect(described_class.totals).to include(
|
||||
web_ide_commits: commits,
|
||||
web_ide_views: views,
|
||||
web_ide_merge_requests: merge_requests
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::UsageDataCounters::WikiPageCounter, :clean_gitlab_redis_shared_state do
|
||||
shared_examples :wiki_page_event do |event|
|
||||
describe ".count(#{event})" do
|
||||
it "increments the wiki page #{event} counter by 1" do
|
||||
expect do
|
||||
described_class.count(event)
|
||||
end.to change { described_class.read(event) }.by 1
|
||||
end
|
||||
end
|
||||
|
||||
describe ".read(#{event})" do
|
||||
event_count = 5
|
||||
|
||||
it "returns the total number of #{event} events" do
|
||||
event_count.times do
|
||||
described_class.count(event)
|
||||
end
|
||||
|
||||
expect(described_class.read(event)).to eq(event_count)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
include_examples :wiki_page_event, :create
|
||||
include_examples :wiki_page_event, :update
|
||||
include_examples :wiki_page_event, :delete
|
||||
|
||||
describe 'totals' do
|
||||
creations = 5
|
||||
edits = 3
|
||||
deletions = 2
|
||||
|
||||
before do
|
||||
creations.times do
|
||||
described_class.count(:create)
|
||||
end
|
||||
edits.times do
|
||||
described_class.count(:update)
|
||||
end
|
||||
deletions.times do
|
||||
described_class.count(:delete)
|
||||
end
|
||||
end
|
||||
|
||||
it 'can report all totals' do
|
||||
expect(described_class.totals).to include(
|
||||
wiki_pages_update: edits,
|
||||
wiki_pages_create: creations,
|
||||
wiki_pages_delete: deletions
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'unknown events' do
|
||||
error = described_class::UnknownEvent
|
||||
|
||||
it 'cannot increment' do
|
||||
expect { described_class.count(:wibble) }.to raise_error error
|
||||
end
|
||||
|
||||
it 'cannot read' do
|
||||
expect { described_class.read(:wibble) }.to raise_error error
|
||||
end
|
||||
end
|
||||
end
|
|
@ -57,20 +57,18 @@ describe Gitlab::UsageData do
|
|||
gitaly
|
||||
database
|
||||
avg_cycle_analytics
|
||||
web_ide_views
|
||||
web_ide_commits
|
||||
web_ide_merge_requests
|
||||
influxdb_metrics_enabled
|
||||
prometheus_metrics_enabled
|
||||
))
|
||||
end
|
||||
|
||||
it 'calls expected usage data methods' do
|
||||
expect(Gitlab::UsageDataCounters::WebIdeCounter).to receive(:total_commits_count)
|
||||
expect(Gitlab::UsageDataCounters::WebIdeCounter).to receive(:total_merge_requests_count)
|
||||
expect(Gitlab::UsageDataCounters::WebIdeCounter).to receive(:total_views_count)
|
||||
|
||||
subject
|
||||
expect(subject).to include(
|
||||
wiki_pages_create: a_kind_of(Integer),
|
||||
wiki_pages_update: a_kind_of(Integer),
|
||||
wiki_pages_delete: a_kind_of(Integer),
|
||||
web_ide_views: a_kind_of(Integer),
|
||||
web_ide_commits: a_kind_of(Integer),
|
||||
web_ide_merge_requests: a_kind_of(Integer)
|
||||
)
|
||||
end
|
||||
|
||||
it "gathers usage counts" do
|
||||
|
@ -192,6 +190,28 @@ describe Gitlab::UsageData do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#usage_data_counters' do
|
||||
subject { described_class.usage_data_counters }
|
||||
|
||||
it { is_expected.to all(respond_to :totals) }
|
||||
|
||||
describe 'the results of calling #totals on all objects in the array' do
|
||||
subject { described_class.usage_data_counters.map(&:totals) }
|
||||
|
||||
it do
|
||||
is_expected
|
||||
.to all(be_a Hash)
|
||||
.and all(have_attributes(keys: all(be_a Symbol), values: all(be_a Integer)))
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not have any conflicts' do
|
||||
all_keys = subject.flat_map { |counter| counter.totals.keys }
|
||||
|
||||
expect(all_keys.size).to eq all_keys.to_set.size
|
||||
end
|
||||
end
|
||||
|
||||
describe '#features_usage_data_ce' do
|
||||
subject { described_class.features_usage_data_ce }
|
||||
|
||||
|
|
27
spec/services/wiki_pages/base_service_spec.rb
Normal file
27
spec/services/wiki_pages/base_service_spec.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe WikiPages::BaseService do
|
||||
let(:project) { double('project') }
|
||||
let(:user) { double('user') }
|
||||
|
||||
subject(:service) { described_class.new(project, user, {}) }
|
||||
|
||||
describe '#increment_usage' do
|
||||
counter = Gitlab::UsageDataCounters::WikiPageCounter
|
||||
error = counter::UnknownEvent
|
||||
|
||||
it 'raises an error on unknown events' do
|
||||
expect { subject.send(:increment_usage, :bad_event) }.to raise_error error
|
||||
end
|
||||
|
||||
context 'the event is valid' do
|
||||
counter::KNOWN_EVENTS.each do |e|
|
||||
it "updates the #{e} counter" do
|
||||
expect { subject.send(:increment_usage, e) }.to change { counter.read(e) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,6 +14,10 @@ describe WikiPages::CreateService do
|
|||
}
|
||||
end
|
||||
|
||||
let(:bad_opts) do
|
||||
{ title: '' }
|
||||
end
|
||||
|
||||
subject(:service) { described_class.new(project, user, opts) }
|
||||
|
||||
before do
|
||||
|
@ -36,5 +40,26 @@ describe WikiPages::CreateService do
|
|||
|
||||
service.execute
|
||||
end
|
||||
|
||||
it 'counts wiki page creation' do
|
||||
counter = Gitlab::UsageDataCounters::WikiPageCounter
|
||||
|
||||
expect { service.execute }.to change { counter.read(:create) }.by 1
|
||||
end
|
||||
|
||||
context 'when the options are bad' do
|
||||
subject(:service) { described_class.new(project, user, bad_opts) }
|
||||
|
||||
it 'does not count a creation event' do
|
||||
counter = Gitlab::UsageDataCounters::WikiPageCounter
|
||||
|
||||
expect { service.execute }.not_to change { counter.read(:create) }
|
||||
end
|
||||
|
||||
it 'reports the error' do
|
||||
expect(service.execute).to be_invalid
|
||||
.and have_attributes(errors: be_present)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,5 +20,17 @@ describe WikiPages::DestroyService do
|
|||
|
||||
service.execute(page)
|
||||
end
|
||||
|
||||
it 'increments the delete count' do
|
||||
counter = Gitlab::UsageDataCounters::WikiPageCounter
|
||||
|
||||
expect { service.execute(page) }.to change { counter.read(:delete) }.by 1
|
||||
end
|
||||
|
||||
it 'does not increment the delete count if the deletion failed' do
|
||||
counter = Gitlab::UsageDataCounters::WikiPageCounter
|
||||
|
||||
expect { service.execute(nil) }.not_to change { counter.read(:delete) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,6 +16,10 @@ describe WikiPages::UpdateService do
|
|||
}
|
||||
end
|
||||
|
||||
let(:bad_opts) do
|
||||
{ title: '' }
|
||||
end
|
||||
|
||||
subject(:service) { described_class.new(project, user, opts) }
|
||||
|
||||
before do
|
||||
|
@ -39,5 +43,26 @@ describe WikiPages::UpdateService do
|
|||
|
||||
service.execute(page)
|
||||
end
|
||||
|
||||
it 'counts edit events' do
|
||||
counter = Gitlab::UsageDataCounters::WikiPageCounter
|
||||
|
||||
expect { service.execute page }.to change { counter.read(:update) }.by 1
|
||||
end
|
||||
|
||||
context 'when the options are bad' do
|
||||
subject(:service) { described_class.new(project, user, bad_opts) }
|
||||
|
||||
it 'does not count an edit event' do
|
||||
counter = Gitlab::UsageDataCounters::WikiPageCounter
|
||||
|
||||
expect { service.execute page }.not_to change { counter.read(:update) }
|
||||
end
|
||||
|
||||
it 'reports the error' do
|
||||
expect(service.execute page).to be_invalid
|
||||
.and have_attributes(errors: be_present)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue