Commit bbab9419 authored by Imre Farkas's avatar Imre Farkas

Merge branch '276499-group-webhook-for-members-edit-remove' into 'master'

Fire webhook when updating or removing a group member

See merge request gitlab-org/gitlab!50217
parents 48d87a2a 4b8764fa
...@@ -113,11 +113,30 @@ module EE ...@@ -113,11 +113,30 @@ module EE
def post_create_hook def post_create_hook
super super
execute_hooks_for(:create)
end
override :post_update_hook
def post_update_hook
super
if saved_change_to_access_level? || saved_change_to_expires_at?
execute_hooks_for(:update)
end
end
def post_destroy_hook
super
execute_hooks_for(:destroy)
end
def execute_hooks_for(event)
return unless self.source.feature_available?(:group_webhooks) return unless self.source.feature_available?(:group_webhooks)
return unless GroupHook.where(group_id: self.source.self_and_ancestors).exists? return unless GroupHook.where(group_id: self.source.self_and_ancestors).exists?
run_after_commit do run_after_commit do
data = ::Gitlab::HookData::GroupMemberBuilder.new(self).build(:create) data = ::Gitlab::HookData::GroupMemberBuilder.new(self).build(event)
self.source.execute_hooks(data, :member_hooks) self.source.execute_hooks(data, :member_hooks)
end end
end end
......
...@@ -3,4 +3,4 @@ ...@@ -3,4 +3,4 @@
= form.label :member_events, class: 'list-label form-check-label gl-ml-1' do = form.label :member_events, class: 'list-label form-check-label gl-ml-1' do
%strong= s_('Webhooks|Member events') %strong= s_('Webhooks|Member events')
%p.text-muted.gl-ml-1 %p.text-muted.gl-ml-1
= s_('Webhooks|This URL will be triggered when a member is added to a group') = s_('Webhooks|This URL will be triggered when a group member is created/updated/removed')
---
title: Add support for member update & destroy events in Group webhooks
merge_request: 50217
author:
type: added
...@@ -241,33 +241,20 @@ RSpec.describe GroupMember do ...@@ -241,33 +241,20 @@ RSpec.describe GroupMember do
context 'group member webhooks', :sidekiq_inline do context 'group member webhooks', :sidekiq_inline do
let_it_be(:group) { create(:group_with_plan, plan: :gold_plan) } let_it_be(:group) { create(:group_with_plan, plan: :gold_plan) }
let_it_be(:group_hook) { create(:group_hook, group: group, member_events: true) } let_it_be(:group_hook) { create(:group_hook, group: group, member_events: true) }
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
context 'when a member is added to the group' do
let(:group_member) { create(:group_member, group: group) }
context 'fires the webhook when a member is added' do
before do before do
WebMock.stub_request(:post, group_hook.url) WebMock.stub_request(:post, group_hook.url)
end end
it 'execute webhooks' do it 'executes user_add_to_group event webhook' do
member = group.add_guest(user) group.add_guest(group_member.user)
expect(WebMock).to have_requested(:post, group_hook.url).with( expect(WebMock).to have_requested(:post, group_hook.url).with(
headers: { 'Content-Type' => 'application/json', 'User-Agent' => "GitLab/#{Gitlab::VERSION}", 'X-Gitlab-Event' => 'Member Hook' }, webhook_data(group_member, 'user_add_to_group')
body: {
created_at: member.created_at&.xmlschema,
updated_at: member.updated_at&.xmlschema,
group_name: group.name,
group_path: group.path,
group_id: group.id,
user_username: user.username,
user_name: user.name,
user_email: user.email,
user_id: user.id,
group_access: 'Guest',
expires_at: member.expires_at&.xmlschema,
group_plan: 'gold',
event_name: 'user_add_to_group'
}.to_json
) )
end end
...@@ -275,7 +262,7 @@ RSpec.describe GroupMember do ...@@ -275,7 +262,7 @@ RSpec.describe GroupMember do
let_it_be(:subgroup) { create(:group, parent: group) } let_it_be(:subgroup) { create(:group, parent: group) }
let_it_be(:subgroup_hook) { create(:group_hook, group: subgroup, member_events: true) } let_it_be(:subgroup_hook) { create(:group_hook, group: subgroup, member_events: true) }
it 'fires webhook twice when parent group has member_events webhook enabled' do it 'fires two webhooks when parent group has member_events webhook enabled' do
WebMock.stub_request(:post, subgroup_hook.url) WebMock.stub_request(:post, subgroup_hook.url)
subgroup.add_guest(user) subgroup.add_guest(user)
...@@ -284,7 +271,7 @@ RSpec.describe GroupMember do ...@@ -284,7 +271,7 @@ RSpec.describe GroupMember do
expect(WebMock).to have_requested(:post, group_hook.url) expect(WebMock).to have_requested(:post, group_hook.url)
end end
it 'fires webhook once when parent group has member_events webhook disabled' do it 'fires one webhook when parent group has member_events webhook disabled' do
group_hook = create(:group_hook, group: group, member_events: false) group_hook = create(:group_hook, group: group, member_events: false)
WebMock.stub_request(:post, subgroup_hook.url) WebMock.stub_request(:post, subgroup_hook.url)
...@@ -297,6 +284,44 @@ RSpec.describe GroupMember do ...@@ -297,6 +284,44 @@ RSpec.describe GroupMember do
end end
end end
context 'when a group member is updated' do
let(:group_member) { create(:group_member, :developer, group: group, expires_at: 1.day.from_now) }
it 'executes user_update_for_group event webhook when user role is updated' do
WebMock.stub_request(:post, group_hook.url)
group_member.update!(access_level: Gitlab::Access::MAINTAINER)
expect(WebMock).to have_requested(:post, group_hook.url).with(
webhook_data(group_member, 'user_update_for_group')
)
end
it 'executes user_update_for_group event webhook when user expiration date is updated' do
WebMock.stub_request(:post, group_hook.url)
group_member.update!(expires_at: 2.days.from_now)
expect(WebMock).to have_requested(:post, group_hook.url).with(
webhook_data(group_member, 'user_update_for_group')
)
end
end
context 'when the group member is deleted' do
let_it_be(:group_member) { create(:group_member, :developer, group: group, expires_at: 1.day.from_now) }
it 'executes user_remove_from_group event webhook when group member is deleted' do
WebMock.stub_request(:post, group_hook.url)
group_member.destroy!
expect(WebMock).to have_requested(:post, group_hook.url).with(
webhook_data(group_member, 'user_remove_from_group')
)
end
end
context 'does not execute webhook' do context 'does not execute webhook' do
before do before do
WebMock.stub_request(:post, group_hook.url) WebMock.stub_request(:post, group_hook.url)
...@@ -319,4 +344,25 @@ RSpec.describe GroupMember do ...@@ -319,4 +344,25 @@ RSpec.describe GroupMember do
end end
end end
end end
def webhook_data(group_member, event)
{
headers: { 'Content-Type' => 'application/json', 'User-Agent' => "GitLab/#{Gitlab::VERSION}", 'X-Gitlab-Event' => 'Member Hook' },
body: {
created_at: group_member.created_at&.xmlschema,
updated_at: group_member.updated_at&.xmlschema,
group_name: group.name,
group_path: group.path,
group_id: group.id,
user_username: group_member.user.username,
user_name: group_member.user.name,
user_email: group_member.user.email,
user_id: group_member.user.id,
group_access: group_member.human_access,
expires_at: group_member.expires_at&.xmlschema,
group_plan: 'gold',
event_name: event
}.to_json
}
end
end end
...@@ -31477,7 +31477,7 @@ msgstr "" ...@@ -31477,7 +31477,7 @@ msgstr ""
msgid "Webhooks|This URL will be triggered when a confidential issue is created/updated/merged" msgid "Webhooks|This URL will be triggered when a confidential issue is created/updated/merged"
msgstr "" msgstr ""
msgid "Webhooks|This URL will be triggered when a member is added to a group" msgid "Webhooks|This URL will be triggered when a group member is created/updated/removed"
msgstr "" msgstr ""
msgid "Webhooks|This URL will be triggered when a merge request is created/updated/merged" msgid "Webhooks|This URL will be triggered when a merge request is created/updated/merged"
......
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