Commit 0290cabf authored by Max Woolf's avatar Max Woolf

Merge branch '351863-stream-audit-event-json' into 'master'

Stream audit event using audit event json

See merge request gitlab-org/gitlab!80297
parents 4d9be902 156e6741
...@@ -14,13 +14,13 @@ module AuditEvents ...@@ -14,13 +14,13 @@ module AuditEvents
data_consistency :always data_consistency :always
feature_category :audit_events feature_category :audit_events
def perform(audit_event_id) def perform(audit_event_id, audit_event_json = nil)
audit_event = AuditEvent.find(audit_event_id) raise ArgumentError, 'audit_event_id and audit_event_json cannot be passed together' if audit_event_id.present? && audit_event_json.present?
return if audit_event.entity.nil? audit_event = audit_event(audit_event_id, audit_event_json)
return if audit_event.nil?
group = group_entity(audit_event) group = group_entity(audit_event)
return if group.nil? # Do nothing if the event can't be resolved to a single group. return if group.nil? # Do nothing if the event can't be resolved to a single group.
return unless group.licensed_feature_available?(:external_audit_events) return unless group.licensed_feature_available?(:external_audit_events)
...@@ -37,14 +37,33 @@ module AuditEvents ...@@ -37,14 +37,33 @@ module AuditEvents
private private
# Fetches audit event from database if audit_event_id is present
# Or parses audit event json into instance of AuditEvent if audit_event_json is present
def audit_event(audit_event_id, audit_event_json)
return parse_audit_event_json(audit_event_json) if audit_event_json.present?
AuditEvent.find(audit_event_id) if audit_event_id.present?
end
def parse_audit_event_json(audit_event_json)
audit_event_json = Gitlab::Json.parse(audit_event_json)
audit_event = AuditEvent.new(audit_event_json)
# We want to have created_at as unique id for deduplication if audit_event id is not present
audit_event.id = audit_event.created_at.to_i if audit_event.id.blank?
audit_event
end
def group_entity(audit_event) def group_entity(audit_event)
entity = audit_event.entity
return if entity.nil?
case audit_event.entity_type case audit_event.entity_type
when 'Group' when 'Group'
audit_event.entity entity
when 'Project' when 'Project'
# Project events should be sent to the root ancestor's streaming destinations # Project events should be sent to the root ancestor's streaming destinations
# Projects without a group root ancestor should be ignored. # Projects without a group root ancestor should be ignored.
audit_event.entity.group&.root_ancestor entity.group&.root_ancestor
else else
nil nil
end end
......
...@@ -9,9 +9,43 @@ RSpec.describe AuditEvents::AuditEventStreamingWorker do ...@@ -9,9 +9,43 @@ RSpec.describe AuditEvents::AuditEventStreamingWorker do
stub_licensed_features(external_audit_events: true) stub_licensed_features(external_audit_events: true)
end end
shared_examples 'a successful audit event stream' do shared_context 'a successful audit event stream' do
subject { worker.perform(event.id) } context 'when audit event id is passed' do
subject { worker.perform(event.id) }
include_context 'audit event stream'
end
context 'when audit event json is passed' do
subject { worker.perform(nil, event.to_json) }
include_context 'audit event stream'
end
end
shared_context 'a error is raised' do
context 'when audit event id is passed' do
subject { worker.perform(event.id) }
include_context 'http post error'
end
context 'when audit event json is passed' do
subject { worker.perform(nil, event.to_json) }
include_context 'http post error'
end
context 'when both audit event id and audit event json is passed' do
subject { worker.perform(event.id, event.to_json) }
it 'a argument error is raised' do
expect { subject }.to raise_error(ArgumentError, 'audit_event_id and audit_event_json cannot be passed together')
end
end
end
shared_context 'audit event stream' do
context 'when the group has no destinations' do context 'when the group has no destinations' do
it 'makes no HTTP calls' do it 'makes no HTTP calls' do
expect(Gitlab::HTTP).not_to receive(:post) expect(Gitlab::HTTP).not_to receive(:post)
...@@ -65,9 +99,7 @@ RSpec.describe AuditEvents::AuditEventStreamingWorker do ...@@ -65,9 +99,7 @@ RSpec.describe AuditEvents::AuditEventStreamingWorker do
end end
end end
shared_examples 'a http post error is raised' do shared_context 'http post error' do
subject { worker.perform(event.id) }
context 'when any of Gitlab::HTTP::HTTP_ERRORS is raised' do context 'when any of Gitlab::HTTP::HTTP_ERRORS is raised' do
Gitlab::HTTP::HTTP_ERRORS.each do |error_klass| Gitlab::HTTP::HTTP_ERRORS.each do |error_klass|
let(:error) { error_klass.new('error') } let(:error) { error_klass.new('error') }
...@@ -102,6 +134,28 @@ RSpec.describe AuditEvents::AuditEventStreamingWorker do ...@@ -102,6 +134,28 @@ RSpec.describe AuditEvents::AuditEventStreamingWorker do
end end
end end
shared_examples 'no HTTP calls are made' do
context 'when audit event id is passed as param' do
subject { worker.perform(event.id) }
it 'makes no HTTP calls' do
expect(Gitlab::HTTP).not_to receive(:post)
subject
end
end
context 'when audit event json is passed as param' do
subject { worker.perform(nil, event.to_json) }
it 'makes no HTTP calls' do
expect(Gitlab::HTTP).not_to receive(:post)
subject
end
end
end
describe "#perform" do describe "#perform" do
context 'when the entity type is a group' do context 'when the entity type is a group' do
it_behaves_like 'a successful audit event stream' do it_behaves_like 'a successful audit event stream' do
...@@ -110,7 +164,7 @@ RSpec.describe AuditEvents::AuditEventStreamingWorker do ...@@ -110,7 +164,7 @@ RSpec.describe AuditEvents::AuditEventStreamingWorker do
let(:group) { event.entity } let(:group) { event.entity }
end end
it_behaves_like 'a http post error is raised' do it_behaves_like 'a error is raised' do
let_it_be(:event) { create(:audit_event, :group_event) } let_it_be(:event) { create(:audit_event, :group_event) }
let(:group) { event.entity } let(:group) { event.entity }
...@@ -124,7 +178,7 @@ RSpec.describe AuditEvents::AuditEventStreamingWorker do ...@@ -124,7 +178,7 @@ RSpec.describe AuditEvents::AuditEventStreamingWorker do
let_it_be(:event) { create(:audit_event, :project_event, target_project: project) } let_it_be(:event) { create(:audit_event, :project_event, target_project: project) }
end end
it_behaves_like 'a http post error is raised' do it_behaves_like 'a error is raised' do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) } let_it_be(:project) { create(:project, group: group) }
let_it_be(:event) { create(:audit_event, :project_event, target_project: project) } let_it_be(:event) { create(:audit_event, :project_event, target_project: project) }
...@@ -134,11 +188,7 @@ RSpec.describe AuditEvents::AuditEventStreamingWorker do ...@@ -134,11 +188,7 @@ RSpec.describe AuditEvents::AuditEventStreamingWorker do
context 'when the entity type is a project at a root namespace level' do context 'when the entity type is a project at a root namespace level' do
let_it_be(:event) { create(:audit_event, :project_event) } let_it_be(:event) { create(:audit_event, :project_event) }
it 'makes no HTTP calls' do it_behaves_like 'no HTTP calls are made'
expect(Gitlab::HTTP).not_to receive(:post)
worker.perform(event.id)
end
end end
context 'when the entity is a NullEntity' do context 'when the entity is a NullEntity' do
...@@ -148,11 +198,7 @@ RSpec.describe AuditEvents::AuditEventStreamingWorker do ...@@ -148,11 +198,7 @@ RSpec.describe AuditEvents::AuditEventStreamingWorker do
event.entity_id = non_existing_record_id event.entity_id = non_existing_record_id
end end
it 'makes no HTTP calls' do it_behaves_like 'no HTTP calls are made'
expect(Gitlab::HTTP).not_to receive(:post)
worker.perform(event.id)
end
end end
end 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