Read about our upcoming Code of Conduct on this issue

Commit acac7889 authored by bugagazavr's avatar bugagazavr Committed by Kirill Zaitsev
Browse files

Added X-GitLab-Event header for web hooks

parent 3308330db8be
...@@ -23,6 +23,7 @@ v 7.11.0 (unreleased) ...@@ -23,6 +23,7 @@ v 7.11.0 (unreleased)
- Fix bug causing `@whatever` inside an inline code snippet (backtick-style) to be picked up as a user mention. - Fix bug causing `@whatever` inside an inline code snippet (backtick-style) to be picked up as a user mention.
- When use change branches link at MR form - save source branch selection instead of target one - When use change branches link at MR form - save source branch selection instead of target one
- Improve handling of large diffs - Improve handling of large diffs
- Added GitLab Event header for project hooks
- -
- Show Atom feed buttons everywhere where applicable. - Show Atom feed buttons everywhere where applicable.
- Add project activity atom feed. - Add project activity atom feed.
......
...@@ -33,7 +33,7 @@ def test ...@@ -33,7 +33,7 @@ def test
owner_name: "Someone", owner_name: "Someone",
owner_email: "example@gitlabhq.com" owner_email: "example@gitlabhq.com"
} }
@hook.execute(data) @hook.execute(data, 'system_hooks')
redirect_to :back redirect_to :back
end end
......
...@@ -17,4 +17,8 @@ ...@@ -17,4 +17,8 @@
class ServiceHook < WebHook class ServiceHook < WebHook
belongs_to :service belongs_to :service
def execute(data)
super(data, 'service_hook')
end
end end
...@@ -30,12 +30,15 @@ class WebHook < ActiveRecord::Base ...@@ -30,12 +30,15 @@ class WebHook < ActiveRecord::Base
validates :url, presence: true, validates :url, presence: true,
format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" } format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }
def execute(data) def execute(data, hook_name)
parsed_url = URI.parse(url) parsed_url = URI.parse(url)
if parsed_url.userinfo.blank? if parsed_url.userinfo.blank?
WebHook.post(url, WebHook.post(url,
body: data.to_json, body: data.to_json,
headers: { "Content-Type" => "application/json" }, headers: {
"Content-Type" => "application/json",
"X-Gitlab-Event" => hook_name.singularize.titleize
},
verify: false) verify: false)
else else
post_url = url.gsub("#{parsed_url.userinfo}@", "") post_url = url.gsub("#{parsed_url.userinfo}@", "")
...@@ -45,7 +48,10 @@ def execute(data) ...@@ -45,7 +48,10 @@ def execute(data)
} }
WebHook.post(post_url, WebHook.post(post_url,
body: data.to_json, body: data.to_json,
headers: { "Content-Type" => "application/json" }, headers: {
"Content-Type" => "application/json",
"X-Gitlab-Event" => hook_name.singularize.titleize
},
verify: false, verify: false,
basic_auth: auth) basic_auth: auth)
end end
...@@ -54,7 +60,7 @@ def execute(data) ...@@ -54,7 +60,7 @@ def execute(data)
false false
end end
def async_execute(data) def async_execute(data, hook_name)
Sidekiq::Client.enqueue(ProjectWebHookWorker, id, data) Sidekiq::Client.enqueue(ProjectWebHookWorker, id, data, hook_name)
end end
end end
...@@ -483,7 +483,7 @@ def path_with_namespace ...@@ -483,7 +483,7 @@ def path_with_namespace
def execute_hooks(data, hooks_scope = :push_hooks) def execute_hooks(data, hooks_scope = :push_hooks)
hooks.send(hooks_scope).each do |hook| hooks.send(hooks_scope).each do |hook|
hook.async_execute(data) hook.async_execute(data, hooks_scope.to_s)
end end
end end
......
...@@ -7,12 +7,12 @@ def execute_hooks_for(model, event) ...@@ -7,12 +7,12 @@ def execute_hooks_for(model, event)
def execute_hooks(data) def execute_hooks(data)
SystemHook.all.each do |sh| SystemHook.all.each do |sh|
async_execute_hook sh, data async_execute_hook(sh, data, 'system_hooks')
end end
end end
def async_execute_hook(hook, data) def async_execute_hook(hook, data, hook_name)
Sidekiq::Client.enqueue(SystemHookWorker, hook.id, data) Sidekiq::Client.enqueue(SystemHookWorker, hook.id, data, hook_name)
end end
def build_event_data(model, event) def build_event_data(model, event)
......
class TestHookService class TestHookService
def execute(hook, current_user) def execute(hook, current_user)
data = Gitlab::PushDataBuilder.build_sample(hook.project, current_user) data = Gitlab::PushDataBuilder.build_sample(hook.project, current_user)
hook.execute(data) hook.execute(data, 'push_hooks')
end end
end end
...@@ -3,8 +3,8 @@ class ProjectWebHookWorker ...@@ -3,8 +3,8 @@ class ProjectWebHookWorker
sidekiq_options queue: :project_web_hook sidekiq_options queue: :project_web_hook
def perform(hook_id, data) def perform(hook_id, data, hook_name)
data = data.with_indifferent_access data = data.with_indifferent_access
WebHook.find(hook_id).execute(data) WebHook.find(hook_id).execute(data, hook_name)
end end
end end
...@@ -3,7 +3,7 @@ class SystemHookWorker ...@@ -3,7 +3,7 @@ class SystemHookWorker
sidekiq_options queue: :system_hook sidekiq_options queue: :system_hook
def perform(hook_id, data) def perform(hook_id, data, hook_name)
SystemHook.find(hook_id).execute data SystemHook.find(hook_id).execute(data, hook_name)
end end
end end
...@@ -6,6 +6,12 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser ...@@ -6,6 +6,12 @@ System hooks can be used, e.g. for logging or changing information in a LDAP ser
## Hooks request example ## Hooks request example
**Request header**:
```
X-Gitlab-Event: System Hook
```
**Project created:** **Project created:**
```json ```json
......
...@@ -12,6 +12,12 @@ If you send a web hook to an SSL endpoint [the certificate will not be verified] ...@@ -12,6 +12,12 @@ If you send a web hook to an SSL endpoint [the certificate will not be verified]
Triggered when you push to the repository except when pushing tags. Triggered when you push to the repository except when pushing tags.
**Request header**:
```
X-Gitlab-Event: Push Hook
```
**Request body:** **Request body:**
```json ```json
...@@ -63,6 +69,13 @@ Triggered when you push to the repository except when pushing tags. ...@@ -63,6 +69,13 @@ Triggered when you push to the repository except when pushing tags.
Triggered when you create (or delete) tags to the repository. Triggered when you create (or delete) tags to the repository.
**Request header**:
```
X-Gitlab-Event: Tag Push Hook
```
**Request body:** **Request body:**
```json ```json
...@@ -92,6 +105,12 @@ Triggered when you create (or delete) tags to the repository. ...@@ -92,6 +105,12 @@ Triggered when you create (or delete) tags to the repository.
Triggered when a new issue is created or an existing issue was updated/closed/reopened. Triggered when a new issue is created or an existing issue was updated/closed/reopened.
**Request header**:
```
X-Gitlab-Event: Issue Hook
```
**Request body:** **Request body:**
```json ```json
...@@ -126,6 +145,12 @@ Triggered when a new issue is created or an existing issue was updated/closed/re ...@@ -126,6 +145,12 @@ Triggered when a new issue is created or an existing issue was updated/closed/re
Triggered when a new merge request is created or an existing merge request was updated/merged/closed. Triggered when a new merge request is created or an existing merge request was updated/merged/closed.
**Request header**:
```
X-Gitlab-Event: Merge Request Hook
```
**Request body:** **Request body:**
```json ```json
......
...@@ -47,7 +47,7 @@ class SystemHooks < Grape::API ...@@ -47,7 +47,7 @@ class SystemHooks < Grape::API
owner_name: "Someone", owner_name: "Someone",
owner_email: "example@gitlabhq.com" owner_email: "example@gitlabhq.com"
} }
@hook.execute(data) @hook.execute(data, 'system_hooks')
data data
end end
......
...@@ -21,4 +21,37 @@ ...@@ -21,4 +21,37 @@
describe "Associations" do describe "Associations" do
it { is_expected.to belong_to :service } it { is_expected.to belong_to :service }
end end
describe "execute" do
before(:each) do
@service_hook = create(:service_hook)
@data = { project_id: 1, data: {}}
WebMock.stub_request(:post, @service_hook.url)
end
it "POSTs to the web hook URL" do
@service_hook.execute(@data)
expect(WebMock).to have_requested(:post, @service_hook.url).with(
headers: {'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Service Hook'}
).once
end
it "POSTs the data as JSON" do
json = @data.to_json
@service_hook.execute(@data)
expect(WebMock).to have_requested(:post, @service_hook.url).with(
headers: {'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Service Hook'}
).once
end
it "catches exceptions" do
expect(WebHook).to receive(:post).and_raise("Some HTTP Post error")
expect {
@service_hook.execute(@data)
}.to raise_error
end
end
end end
...@@ -26,32 +26,47 @@ ...@@ -26,32 +26,47 @@
it "project_create hook" do it "project_create hook" do
Projects::CreateService.new(create(:user), name: 'empty').execute Projects::CreateService.new(create(:user), name: 'empty').execute
expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /project_create/).once expect(WebMock).to have_requested(:post, @system_hook.url).with(
body: /project_create/,
headers: {'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook'}
).once
end end
it "project_destroy hook" do it "project_destroy hook" do
user = create(:user) user = create(:user)
project = create(:empty_project, namespace: user.namespace) project = create(:empty_project, namespace: user.namespace)
Projects::DestroyService.new(project, user, {}).execute Projects::DestroyService.new(project, user, {}).execute
expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /project_destroy/).once expect(WebMock).to have_requested(:post, @system_hook.url).with(
body: /project_destroy/,
headers: {'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook'}
).once
end end
it "user_create hook" do it "user_create hook" do
create(:user) create(:user)
expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /user_create/).once expect(WebMock).to have_requested(:post, @system_hook.url).with(
body: /user_create/,
headers: {'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook'}
).once
end end
it "user_destroy hook" do it "user_destroy hook" do
user = create(:user) user = create(:user)
user.destroy user.destroy
expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /user_destroy/).once expect(WebMock).to have_requested(:post, @system_hook.url).with(
body: /user_destroy/,
headers: {'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook'}
).once
end end
it "project_create hook" do it "project_create hook" do
user = create(:user) user = create(:user)
project = create(:project) project = create(:project)
project.team << [user, :master] project.team << [user, :master]
expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /user_add_to_team/).once expect(WebMock).to have_requested(:post, @system_hook.url).with(
body: /user_add_to_team/,
headers: {'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook'}
).once
end end
it "project_destroy hook" do it "project_destroy hook" do
...@@ -59,13 +74,17 @@ ...@@ -59,13 +74,17 @@
project = create(:project) project = create(:project)
project.team << [user, :master] project.team << [user, :master]
project.project_members.destroy_all project.project_members.destroy_all
expect(WebMock).to have_requested(:post, @system_hook.url).with(body: /user_remove_from_team/).once expect(WebMock).to have_requested(:post, @system_hook.url).with(
body: /user_remove_from_team/,
headers: {'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook'}
).once
end end
it 'group create hook' do it 'group create hook' do
create(:group) create(:group)
expect(WebMock).to have_requested(:post, @system_hook.url).with( expect(WebMock).to have_requested(:post, @system_hook.url).with(
body: /group_create/ body: /group_create/,
headers: {'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook'}
).once ).once
end end
...@@ -73,7 +92,8 @@ ...@@ -73,7 +92,8 @@
group = create(:group) group = create(:group)
group.destroy group.destroy
expect(WebMock).to have_requested(:post, @system_hook.url).with( expect(WebMock).to have_requested(:post, @system_hook.url).with(
body: /group_destroy/ body: /group_destroy/,
headers: {'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook'}
).once ).once
end end
...@@ -82,7 +102,8 @@ ...@@ -82,7 +102,8 @@
user = create(:user) user = create(:user)
group.add_user(user, Gitlab::Access::MASTER) group.add_user(user, Gitlab::Access::MASTER)
expect(WebMock).to have_requested(:post, @system_hook.url).with( expect(WebMock).to have_requested(:post, @system_hook.url).with(
body: /user_add_to_group/ body: /user_add_to_group/,
headers: {'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook'}
).once ).once
end end
...@@ -92,7 +113,8 @@ ...@@ -92,7 +113,8 @@
group.add_user(user, Gitlab::Access::MASTER) group.add_user(user, Gitlab::Access::MASTER)
group.group_members.destroy_all group.group_members.destroy_all
expect(WebMock).to have_requested(:post, @system_hook.url).with( expect(WebMock).to have_requested(:post, @system_hook.url).with(
body: /user_remove_from_group/ body: /user_remove_from_group/,
headers: {'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook'}
).once ).once
end end
......
...@@ -52,22 +52,26 @@ ...@@ -52,22 +52,26 @@
end end
it "POSTs to the web hook URL" do it "POSTs to the web hook URL" do
@project_hook.execute(@data) @project_hook.execute(@data, 'push_hooks')
expect(WebMock).to have_requested(:post, @project_hook.url).once expect(WebMock).to have_requested(:post, @project_hook.url).with(
headers: {'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Push Hook'}
).once
end end
it "POSTs the data as JSON" do it "POSTs the data as JSON" do
json = @data.to_json json = @data.to_json
@project_hook.execute(@data) @project_hook.execute(@data, 'push_hooks')
expect(WebMock).to have_requested(:post, @project_hook.url).with(body: json).once expect(WebMock).to have_requested(:post, @project_hook.url).with(
headers: {'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Push Hook'}
).once
end end
it "catches exceptions" do it "catches exceptions" do
expect(WebHook).to receive(:post).and_raise("Some HTTP Post error") expect(WebHook).to receive(:post).and_raise("Some HTTP Post error")
expect { expect {
@project_hook.execute(@data) @project_hook.execute(@data, 'push_hooks')
}.to raise_error }.to raise_error
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