Commit ab97b007 authored by Alex Kalderimis's avatar Alex Kalderimis

Create wiki events on pushes

On every push received by the post-receive hook, the WikiPushService is
run on the changes. This modifies that file to look at the changes in
the master branch (well, wiki repo default branch) and create any wiki
events necessary.

These actions are guarded by two feature flags:

 - :wiki_events
    This prevents wiki pushes creating events while the wiki_events flag is
    off. This flag is the same flag used by the WikiPage services.
 - :wiki_events_on_git_push, (scoped to project)
    Specific feature flag for git-access

- WikiPushService::Change

Most of the state is in the change class, combining the change with the
raw diff change, and this extraction tidies
things up a lot.

Logic repeated between EE and core is refactored to be consistent.

- Add WikiPages::EventCreateService

This service wraps EventCreateService and manages the instantiation of
WikiPage::Meta objects. A spec is added to verify this.

This verifies the three public methods for this class
- last_known_slug
- page
- event_action

The number of changes we will process is subject to a safety limit.
parent 7c4295a1
...@@ -85,14 +85,14 @@ class EventCreateService ...@@ -85,14 +85,14 @@ class EventCreateService
# Create a new wiki page event # Create a new wiki page event
# #
# @param [WikiPage::Meta] wiki_page_meta The event target # @param [WikiPage::Meta] wiki_page_meta The event target
# @param [User] current_user The event author # @param [User] author The event author
# @param [Integer] action One of the Event::WIKI_ACTIONS # @param [Integer] action One of the Event::WIKI_ACTIONS
def wiki_event(wiki_page_meta, current_user, action) def wiki_event(wiki_page_meta, author, action)
return unless Feature.enabled?(:wiki_events) return unless Feature.enabled?(:wiki_events)
raise IllegalActionError, action unless Event::WIKI_ACTIONS.include?(action) raise IllegalActionError, action unless Event::WIKI_ACTIONS.include?(action)
create_record_event(wiki_page_meta, current_user, action) create_record_event(wiki_page_meta, author, action)
end end
private private
......
...@@ -2,8 +2,63 @@ ...@@ -2,8 +2,63 @@
module Git module Git
class WikiPushService < ::BaseService class WikiPushService < ::BaseService
# Maximum number of change events we will process on any single push
MAX_CHANGES = 100
def execute def execute
# This is used in EE process_changes
end
private
def process_changes
return unless can_process_wiki_events?
push_changes.take(MAX_CHANGES).each do |change| # rubocop:disable CodeReuse/ActiveRecord
next unless change.page.present?
response = create_event_for(change)
log_error(response.message) if response.error?
end
end
def can_process_wiki_events?
Feature.enabled?(:wiki_events) && Feature.enabled?(:wiki_events_on_git_push, project)
end
def push_changes
default_branch_changes.flat_map do |change|
raw_changes(change).map { |raw| Git::WikiPushService::Change.new(wiki, change, raw) }
end
end
def raw_changes(change)
wiki.repository.raw.raw_changes_between(change[:oldrev], change[:newrev])
end
def wiki
project.wiki
end
def create_event_for(change)
event_service.execute(change.last_known_slug, change.page, change.event_action)
end
def event_service
@event_service ||= WikiPages::EventCreateService.new(current_user)
end
def on_default_branch?(change)
project.wiki.default_branch == ::Gitlab::Git.branch_name(change[:ref])
end
# See: [Gitlab::GitPostReceive#changes]
def changes
params[:changes] || []
end
def default_branch_changes
@default_branch_changes ||= changes.select { |change| on_default_branch?(change) }
end end
end end
end end
......
# frozen_string_literal: true
module Git
class WikiPushService
class Change
include Gitlab::Utils::StrongMemoize
# @param [ProjectWiki] wiki
# @param [Hash] change - must have keys `:oldrev` and `:newrev`
# @param [Gitlab::Git::RawDiffChange] raw_change
def initialize(project_wiki, change, raw_change)
@wiki, @raw_change, @change = project_wiki, raw_change, change
end
def page
strong_memoize(:page) { wiki.find_page(slug, revision) }
end
# See [Gitlab::Git::RawDiffChange#extract_operation] for the
# definition of the full range of operation values.
def event_action
case raw_change.operation
when :added
Event::CREATED
when :deleted
Event::DESTROYED
else
Event::UPDATED
end
end
def last_known_slug
strip_extension(raw_change.old_path || raw_change.new_path)
end
private
attr_reader :raw_change, :change, :wiki
def filename
return raw_change.old_path if deleted?
raw_change.new_path
end
def slug
strip_extension(filename)
end
def revision
return change[:oldrev] if deleted?
change[:newrev]
end
def deleted?
raw_change.operation == :deleted
end
def strip_extension(filename)
return unless filename
File.basename(filename, File.extname(filename))
end
end
end
end
...@@ -46,12 +46,9 @@ module WikiPages ...@@ -46,12 +46,9 @@ module WikiPages
def create_wiki_event(page) def create_wiki_event(page)
return unless ::Feature.enabled?(:wiki_events) return unless ::Feature.enabled?(:wiki_events)
slug = slug_for_page(page) response = WikiPages::EventCreateService.new(current_user).execute(slug_for_page(page), page, event_action)
Event.transaction do log_error(response.message) if response.error?
wiki_page_meta = WikiPage::Meta.find_or_create(slug, page)
EventCreateService.new.wiki_event(wiki_page_meta, current_user, event_action)
end
end end
def slug_for_page(page) def slug_for_page(page)
......
# frozen_string_literal: true
module WikiPages
class EventCreateService
# @param [User] author The event author
def initialize(author)
@author = author
end
def execute(slug, page, action)
return ServiceResponse.success(message: 'No event created as `wiki_events` feature is disabled') unless ::Feature.enabled?(:wiki_events)
event = Event.transaction do
wiki_page_meta = WikiPage::Meta.find_or_create(slug, page)
::EventCreateService.new.wiki_event(wiki_page_meta, author, action)
end
ServiceResponse.success(message: 'Event created', payload: { event: event })
rescue ::EventCreateService::IllegalActionError, ::ActiveRecord::ActiveRecordError => e
ServiceResponse.error(message: e.message, payload: { error: e })
end
private
attr_reader :author
end
end
---
title: Create Wiki activity events on pushes to Wiki git repository
merge_request: 26624
author:
type: added
...@@ -3,16 +3,16 @@ ...@@ -3,16 +3,16 @@
module EE module EE
module Git module Git
module WikiPushService module WikiPushService
extend ::Gitlab::Utils::Override
override :execute
def execute def execute
super super
return unless project.use_elasticsearch? return unless project.use_elasticsearch?
# Check if one of the changes we got was for the default branch. If it was, trigger an ES update # For all changes on the default branch (usually master) trigger an ES update
params[:changes].each do |change| default_branch_changes.each do |change|
branch_name = ::Gitlab::Git.ref_name(change[:ref])
next unless project.wiki.default_branch == branch_name
project.wiki.index_wiki_blobs(change[:newrev]) project.wiki.index_wiki_blobs(change[:newrev])
end end
end end
......
...@@ -23,6 +23,10 @@ describe Git::WikiPushService do ...@@ -23,6 +23,10 @@ describe Git::WikiPushService do
describe 'when changes include master ref' do describe 'when changes include master ref' do
let(:changes) { +"123456 789012 refs/heads/tést\n654321 210987 refs/tags/tag\n423423 797823 refs/heads/master" } let(:changes) { +"123456 789012 refs/heads/tést\n654321 210987 refs/tags/tag\n423423 797823 refs/heads/master" }
before do
allow(project.wiki.repository.raw).to receive(:raw_changes_between).once.with('423423', '797823').and_return([])
end
it 'triggers a wiki update' do it 'triggers a wiki update' do
expect(project.wiki).to receive(:index_wiki_blobs).with("797823") expect(project.wiki).to receive(:index_wiki_blobs).with("797823")
...@@ -44,6 +48,7 @@ describe Git::WikiPushService do ...@@ -44,6 +48,7 @@ describe Git::WikiPushService do
context 'when elasticsearch is disabled' do context 'when elasticsearch is disabled' do
before do before do
stub_ee_application_setting(elasticsearch_search: false, elasticsearch_indexing: false) stub_ee_application_setting(elasticsearch_search: false, elasticsearch_indexing: false)
allow(project.wiki.repository.raw).to receive(:raw_changes_between).once.with('423423', '797823').and_return([])
end end
describe 'when changes include master ref' do describe 'when changes include master ref' do
......
...@@ -99,82 +99,94 @@ describe PostReceive do ...@@ -99,82 +99,94 @@ describe PostReceive do
describe '#process_wiki_changes' do describe '#process_wiki_changes' do
let(:gl_repository) { "wiki-#{project.id}" } let(:gl_repository) { "wiki-#{project.id}" }
it 'calls Geo::RepositoryUpdatedService when running on a Geo primary node' do it 'calls Git::WikiPushService#process_changes' do
allow(Gitlab::Geo).to receive(:primary?) { true } expect_any_instance_of(::Git::WikiPushService).to receive(:process_changes)
expect_any_instance_of(::Geo::RepositoryUpdatedService).to receive(:execute)
described_class.new.perform(gl_repository, key_id, base64_changes) described_class.new.perform(gl_repository, key_id, base64_changes)
end end
it 'does not call Geo::RepositoryUpdatedService when not running on a Geo primary node' do context 'assuming calls to process_changes are successful' do
allow(Gitlab::Geo).to receive(:primary?) { false } before do
allow_any_instance_of(Git::WikiPushService).to receive(:process_changes)
end
expect_any_instance_of(::Geo::RepositoryUpdatedService).not_to receive(:execute) it 'calls Geo::RepositoryUpdatedService when running on a Geo primary node' do
allow(Gitlab::Geo).to receive(:primary?) { true }
described_class.new.perform(gl_repository, key_id, base64_changes) expect_any_instance_of(::Geo::RepositoryUpdatedService).to receive(:execute)
end
it 'triggers wiki index update when ElasticSearch is enabled and pushed to master', :elastic do described_class.new.perform(gl_repository, key_id, base64_changes)
stub_ee_application_setting(elasticsearch_search: true, elasticsearch_indexing: true) end
expect_any_instance_of(ProjectWiki).to receive(:index_wiki_blobs) it 'does not call Geo::RepositoryUpdatedService when not running on a Geo primary node' do
allow(Gitlab::Geo).to receive(:primary?) { false }
described_class.new.perform(gl_repository, key_id, base64_changes_with_master) expect_any_instance_of(::Geo::RepositoryUpdatedService).not_to receive(:execute)
end
it 'does not trigger wiki index update when Elasticsearch is enabled and not pushed to master', :elastic do described_class.new.perform(gl_repository, key_id, base64_changes)
stub_ee_application_setting(elasticsearch_search: true, elasticsearch_indexing: true) end
expect_any_instance_of(ProjectWiki).not_to receive(:index_wiki_blobs) it 'triggers wiki index update when ElasticSearch is enabled and pushed to master', :elastic do
stub_ee_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
described_class.new.perform(gl_repository, key_id, base64_changes) expect_any_instance_of(ProjectWiki).to receive(:index_wiki_blobs)
end
context 'when limited indexing is on', :elastic do described_class.new.perform(gl_repository, key_id, base64_changes_with_master)
before do
stub_ee_application_setting(
elasticsearch_search: true,
elasticsearch_indexing: true,
elasticsearch_limit_indexing: true
)
end end
context 'when the project is not enabled specifically' do it 'does not trigger wiki index update when Elasticsearch is enabled and not pushed to master', :elastic do
it 'does not trigger wiki index update' do stub_ee_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
expect_any_instance_of(ProjectWiki).not_to receive(:index_wiki_blobs)
described_class.new.perform(gl_repository, key_id, base64_changes_with_master) expect_any_instance_of(ProjectWiki).not_to receive(:index_wiki_blobs)
end
described_class.new.perform(gl_repository, key_id, base64_changes)
end end
context 'when a project is enabled specifically' do context 'when limited indexing is on', :elastic do
before do before do
create :elasticsearch_indexed_project, project: project stub_ee_application_setting(
elasticsearch_search: true,
elasticsearch_indexing: true,
elasticsearch_limit_indexing: true
)
end end
it 'triggers wiki index update' do context 'when the project is not enabled specifically' do
expect_any_instance_of(ProjectWiki).to receive(:index_wiki_blobs) it 'does not trigger wiki index update' do
expect_any_instance_of(ProjectWiki).not_to receive(:index_wiki_blobs)
described_class.new.perform(gl_repository, key_id, base64_changes_with_master) described_class.new.perform(gl_repository, key_id, base64_changes_with_master)
end
end end
end
context 'when a group is enabled' do context 'when a project is enabled specifically' do
let(:user) { create(:user) } before do
let(:group) { create(:group) } create :elasticsearch_indexed_project, project: project
let(:project) { create(:project, :wiki_repo, group: group) } end
let(:key) { create(:key, user: user) }
before do it 'triggers wiki index update' do
create :elasticsearch_indexed_namespace, namespace: group expect_any_instance_of(ProjectWiki).to receive(:index_wiki_blobs)
group.add_owner(user)
described_class.new.perform(gl_repository, key_id, base64_changes_with_master)
end
end end
it 'triggers wiki index update' do context 'when a group is enabled' do
expect_any_instance_of(ProjectWiki).to receive(:index_wiki_blobs) let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, :wiki_repo, group: group) }
let(:key) { create(:key, user: user) }
before do
create :elasticsearch_indexed_namespace, namespace: group
group.add_owner(user)
end
described_class.new.perform(gl_repository, key_id, base64_changes_with_master) it 'triggers wiki index update' do
expect_any_instance_of(ProjectWiki).to receive(:index_wiki_blobs)
described_class.new.perform(gl_repository, key_id, base64_changes_with_master)
end
end end
end end
end end
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
FactoryBot.define do FactoryBot.define do
factory :design_version, class: 'DesignManagement::Version' do factory :design_version, class: 'DesignManagement::Version' do
sequence(:sha) { |n| Digest::SHA1.hexdigest("commit-like-#{n}") } sha
issue { designs.first&.issue || create(:issue) } issue { designs.first&.issue || create(:issue) }
author { issue&.author || create(:user) } author { issue&.author || create(:user) }
......
# frozen_string_literal: true
FactoryBot.define do
factory :git_wiki_commit_details, class: 'Gitlab::Git::Wiki::CommitDetails' do
skip_create
transient do
author { create(:user) }
end
sequence(:message) { |n| "Commit message #{n}" }
initialize_with { new(author.id, author.username, author.name, author.email, message) }
end
end
...@@ -12,4 +12,5 @@ FactoryBot.define do ...@@ -12,4 +12,5 @@ FactoryBot.define do
sequence(:branch) { |n| "my-branch-#{n}" } sequence(:branch) { |n| "my-branch-#{n}" }
sequence(:past_time) { |n| 4.hours.ago + (2 * n).seconds } sequence(:past_time) { |n| 4.hours.ago + (2 * n).seconds }
sequence(:iid) sequence(:iid)
sequence(:sha) { |n| Digest::SHA1.hexdigest("commit-like-#{n}") }
end end
...@@ -66,5 +66,6 @@ FactoryBot.define do ...@@ -66,5 +66,6 @@ FactoryBot.define do
end end
sequence(:wiki_page_title) { |n| "Page #{n}" } sequence(:wiki_page_title) { |n| "Page #{n}" }
sequence(:wiki_filename) { |n| "Page_#{n}.md" }
sequence(:sluggified_title) { |n| "slug-#{n}" } sequence(:sluggified_title) { |n| "slug-#{n}" }
end end
...@@ -168,7 +168,8 @@ describe EventCreateService do ...@@ -168,7 +168,8 @@ describe EventCreateService do
valid?: true, valid?: true,
persisted?: true, persisted?: true,
action: action, action: action,
wiki_page: wiki_page wiki_page: wiki_page,
author: user
) )
end end
......
# frozen_string_literal: true
require 'spec_helper'
describe Git::WikiPushService::Change do
subject { described_class.new(project_wiki, change, raw_change) }
let(:project_wiki) { double('ProjectWiki') }
let(:raw_change) { double('RawChange', new_path: new_path, old_path: old_path, operation: operation) }
let(:change) { { oldrev: generate(:sha), newrev: generate(:sha) } }
let(:new_path) do
case operation
when :deleted
nil
else
generate(:wiki_filename)
end
end
let(:old_path) do
case operation
when :added
nil
when :deleted, :renamed
generate(:wiki_filename)
else
new_path
end
end
describe '#page' do
context 'the page does not exist' do
before do
expect(project_wiki).to receive(:find_page).with(String, String).and_return(nil)
end
%i[added deleted renamed modified].each do |op|
context "the operation is #{op}" do
let(:operation) { op }
it { is_expected.to have_attributes(page: be_nil) }
end
end
end
context 'the page can be found' do
let(:wiki_page) { double('WikiPage') }
before do
expect(project_wiki).to receive(:find_page).with(slug, revision).and_return(wiki_page)
end
context 'the page has been deleted' do
let(:operation) { :deleted }
let(:slug) { old_path.chomp('.md') }
let(:revision) { change[:oldrev] }
it { is_expected.to have_attributes(page: wiki_page) }
end
%i[added renamed modified].each do |op|
let(:operation) { op }
let(:slug) { new_path.chomp('.md') }
let(:revision) { change[:newrev] }
it { is_expected.to have_attributes(page: wiki_page) }
end
end
end
describe '#last_known_slug' do
context 'the page has been created' do
let(:operation) { :added }
it { is_expected.to have_attributes(last_known_slug: new_path.chomp('.md')) }
end
%i[renamed modified deleted].each do |op|
context "the operation is #{op}" do
let(:operation) { op }
it { is_expected.to have_attributes(last_known_slug: old_path.chomp('.md')) }
end
end
end
describe '#event_action' do
context 'the page is deleted' do
let(:operation) { :deleted }
it { is_expected.to have_attributes(event_action: Event::DESTROYED) }
end
context 'the page is added' do
let(:operation) { :added }
it { is_expected.to have_attributes(event_action: Event::CREATED) }
end
%i[renamed modified].each do |op|
context "the page is #{op}" do
let(:operation) { op }
it { is_expected.to have_attributes(event_action: Event::UPDATED) }
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Git::WikiPushService, services: true do
include RepoHelpers
let(:gl_repository) { "wiki-#{project.id}" }
let(:key) { create(:key, user: current_user) }
let(:key_id) { key.shell_id }
let_it_be(:project) { create(:project, :wiki_repo) }
let_it_be(:current_user) { create(:user) }
let_it_be(:git_wiki) { project.wiki.wiki }
let_it_be(:repository) { git_wiki.repository }
let(:commit_details) { create(:git_wiki_commit_details, author: current_user) }
# Any actions that need to happen before we capture the base_sha
let(:setup) { nil }
# We need to know the SHA before each example runs, hence the use of let-bang
let!(:base_sha) do
setup
current_sha || Gitlab::Git::BLANK_SHA
end
describe '#execute' do
context 'the push contains more than the permitted number of changes' do
before do
described_class::MAX_CHANGES.times { write_new_page }
write_new_page
end
it 'creates only MAX_CHANGES events' do
expect { execute_service }.to change(Event, :count).by(described_class::MAX_CHANGES)
end
end
context 'default_branch collides with a tag' do
before do
write_new_page
end
it 'creates only one event' do
changes = post_received(base_sha, ['refs/heads/master', 'refs/tags/master']).changes
service = described_class.new(project, current_user, changes: changes)
expect { service.execute }.to change(Event, :count).by(1)
end
end
context 'one creation, one update, one deletion' do
let(:wiki_page_a) { create(:wiki_page, :with_real_page, project: project) }
let(:wiki_page_b) { create(:wiki_page, :with_real_page, project: project) }
let(:count) { Event::WIKI_ACTIONS.count }
let(:setup) { wiki_page_a && wiki_page_b }
before do
write_new_page
update_page(wiki_page_a.title)
delete_page(wiki_page_b.page.path)
end
it 'creates two events' do
expect { execute_service }.to change(Event, :count).by(count)
end
it 'handles all known actions' do
execute_service
expect(Event.last(count).pluck(:action)).to match_array(Event::WIKI_ACTIONS)
end
shared_examples 'a no-op push' do
it 'does not create any events' do
expect { execute_service }.not_to change(Event, :count)
end
it 'does not even look for events to process' do
service = described_class.new(project, current_user, changes: post_received.changes)
expect(service).not_to receive(:changed_files)
service.execute
end
end
context 'the wiki_events feature is disabled' do
before do
stub_feature_flags(wiki_events: false)
end
it_behaves_like 'a no-op push'
end
context 'the wiki_events_on_git_push feature is disabled' do
before do
stub_feature_flags(wiki_events_on_git_push: false)
end
it_behaves_like 'a no-op push'
context 'but is enabled for a given project' do
before do
stub_feature_flags(wiki_events_on_git_push: { enabled: true, thing: project })
end
it 'creates events' do
expect { execute_service }.to change(Event, :count).by(count)
end
end
end
end
context 'two pages have been created' do
before do
write_new_page
write_new_page
end
it 'creates two events' do
expect { execute_service }.to change(Event, :count).by(2)
end
it 'creates two metadata records' do
expect { execute_service }.to change(WikiPage::Meta, :count).by(2)
end
it 'creates appropriate events' do
execute_service
expect(Event.last(2)).to all(have_attributes(wiki_page?: true, action: Event::CREATED))
end
end
context 'a non-page file as been added' do
before do
write_non_page
end
it 'does not create events, or WikiPage metadata' do
expect { execute_service }.not_to change { [Event.count, WikiPage::Meta.count] }
end
end
context 'one page, and one non-page have been created' do
before do
write_new_page
write_non_page
end
it 'creates one events' do
expect { execute_service }.to change(Event, :count).by(1)
end
it 'creates two metadata records' do
expect { execute_service }.to change(WikiPage::Meta, :count).by(1)
end
it 'creates appropriate events' do
execute_service
expect(Event.last).to have_attributes(wiki_page?: true, action: Event::CREATED)
end
end
context 'one page has been added, and then updated' do
before do
title = write_new_page
update_page(title)
end
it 'creates just a single event' do
expect { execute_service }.to change(Event, :count).by(1)
end
it 'creates just one metadata record' do
expect { execute_service }.to change(WikiPage::Meta, :count).by(1)
end
it 'creates a new wiki page creation event' do
execute_service
expect(Event.last).to have_attributes(
wiki_page?: true,
action: Event::CREATED
)
end
end
context 'when a page we already know about has been updated' do
let(:wiki_page) { create(:wiki_page, :with_real_page, project: project) }
let(:setup) { create(:wiki_page_meta, :for_wiki_page, wiki_page: wiki_page) }
before do
update_page(wiki_page.title)
end
it 'does not create a new meta-data record' do
expect { execute_service }.not_to change(WikiPage::Meta, :count)
end
it 'creates a new event' do
expect { execute_service }.to change(Event, :count).by(1)
end
it 'adds an update event' do
execute_service
expect(Event.last).to have_attributes(
wiki_page?: true,
action: Event::UPDATED
)
end
context 'adding the event is not successful' do
it 'calls log_error' do
event_service = double(:event_service)
error = ServiceResponse.error(message: 'something went very very wrong')
service = described_class.new(project, current_user, changes: post_received.changes)
allow(service).to receive(:event_service).and_return(event_service)
allow(event_service).to receive(:execute).with(String, WikiPage, Integer).and_return(error)
expect(service).to receive(:log_error).with(error.message)
service.execute
end
end
end
context 'when a page we do not know about has been updated' do
let(:wiki_page) { create(:wiki_page, :with_real_page, project: project) }
let(:setup) { wiki_page }
before do
update_page(wiki_page.title)
end
it 'creates a new meta-data record' do
expect { execute_service }.to change(WikiPage::Meta, :count).by(1)
end
it 'creates a new event' do
expect { execute_service(base_sha) }.to change(Event, :count).by(1)
end
it 'adds an update event' do
execute_service
expect(Event.last).to have_attributes(
wiki_page?: true,
action: Event::UPDATED
)
end
end
context 'when a page we do not know about has been deleted' do
let(:wiki_page) { create(:wiki_page, :with_real_page, project: project) }
let(:setup) { wiki_page }
before do
delete_page(wiki_page.page.path)
end
it 'create a new meta-data record' do
expect { execute_service }.to change(WikiPage::Meta, :count).by(1)
end
it 'creates a new event' do
expect { execute_service(base_sha) }.to change(Event, :count).by(1)
end
it 'adds an update event' do
execute_service
expect(Event.last).to have_attributes(
wiki_page?: true,
action: Event::DESTROYED
)
end
end
end
def execute_service(base = base_sha)
changes = post_received(base).changes
described_class.new(project, current_user, changes: changes).tap(&:execute)
end
def post_received(base = base_sha, refs = ['refs/heads/master'])
change_str = refs.map { |ref| +"#{base} #{current_sha} #{ref}" }.join("\n")
post_received = ::Gitlab::GitPostReceive.new(project, key_id, change_str, {})
allow(post_received).to receive(:identify).with(key_id).and_return(current_user)
post_received
end
def current_sha
repository.gitaly_ref_client.find_branch('master')&.dereferenced_target&.id
end
def write_new_page
commit_details = create(:git_wiki_commit_details, author: current_user)
generate(:wiki_page_title).tap { |t| git_wiki.write_page(t, 'markdown', 'Hello', commit_details) }
end
# We write something to the wiki-repo that is not a page - as, for example, an
# attachment. This will appear as a raw-diff change, but wiki.find_file will
# return nil.
def write_non_page
params = {
file_name: 'attachment.log',
file_content: 'some stuff',
branch_name: 'master'
}
::Wikis::CreateAttachmentService.new(project, project.owner, params).execute
end
def update_page(title)
commit_details = create(:git_wiki_commit_details, author: current_user)
page = git_wiki.page(title: title)
git_wiki.update_page(page.path, title, 'markdown', 'Hey', commit_details)
end
def delete_page(path)
git_wiki.delete_page(path, commit_details)
end
end
# frozen_string_literal: true
require 'spec_helper'
describe WikiPages::EventCreateService do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
subject { described_class.new(user) }
describe '#execute' do
let_it_be(:page) { create(:wiki_page, project: project) }
let(:slug) { generate(:sluggified_title) }
let(:action) { Event::CREATED }
let(:response) { subject.execute(slug, page, action) }
context 'feature flag is not enabled' do
before do
stub_feature_flags(wiki_events: false)
end
it 'does not error' do
expect(response).to be_success
.and have_attributes(message: /No event created/)
end
it 'does not create an event' do
expect { response }.not_to change(Event, :count)
end
end
context 'the action is illegal' do
let(:action) { Event::WIKI_ACTIONS.max + 1 }
it 'returns an error' do
expect(response).to be_error
end
end
it 'returns a successful response' do
expect(response).to be_success
end
it 'creates a wiki page event' do
expect { response }.to change(Event, :count).by(1)
end
it 'returns an event in the payload' do
expect(response.payload).to include(event: have_attributes(author: user, wiki_page?: true, action: action))
end
it 'records the slug for the page' do
response
meta = WikiPage::Meta.find_or_create(page.slug, page)
expect(meta.slugs.pluck(:slug)).to include(slug)
end
end
end
...@@ -299,6 +299,31 @@ describe PostReceive do ...@@ -299,6 +299,31 @@ describe PostReceive do
end end
end end
context "master" do
let(:default_branch) { 'master' }
let(:oldrev) { '012345' }
let(:newrev) { '6789ab' }
let(:changes) do
<<~EOF
#{oldrev} #{newrev} refs/heads/#{default_branch}
123456 789012 refs/heads/tést2
EOF
end
let(:raw_repo) { double('RawRepo') }
it 'processes the changes on the master branch' do
expect_next_instance_of(Git::WikiPushService) do |service|
expect(service).to receive(:process_changes).and_call_original
end
expect(project.wiki).to receive(:default_branch).twice.and_return(default_branch)
expect(project.wiki.repository).to receive(:raw).and_return(raw_repo)
expect(raw_repo).to receive(:raw_changes_between).once.with(oldrev, newrev).and_return([])
perform
end
end
context "branches" do context "branches" do
let(:changes) do let(:changes) do
<<~EOF <<~EOF
...@@ -307,6 +332,12 @@ describe PostReceive do ...@@ -307,6 +332,12 @@ describe PostReceive do
EOF EOF
end end
before do
allow_next_instance_of(Git::WikiPushService) do |service|
allow(service).to receive(:process_changes)
end
end
it 'expires the branches cache' do it 'expires the branches cache' do
expect(project.wiki.repository).to receive(:expire_branches_cache).once expect(project.wiki.repository).to receive(:expire_branches_cache).once
......
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