Commit 4f067ae931bef908312bbf7162abb3b6fdb85f8d
Exists in
master
and in
4 other branches
Merge branch 'feature/event_hooks' of /home/git/repositories/gitlab/gitlabhq
Showing
27 changed files
with
447 additions
and
268 deletions
Show diff stats
CHANGELOG
... | ... | @@ -7,6 +7,7 @@ v 6.4.0 |
7 | 7 | - Side-by-side diff view (Steven Thonus) |
8 | 8 | - Internal projects (Jason Hollingsworth) |
9 | 9 | - Allow removal of avatar (Drew Blessing) |
10 | + - Project web hooks now support issues and merge request events | |
10 | 11 | |
11 | 12 | v 6.3.0 |
12 | 13 | - API for adding gitlab-ci service | ... | ... |
app/assets/stylesheets/gitlab_bootstrap/forms.scss
app/helpers/application_helper.rb
... | ... | @@ -207,4 +207,12 @@ module ApplicationHelper |
207 | 207 | def broadcast_message |
208 | 208 | BroadcastMessage.current |
209 | 209 | end |
210 | + | |
211 | + def highlight_js(&block) | |
212 | + string = capture(&block) | |
213 | + | |
214 | + content_tag :div, class: user_color_scheme_class do | |
215 | + Pygments::Lexer[:js].highlight(string).html_safe | |
216 | + end | |
217 | + end | |
210 | 218 | end | ... | ... |
app/models/concerns/issuable.rb
app/models/project.rb
... | ... | @@ -298,8 +298,10 @@ class Project < ActiveRecord::Base |
298 | 298 | ProjectTransferService.new.transfer(self, new_namespace) |
299 | 299 | end |
300 | 300 | |
301 | - def execute_hooks(data) | |
302 | - hooks.each { |hook| hook.async_execute(data) } | |
301 | + def execute_hooks(data, hooks_scope = :push_hooks) | |
302 | + hooks.send(hooks_scope).each do |hook| | |
303 | + hook.async_execute(data) | |
304 | + end | |
303 | 305 | end |
304 | 306 | |
305 | 307 | def execute_services(data) | ... | ... |
app/models/project_hook.rb
... | ... | @@ -2,15 +2,24 @@ |
2 | 2 | # |
3 | 3 | # Table name: web_hooks |
4 | 4 | # |
5 | -# id :integer not null, primary key | |
6 | -# url :string(255) | |
7 | -# project_id :integer | |
8 | -# created_at :datetime not null | |
9 | -# updated_at :datetime not null | |
10 | -# type :string(255) default("ProjectHook") | |
11 | -# service_id :integer | |
5 | +# id :integer not null, primary key | |
6 | +# url :string(255) | |
7 | +# project_id :integer | |
8 | +# created_at :datetime not null | |
9 | +# updated_at :datetime not null | |
10 | +# type :string(255) default("ProjectHook") | |
11 | +# service_id :integer | |
12 | +# push_events :boolean default(TRUE), not null | |
13 | +# issues_events :boolean default(FALSE), not null | |
14 | +# merge_requests_events :boolean default(FALSE), not null | |
12 | 15 | # |
13 | 16 | |
14 | 17 | class ProjectHook < WebHook |
15 | 18 | belongs_to :project |
19 | + | |
20 | + attr_accessible :push_events, :issues_events, :merge_requests_events | |
21 | + | |
22 | + scope :push_hooks, -> { where(push_events: true) } | |
23 | + scope :issue_hooks, -> { where(issues_events: true) } | |
24 | + scope :merge_request_hooks, -> { where(merge_requests_events: true) } | |
16 | 25 | end | ... | ... |
app/models/service_hook.rb
... | ... | @@ -2,13 +2,16 @@ |
2 | 2 | # |
3 | 3 | # Table name: web_hooks |
4 | 4 | # |
5 | -# id :integer not null, primary key | |
6 | -# url :string(255) | |
7 | -# project_id :integer | |
8 | -# created_at :datetime not null | |
9 | -# updated_at :datetime not null | |
10 | -# type :string(255) default("ProjectHook") | |
11 | -# service_id :integer | |
5 | +# id :integer not null, primary key | |
6 | +# url :string(255) | |
7 | +# project_id :integer | |
8 | +# created_at :datetime not null | |
9 | +# updated_at :datetime not null | |
10 | +# type :string(255) default("ProjectHook") | |
11 | +# service_id :integer | |
12 | +# push_events :boolean default(TRUE), not null | |
13 | +# issues_events :boolean default(FALSE), not null | |
14 | +# merge_requests_events :boolean default(FALSE), not null | |
12 | 15 | # |
13 | 16 | |
14 | 17 | class ServiceHook < WebHook | ... | ... |
app/models/system_hook.rb
... | ... | @@ -2,13 +2,16 @@ |
2 | 2 | # |
3 | 3 | # Table name: web_hooks |
4 | 4 | # |
5 | -# id :integer not null, primary key | |
6 | -# url :string(255) | |
7 | -# project_id :integer | |
8 | -# created_at :datetime not null | |
9 | -# updated_at :datetime not null | |
10 | -# type :string(255) default("ProjectHook") | |
11 | -# service_id :integer | |
5 | +# id :integer not null, primary key | |
6 | +# url :string(255) | |
7 | +# project_id :integer | |
8 | +# created_at :datetime not null | |
9 | +# updated_at :datetime not null | |
10 | +# type :string(255) default("ProjectHook") | |
11 | +# service_id :integer | |
12 | +# push_events :boolean default(TRUE), not null | |
13 | +# issues_events :boolean default(FALSE), not null | |
14 | +# merge_requests_events :boolean default(FALSE), not null | |
12 | 15 | # |
13 | 16 | |
14 | 17 | class SystemHook < WebHook | ... | ... |
app/models/web_hook.rb
... | ... | @@ -2,13 +2,16 @@ |
2 | 2 | # |
3 | 3 | # Table name: web_hooks |
4 | 4 | # |
5 | -# id :integer not null, primary key | |
6 | -# url :string(255) | |
7 | -# project_id :integer | |
8 | -# created_at :datetime not null | |
9 | -# updated_at :datetime not null | |
10 | -# type :string(255) default("ProjectHook") | |
11 | -# service_id :integer | |
5 | +# id :integer not null, primary key | |
6 | +# url :string(255) | |
7 | +# project_id :integer | |
8 | +# created_at :datetime not null | |
9 | +# updated_at :datetime not null | |
10 | +# type :string(255) default("ProjectHook") | |
11 | +# service_id :integer | |
12 | +# push_events :boolean default(TRUE), not null | |
13 | +# issues_events :boolean default(FALSE), not null | |
14 | +# merge_requests_events :boolean default(FALSE), not null | |
12 | 15 | # |
13 | 16 | |
14 | 17 | class WebHook < ActiveRecord::Base | ... | ... |
app/observers/issue_observer.rb
1 | 1 | class IssueObserver < BaseObserver |
2 | 2 | def after_create(issue) |
3 | 3 | notification.new_issue(issue, current_user) |
4 | - | |
5 | 4 | issue.create_cross_references!(issue.project, current_user) |
5 | + execute_hooks(issue) | |
6 | 6 | end |
7 | 7 | |
8 | 8 | def after_close(issue, transition) |
9 | 9 | notification.close_issue(issue, current_user) |
10 | - | |
11 | 10 | create_note(issue) |
11 | + execute_hooks(issue) | |
12 | 12 | end |
13 | 13 | |
14 | 14 | def after_reopen(issue, transition) |
... | ... | @@ -29,4 +29,8 @@ class IssueObserver < BaseObserver |
29 | 29 | def create_note(issue) |
30 | 30 | Note.create_status_change_note(issue, issue.project, current_user, issue.state, current_commit) |
31 | 31 | end |
32 | + | |
33 | + def execute_hooks(issue) | |
34 | + issue.project.execute_hooks(issue.to_hook_data, :issue_hooks) | |
35 | + end | |
32 | 36 | end | ... | ... |
app/observers/merge_request_observer.rb
... | ... | @@ -7,15 +7,15 @@ class MergeRequestObserver < ActivityObserver |
7 | 7 | end |
8 | 8 | |
9 | 9 | notification.new_merge_request(merge_request, current_user) |
10 | - | |
11 | 10 | merge_request.create_cross_references!(merge_request.project, current_user) |
11 | + execute_hooks(merge_request) | |
12 | 12 | end |
13 | 13 | |
14 | 14 | def after_close(merge_request, transition) |
15 | 15 | create_event(merge_request, Event::CLOSED) |
16 | - Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil) | |
17 | - | |
18 | 16 | notification.close_mr(merge_request, current_user) |
17 | + create_note(merge_request) | |
18 | + execute_hooks(merge_request) | |
19 | 19 | end |
20 | 20 | |
21 | 21 | def after_merge(merge_request, transition) |
... | ... | @@ -31,11 +31,13 @@ class MergeRequestObserver < ActivityObserver |
31 | 31 | action: Event::MERGED, |
32 | 32 | author_id: merge_request.author_id_of_changes |
33 | 33 | ) |
34 | + | |
35 | + execute_hooks(merge_request) | |
34 | 36 | end |
35 | 37 | |
36 | 38 | def after_reopen(merge_request, transition) |
37 | 39 | create_event(merge_request, Event::REOPENED) |
38 | - Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil) | |
40 | + create_note(merge_request) | |
39 | 41 | end |
40 | 42 | |
41 | 43 | def after_update(merge_request) |
... | ... | @@ -53,4 +55,17 @@ class MergeRequestObserver < ActivityObserver |
53 | 55 | author_id: current_user.id |
54 | 56 | ) |
55 | 57 | end |
58 | + | |
59 | + private | |
60 | + | |
61 | + # Create merge request note with service comment like 'Status changed to closed' | |
62 | + def create_note(merge_request) | |
63 | + Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil) | |
64 | + end | |
65 | + | |
66 | + def execute_hooks(merge_request) | |
67 | + if merge_request.project | |
68 | + merge_request.project.execute_hooks(merge_request.to_hook_data, :merge_request_hooks) | |
69 | + end | |
70 | + end | |
56 | 71 | end | ... | ... |
app/services/git_push_service.rb
app/views/help/web_hooks.html.haml
1 | 1 | = render layout: 'help/layout' do |
2 | - %h3.page-title Web hooks | |
2 | + %h3.page-title Project web hooks | |
3 | + %p.light | |
4 | + Project web hooks allow you to trigger url if new code is pushed or new issue is created | |
5 | + %hr | |
3 | 6 | |
4 | 7 | %p.slead |
5 | - Every GitLab project can trigger a web server whenever the repo is pushed to. | |
8 | + You can configure web hook to listen for specific events like pushes, issues, merge requests. | |
9 | + %br | |
10 | + GitLab will send POST request with data to web hook url. | |
6 | 11 | %br |
7 | 12 | Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. |
13 | + %hr | |
14 | + | |
15 | + %h4 Push events | |
16 | + %p.light | |
17 | + Triggered when you push to the repository except pushing tags. | |
8 | 18 | %br |
9 | - GitLab will send POST request with commits information on every push. | |
10 | - %h5 Hooks request example: | |
11 | - = render "projects/hooks/data_ex" | |
19 | + Request body: | |
20 | + = highlight_js do | |
21 | + :erb | |
22 | + { | |
23 | + "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", | |
24 | + "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", | |
25 | + "ref": "refs/heads/master", | |
26 | + "user_id": 4, | |
27 | + "user_name": "John Smith", | |
28 | + "project_id": 15, | |
29 | + "repository": { | |
30 | + "name": "Diaspora", | |
31 | + "url": "git@localhost:diaspora.git", | |
32 | + "description": "", | |
33 | + "homepage": "http://localhost/diaspora", | |
34 | + }, | |
35 | + "commits": [ | |
36 | + { | |
37 | + "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", | |
38 | + "message": "Update Catalan translation to e38cb41.", | |
39 | + "timestamp": "2011-12-12T14:27:31+02:00", | |
40 | + "url": "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", | |
41 | + "author": { | |
42 | + "name": "Jordi Mallach", | |
43 | + "email": "jordi@softcatala.org", | |
44 | + } | |
45 | + }, | |
46 | + // ... | |
47 | + { | |
48 | + "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", | |
49 | + "message": "fixed readme", | |
50 | + "timestamp": "2012-01-03T23:36:29+02:00", | |
51 | + "url": "http://localhost/diaspora/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", | |
52 | + "author": { | |
53 | + "name": "GitLab dev user", | |
54 | + "email": "gitlabdev@dv6700.(none)", | |
55 | + }, | |
56 | + }, | |
57 | + ], | |
58 | + "total_commits_count": 4, | |
59 | + }; | |
60 | + | |
12 | 61 | |
62 | + %h4.prepend-top-20 Issues events | |
63 | + %p.light | |
64 | + Triggered when new issue created or existing issue was closed. | |
65 | + %br | |
66 | + Request body: | |
67 | + = highlight_js do | |
68 | + :erb | |
69 | + { | |
70 | + "object_kind":"issue", | |
71 | + "object_attributes":{ | |
72 | + "id":301, | |
73 | + "title":"New API: create/update/delete file", | |
74 | + "assignee_id":51, | |
75 | + "author_id":51, | |
76 | + "project_id":14, | |
77 | + "created_at":"2013-12-03T17:15:43Z", | |
78 | + "updated_at":"2013-12-03T17:15:43Z", | |
79 | + "position":0, | |
80 | + "branch_name":null, | |
81 | + "description":"Create new API for manipulations with repository", | |
82 | + "milestone_id":null, | |
83 | + "state":"opened", | |
84 | + "iid":23 | |
85 | + } | |
86 | + } | |
87 | + %h4.prepend-top-20 Merge request events | |
88 | + %p.light | |
89 | + Triggered when new merge request created or existing merge request was merged/closed. | |
90 | + %br | |
91 | + Request body: | |
92 | + = highlight_js do | |
93 | + :erb | |
94 | + { | |
95 | + "object_kind":"merge_request", | |
96 | + "object_attributes":{ | |
97 | + "id":99, | |
98 | + "target_branch":"master", | |
99 | + "source_branch":"ms-viewport", | |
100 | + "source_project_id":14, | |
101 | + "author_id":51, | |
102 | + "assignee_id":6, | |
103 | + "title":"MS-Viewport", | |
104 | + "created_at":"2013-12-03T17:23:34Z", | |
105 | + "updated_at":"2013-12-03T17:23:34Z", | |
106 | + "st_commits":null, | |
107 | + "st_diffs":null, | |
108 | + "milestone_id":null, | |
109 | + "state":"opened", | |
110 | + "merge_status":"unchecked", | |
111 | + "target_project_id":14, | |
112 | + "iid":1, | |
113 | + "description":"" | |
114 | + } | |
115 | + } | ... | ... |
app/views/projects/hooks/_data_ex.html.erb
... | ... | @@ -1,44 +0,0 @@ |
1 | -<% data_ex_str = <<eos | |
2 | -{ | |
3 | - "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", | |
4 | - "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", | |
5 | - "ref": "refs/heads/master", | |
6 | - "user_id": 4, | |
7 | - "user_name": "John Smith", | |
8 | - "project_id": 15, | |
9 | - "repository": { | |
10 | - "name": "Diaspora", | |
11 | - "url": "git@localhost:diaspora.git", | |
12 | - "description": "", | |
13 | - "homepage": "http://localhost/diaspora", | |
14 | - }, | |
15 | - "commits": [ | |
16 | - { | |
17 | - "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", | |
18 | - "message": "Update Catalan translation to e38cb41.", | |
19 | - "timestamp": "2011-12-12T14:27:31+02:00", | |
20 | - "url": "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", | |
21 | - "author": { | |
22 | - "name": "Jordi Mallach", | |
23 | - "email": "jordi@softcatala.org", | |
24 | - } | |
25 | - }, | |
26 | - // ... | |
27 | - { | |
28 | - "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", | |
29 | - "message": "fixed readme", | |
30 | - "timestamp": "2012-01-03T23:36:29+02:00", | |
31 | - "url": "http://localhost/diaspora/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", | |
32 | - "author": { | |
33 | - "name": "GitLab dev user", | |
34 | - "email": "gitlabdev@dv6700.(none)", | |
35 | - }, | |
36 | - }, | |
37 | - ], | |
38 | - "total_commits_count": 4, | |
39 | -}; | |
40 | -eos | |
41 | -%> | |
42 | -<div class="<%= user_color_scheme_class%>"> | |
43 | - <%= raw Pygments::Lexer[:js].highlight(data_ex_str) %> | |
44 | -</div> |
app/views/projects/hooks/index.html.haml
1 | 1 | %h3.page-title |
2 | - Post-receive hooks | |
2 | + Web hooks | |
3 | 3 | |
4 | 4 | %p.light |
5 | - #{link_to "Post-receive hooks ", help_web_hooks_path, class: "vlink"} can be | |
6 | - used for binding events when someone pushes to the repository. | |
5 | + #{link_to "Web hooks ", help_web_hooks_path, class: "vlink"} can be | |
6 | + used for binding events when something happends to the the project. | |
7 | 7 | |
8 | 8 | %hr.clearfix |
9 | 9 | |
... | ... | @@ -13,23 +13,50 @@ |
13 | 13 | - @hook.errors.full_messages.each do |msg| |
14 | 14 | %p= msg |
15 | 15 | .control-group |
16 | - = f.label :url, "URL:" | |
16 | + = f.label :url, "URL" | |
17 | 17 | .controls |
18 | 18 | = f.text_field :url, class: "text_field input-xxlarge input-xpadding", placeholder: 'http://example.com/trigger-ci.json' |
19 | 19 | |
20 | 20 | = f.submit "Add Web Hook", class: "btn btn-create" |
21 | + .control-group | |
22 | + = f.label :url, "Trigger" | |
23 | + .controls | |
24 | + %div | |
25 | + = f.check_box :push_events, class: 'pull-left' | |
26 | + .prepend-left-20 | |
27 | + = f.label :push_events, class: 'list-label' do | |
28 | + %strong Push events | |
29 | + %p.light | |
30 | + This url will be triggered in case of push to repository | |
31 | + %div | |
32 | + = f.check_box :issues_events, class: 'pull-left' | |
33 | + .prepend-left-20 | |
34 | + = f.label :issues_events, class: 'list-label' do | |
35 | + %strong Issues events | |
36 | + %p.light | |
37 | + This url will be triggered for created issues | |
38 | + %div | |
39 | + = f.check_box :merge_requests_events, class: 'pull-left' | |
40 | + .prepend-left-20 | |
41 | + = f.label :merge_requests_events, class: 'list-label' do | |
42 | + %strong Merge Request events | |
43 | + %p.light | |
44 | + This url will be triggered for created merge requests | |
21 | 45 | %hr |
22 | 46 | |
23 | 47 | -if @hooks.any? |
24 | 48 | .ui-box |
25 | 49 | .title |
26 | - Hooks (#{@hooks.count}) | |
50 | + Web Hooks (#{@hooks.count}) | |
27 | 51 | %ul.well-list |
28 | 52 | - @hooks.each do |hook| |
29 | 53 | %li |
30 | - %span.badge.badge-info POST | |
31 | - → | |
32 | - %span.monospace= hook.url | |
33 | 54 | .pull-right |
34 | 55 | = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn btn-small grouped" |
35 | 56 | = link_to 'Remove', project_hook_path(@project, hook), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove btn-small grouped" |
57 | + .clearfix | |
58 | + %span.monospace= hook.url | |
59 | + %p | |
60 | + - %w(push_events issues_events merge_requests_events).each do |trigger| | |
61 | + - if hook.send(trigger) | |
62 | + %span.label.label-gray= trigger.titleize | ... | ... |
db/migrate/20131202192556_add_event_fields_for_web_hook.rb
0 → 100644
... | ... | @@ -0,0 +1,7 @@ |
1 | +class AddEventFieldsForWebHook < ActiveRecord::Migration | |
2 | + def change | |
3 | + add_column :web_hooks, :push_events, :boolean, default: true, null: false | |
4 | + add_column :web_hooks, :issues_events, :boolean, default: false, null: false | |
5 | + add_column :web_hooks, :merge_requests_events, :boolean, default: false, null: false | |
6 | + end | |
7 | +end | ... | ... |
db/schema.rb
... | ... | @@ -11,7 +11,7 @@ |
11 | 11 | # |
12 | 12 | # It's strongly recommended to check this file into your version control system. |
13 | 13 | |
14 | -ActiveRecord::Schema.define(:version => 20131112220935) do | |
14 | +ActiveRecord::Schema.define(:version => 20131202192556) do | |
15 | 15 | |
16 | 16 | create_table "broadcast_messages", :force => true do |t| |
17 | 17 | t.text "message", :null => false |
... | ... | @@ -334,10 +334,13 @@ ActiveRecord::Schema.define(:version => 20131112220935) do |
334 | 334 | create_table "web_hooks", :force => true do |t| |
335 | 335 | t.string "url" |
336 | 336 | t.integer "project_id" |
337 | - t.datetime "created_at", :null => false | |
338 | - t.datetime "updated_at", :null => false | |
339 | - t.string "type", :default => "ProjectHook" | |
337 | + t.datetime "created_at", :null => false | |
338 | + t.datetime "updated_at", :null => false | |
339 | + t.string "type", :default => "ProjectHook" | |
340 | 340 | t.integer "service_id" |
341 | + t.boolean "push_events", :default => true, :null => false | |
342 | + t.boolean "issues_events", :default => false, :null => false | |
343 | + t.boolean "merge_requests_events", :default => false, :null => false | |
341 | 344 | end |
342 | 345 | |
343 | 346 | add_index "web_hooks", ["project_id"], :name => "index_web_hooks_on_project_id" | ... | ... |
doc/api/projects.md
... | ... | @@ -402,6 +402,10 @@ Parameters: |
402 | 402 | { |
403 | 403 | "id": 1, |
404 | 404 | "url": "http://example.com/hook", |
405 | + "project_id": 3, | |
406 | + "push_events": "true", | |
407 | + "issues_events": "true", | |
408 | + "merge_requests_events": "true", | |
405 | 409 | "created_at": "2012-10-12T17:04:47Z" |
406 | 410 | } |
407 | 411 | ``` |
... | ... | @@ -419,6 +423,9 @@ Parameters: |
419 | 423 | |
420 | 424 | + `id` (required) - The ID or NAME of a project |
421 | 425 | + `url` (required) - The hook URL |
426 | ++ `push_events` - Trigger hook on push events | |
427 | ++ `issues_events` - Trigger hook on issues events | |
428 | ++ `merge_requests_events` - Trigger hook on merge_requests events | |
422 | 429 | |
423 | 430 | |
424 | 431 | ### Edit project hook |
... | ... | @@ -434,6 +441,9 @@ Parameters: |
434 | 441 | + `id` (required) - The ID or NAME of a project |
435 | 442 | + `hook_id` (required) - The ID of a project hook |
436 | 443 | + `url` (required) - The hook URL |
444 | ++ `push_events` - Trigger hook on push events | |
445 | ++ `issues_events` - Trigger hook on issues events | |
446 | ++ `merge_requests_events` - Trigger hook on merge_requests events | |
437 | 447 | |
438 | 448 | |
439 | 449 | ### Delete project hook | ... | ... |
lib/api/entities.rb
... | ... | @@ -24,6 +24,10 @@ module API |
24 | 24 | expose :id, :url, :created_at |
25 | 25 | end |
26 | 26 | |
27 | + class ProjectHook < Hook | |
28 | + expose :project_id, :push_events, :issues_events, :merge_requests_events | |
29 | + end | |
30 | + | |
27 | 31 | class ForkedFromProject < Grape::Entity |
28 | 32 | expose :id |
29 | 33 | expose :name, :name_with_namespace | ... | ... |
lib/api/project_hooks.rb
... | ... | @@ -22,7 +22,7 @@ module API |
22 | 22 | # GET /projects/:id/hooks |
23 | 23 | get ":id/hooks" do |
24 | 24 | @hooks = paginate user_project.hooks |
25 | - present @hooks, with: Entities::Hook | |
25 | + present @hooks, with: Entities::ProjectHook | |
26 | 26 | end |
27 | 27 | |
28 | 28 | # Get a project hook |
... | ... | @@ -34,7 +34,7 @@ module API |
34 | 34 | # GET /projects/:id/hooks/:hook_id |
35 | 35 | get ":id/hooks/:hook_id" do |
36 | 36 | @hook = user_project.hooks.find(params[:hook_id]) |
37 | - present @hook, with: Entities::Hook | |
37 | + present @hook, with: Entities::ProjectHook | |
38 | 38 | end |
39 | 39 | |
40 | 40 | |
... | ... | @@ -47,10 +47,11 @@ module API |
47 | 47 | # POST /projects/:id/hooks |
48 | 48 | post ":id/hooks" do |
49 | 49 | required_attributes! [:url] |
50 | + attrs = attributes_for_keys [:url, :push_events, :issues_events, :merge_requests_events] | |
51 | + @hook = user_project.hooks.new(attrs) | |
50 | 52 | |
51 | - @hook = user_project.hooks.new({"url" => params[:url]}) | |
52 | 53 | if @hook.save |
53 | - present @hook, with: Entities::Hook | |
54 | + present @hook, with: Entities::ProjectHook | |
54 | 55 | else |
55 | 56 | if @hook.errors[:url].present? |
56 | 57 | error!("Invalid url given", 422) |
... | ... | @@ -70,10 +71,10 @@ module API |
70 | 71 | put ":id/hooks/:hook_id" do |
71 | 72 | @hook = user_project.hooks.find(params[:hook_id]) |
72 | 73 | required_attributes! [:url] |
74 | + attrs = attributes_for_keys [:url, :push_events, :issues_events, :merge_requests_events] | |
73 | 75 | |
74 | - attrs = attributes_for_keys [:url] | |
75 | 76 | if @hook.update_attributes attrs |
76 | - present @hook, with: Entities::Hook | |
77 | + present @hook, with: Entities::ProjectHook | |
77 | 78 | else |
78 | 79 | if @hook.errors[:url].present? |
79 | 80 | error!("Invalid url given", 422) | ... | ... |
spec/models/service_hook_spec.rb
... | ... | @@ -2,13 +2,16 @@ |
2 | 2 | # |
3 | 3 | # Table name: web_hooks |
4 | 4 | # |
5 | -# id :integer not null, primary key | |
6 | -# url :string(255) | |
7 | -# project_id :integer | |
8 | -# created_at :datetime not null | |
9 | -# updated_at :datetime not null | |
10 | -# type :string(255) default("ProjectHook") | |
11 | -# service_id :integer | |
5 | +# id :integer not null, primary key | |
6 | +# url :string(255) | |
7 | +# project_id :integer | |
8 | +# created_at :datetime not null | |
9 | +# updated_at :datetime not null | |
10 | +# type :string(255) default("ProjectHook") | |
11 | +# service_id :integer | |
12 | +# push_events :boolean default(TRUE), not null | |
13 | +# issues_events :boolean default(FALSE), not null | |
14 | +# merge_requests_events :boolean default(FALSE), not null | |
12 | 15 | # |
13 | 16 | |
14 | 17 | require "spec_helper" | ... | ... |
spec/models/system_hook_spec.rb
... | ... | @@ -2,13 +2,16 @@ |
2 | 2 | # |
3 | 3 | # Table name: web_hooks |
4 | 4 | # |
5 | -# id :integer not null, primary key | |
6 | -# url :string(255) | |
7 | -# project_id :integer | |
8 | -# created_at :datetime not null | |
9 | -# updated_at :datetime not null | |
10 | -# type :string(255) default("ProjectHook") | |
11 | -# service_id :integer | |
5 | +# id :integer not null, primary key | |
6 | +# url :string(255) | |
7 | +# project_id :integer | |
8 | +# created_at :datetime not null | |
9 | +# updated_at :datetime not null | |
10 | +# type :string(255) default("ProjectHook") | |
11 | +# service_id :integer | |
12 | +# push_events :boolean default(TRUE), not null | |
13 | +# issues_events :boolean default(FALSE), not null | |
14 | +# merge_requests_events :boolean default(FALSE), not null | |
12 | 15 | # |
13 | 16 | |
14 | 17 | require "spec_helper" | ... | ... |
spec/models/web_hook_spec.rb
... | ... | @@ -2,13 +2,16 @@ |
2 | 2 | # |
3 | 3 | # Table name: web_hooks |
4 | 4 | # |
5 | -# id :integer not null, primary key | |
6 | -# url :string(255) | |
7 | -# project_id :integer | |
8 | -# created_at :datetime not null | |
9 | -# updated_at :datetime not null | |
10 | -# type :string(255) default("ProjectHook") | |
11 | -# service_id :integer | |
5 | +# id :integer not null, primary key | |
6 | +# url :string(255) | |
7 | +# project_id :integer | |
8 | +# created_at :datetime not null | |
9 | +# updated_at :datetime not null | |
10 | +# type :string(255) default("ProjectHook") | |
11 | +# service_id :integer | |
12 | +# push_events :boolean default(TRUE), not null | |
13 | +# issues_events :boolean default(FALSE), not null | |
14 | +# merge_requests_events :boolean default(FALSE), not null | |
12 | 15 | # |
13 | 16 | |
14 | 17 | require 'spec_helper' | ... | ... |
spec/observers/merge_request_observer_spec.rb
... | ... | @@ -4,7 +4,7 @@ describe MergeRequestObserver do |
4 | 4 | let(:some_user) { create :user } |
5 | 5 | let(:assignee) { create :user } |
6 | 6 | let(:author) { create :user } |
7 | - let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author) } | |
7 | + let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author).as_null_object } | |
8 | 8 | let(:assigned_mr) { create(:merge_request, assignee: assignee, author: author, target_project: create(:project)) } |
9 | 9 | let(:unassigned_mr) { create(:merge_request, author: author, target_project: create(:project)) } |
10 | 10 | let(:closed_assigned_mr) { create(:closed_merge_request, assignee: assignee, author: author, target_project: create(:project)) } | ... | ... |
... | ... | @@ -0,0 +1,132 @@ |
1 | +require 'spec_helper' | |
2 | + | |
3 | +describe API::API, 'ProjectHooks' do | |
4 | + include ApiHelpers | |
5 | + before(:each) { enable_observers } | |
6 | + after(:each) { disable_observers } | |
7 | + | |
8 | + let(:user) { create(:user) } | |
9 | + let(:user3) { create(:user) } | |
10 | + let!(:project) { create(:project_with_code, creator_id: user.id, namespace: user.namespace) } | |
11 | + let!(:hook) { create(:project_hook, project: project, url: "http://example.com") } | |
12 | + | |
13 | + before do | |
14 | + project.team << [user, :master] | |
15 | + project.team << [user3, :developer] | |
16 | + end | |
17 | + | |
18 | + describe "GET /projects/:id/hooks" do | |
19 | + context "authorized user" do | |
20 | + it "should return project hooks" do | |
21 | + get api("/projects/#{project.id}/hooks", user) | |
22 | + response.status.should == 200 | |
23 | + | |
24 | + json_response.should be_an Array | |
25 | + json_response.count.should == 1 | |
26 | + json_response.first['url'].should == "http://example.com" | |
27 | + end | |
28 | + end | |
29 | + | |
30 | + context "unauthorized user" do | |
31 | + it "should not access project hooks" do | |
32 | + get api("/projects/#{project.id}/hooks", user3) | |
33 | + response.status.should == 403 | |
34 | + end | |
35 | + end | |
36 | + end | |
37 | + | |
38 | + describe "GET /projects/:id/hooks/:hook_id" do | |
39 | + context "authorized user" do | |
40 | + it "should return a project hook" do | |
41 | + get api("/projects/#{project.id}/hooks/#{hook.id}", user) | |
42 | + response.status.should == 200 | |
43 | + json_response['url'].should == hook.url | |
44 | + end | |
45 | + | |
46 | + it "should return a 404 error if hook id is not available" do | |
47 | + get api("/projects/#{project.id}/hooks/1234", user) | |
48 | + response.status.should == 404 | |
49 | + end | |
50 | + end | |
51 | + | |
52 | + context "unauthorized user" do | |
53 | + it "should not access an existing hook" do | |
54 | + get api("/projects/#{project.id}/hooks/#{hook.id}", user3) | |
55 | + response.status.should == 403 | |
56 | + end | |
57 | + end | |
58 | + | |
59 | + it "should return a 404 error if hook id is not available" do | |
60 | + get api("/projects/#{project.id}/hooks/1234", user) | |
61 | + response.status.should == 404 | |
62 | + end | |
63 | + end | |
64 | + | |
65 | + describe "POST /projects/:id/hooks" do | |
66 | + it "should add hook to project" do | |
67 | + expect { | |
68 | + post api("/projects/#{project.id}/hooks", user), | |
69 | + url: "http://example.com", issues_events: true | |
70 | + }.to change {project.hooks.count}.by(1) | |
71 | + response.status.should == 201 | |
72 | + end | |
73 | + | |
74 | + it "should return a 400 error if url not given" do | |
75 | + post api("/projects/#{project.id}/hooks", user) | |
76 | + response.status.should == 400 | |
77 | + end | |
78 | + | |
79 | + it "should return a 422 error if url not valid" do | |
80 | + post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com" | |
81 | + response.status.should == 422 | |
82 | + end | |
83 | + end | |
84 | + | |
85 | + describe "PUT /projects/:id/hooks/:hook_id" do | |
86 | + it "should update an existing project hook" do | |
87 | + put api("/projects/#{project.id}/hooks/#{hook.id}", user), | |
88 | + url: 'http://example.org', push_events: false | |
89 | + response.status.should == 200 | |
90 | + json_response['url'].should == 'http://example.org' | |
91 | + end | |
92 | + | |
93 | + it "should return 404 error if hook id not found" do | |
94 | + put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org' | |
95 | + response.status.should == 404 | |
96 | + end | |
97 | + | |
98 | + it "should return 400 error if url is not given" do | |
99 | + put api("/projects/#{project.id}/hooks/#{hook.id}", user) | |
100 | + response.status.should == 400 | |
101 | + end | |
102 | + | |
103 | + it "should return a 422 error if url is not valid" do | |
104 | + put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com' | |
105 | + response.status.should == 422 | |
106 | + end | |
107 | + end | |
108 | + | |
109 | + describe "DELETE /projects/:id/hooks/:hook_id" do | |
110 | + it "should delete hook from project" do | |
111 | + expect { | |
112 | + delete api("/projects/#{project.id}/hooks/#{hook.id}", user) | |
113 | + }.to change {project.hooks.count}.by(-1) | |
114 | + response.status.should == 200 | |
115 | + end | |
116 | + | |
117 | + it "should return success when deleting hook" do | |
118 | + delete api("/projects/#{project.id}/hooks/#{hook.id}", user) | |
119 | + response.status.should == 200 | |
120 | + end | |
121 | + | |
122 | + it "should return success when deleting non existent hook" do | |
123 | + delete api("/projects/#{project.id}/hooks/42", user) | |
124 | + response.status.should == 200 | |
125 | + end | |
126 | + | |
127 | + it "should return a 405 error if hook id not given" do | |
128 | + delete api("/projects/#{project.id}/hooks", user) | |
129 | + response.status.should == 405 | |
130 | + end | |
131 | + end | |
132 | +end | ... | ... |
spec/requests/api/projects_spec.rb
... | ... | @@ -10,7 +10,6 @@ describe API::API do |
10 | 10 | let(:user3) { create(:user) } |
11 | 11 | let(:admin) { create(:admin) } |
12 | 12 | let!(:project) { create(:project_with_code, creator_id: user.id, namespace: user.namespace) } |
13 | - let!(:hook) { create(:project_hook, project: project, url: "http://example.com") } | |
14 | 13 | let!(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') } |
15 | 14 | let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } |
16 | 15 | let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) } |
... | ... | @@ -439,121 +438,6 @@ describe API::API do |
439 | 438 | end |
440 | 439 | end |
441 | 440 | |
442 | - describe "GET /projects/:id/hooks" do | |
443 | - context "authorized user" do | |
444 | - it "should return project hooks" do | |
445 | - get api("/projects/#{project.id}/hooks", user) | |
446 | - response.status.should == 200 | |
447 | - | |
448 | - json_response.should be_an Array | |
449 | - json_response.count.should == 1 | |
450 | - json_response.first['url'].should == "http://example.com" | |
451 | - end | |
452 | - end | |
453 | - | |
454 | - context "unauthorized user" do | |
455 | - it "should not access project hooks" do | |
456 | - get api("/projects/#{project.id}/hooks", user3) | |
457 | - response.status.should == 403 | |
458 | - end | |
459 | - end | |
460 | - end | |
461 | - | |
462 | - describe "GET /projects/:id/hooks/:hook_id" do | |
463 | - context "authorized user" do | |
464 | - it "should return a project hook" do | |
465 | - get api("/projects/#{project.id}/hooks/#{hook.id}", user) | |
466 | - response.status.should == 200 | |
467 | - json_response['url'].should == hook.url | |
468 | - end | |
469 | - | |
470 | - it "should return a 404 error if hook id is not available" do | |
471 | - get api("/projects/#{project.id}/hooks/1234", user) | |
472 | - response.status.should == 404 | |
473 | - end | |
474 | - end | |
475 | - | |
476 | - context "unauthorized user" do | |
477 | - it "should not access an existing hook" do | |
478 | - get api("/projects/#{project.id}/hooks/#{hook.id}", user3) | |
479 | - response.status.should == 403 | |
480 | - end | |
481 | - end | |
482 | - | |
483 | - it "should return a 404 error if hook id is not available" do | |
484 | - get api("/projects/#{project.id}/hooks/1234", user) | |
485 | - response.status.should == 404 | |
486 | - end | |
487 | - end | |
488 | - | |
489 | - describe "POST /projects/:id/hooks" do | |
490 | - it "should add hook to project" do | |
491 | - expect { | |
492 | - post api("/projects/#{project.id}/hooks", user), | |
493 | - url: "http://example.com" | |
494 | - }.to change {project.hooks.count}.by(1) | |
495 | - response.status.should == 201 | |
496 | - end | |
497 | - | |
498 | - it "should return a 400 error if url not given" do | |
499 | - post api("/projects/#{project.id}/hooks", user) | |
500 | - response.status.should == 400 | |
501 | - end | |
502 | - | |
503 | - it "should return a 422 error if url not valid" do | |
504 | - post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com" | |
505 | - response.status.should == 422 | |
506 | - end | |
507 | - end | |
508 | - | |
509 | - describe "PUT /projects/:id/hooks/:hook_id" do | |
510 | - it "should update an existing project hook" do | |
511 | - put api("/projects/#{project.id}/hooks/#{hook.id}", user), | |
512 | - url: 'http://example.org' | |
513 | - response.status.should == 200 | |
514 | - json_response['url'].should == 'http://example.org' | |
515 | - end | |
516 | - | |
517 | - it "should return 404 error if hook id not found" do | |
518 | - put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org' | |
519 | - response.status.should == 404 | |
520 | - end | |
521 | - | |
522 | - it "should return 400 error if url is not given" do | |
523 | - put api("/projects/#{project.id}/hooks/#{hook.id}", user) | |
524 | - response.status.should == 400 | |
525 | - end | |
526 | - | |
527 | - it "should return a 422 error if url is not valid" do | |
528 | - put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com' | |
529 | - response.status.should == 422 | |
530 | - end | |
531 | - end | |
532 | - | |
533 | - describe "DELETE /projects/:id/hooks/:hook_id" do | |
534 | - it "should delete hook from project" do | |
535 | - expect { | |
536 | - delete api("/projects/#{project.id}/hooks/#{hook.id}", user) | |
537 | - }.to change {project.hooks.count}.by(-1) | |
538 | - response.status.should == 200 | |
539 | - end | |
540 | - | |
541 | - it "should return success when deleting hook" do | |
542 | - delete api("/projects/#{project.id}/hooks/#{hook.id}", user) | |
543 | - response.status.should == 200 | |
544 | - end | |
545 | - | |
546 | - it "should return success when deleting non existent hook" do | |
547 | - delete api("/projects/#{project.id}/hooks/42", user) | |
548 | - response.status.should == 200 | |
549 | - end | |
550 | - | |
551 | - it "should return a 405 error if hook id not given" do | |
552 | - delete api("/projects/#{project.id}/hooks", user) | |
553 | - response.status.should == 405 | |
554 | - end | |
555 | - end | |
556 | - | |
557 | 441 | describe "GET /projects/:id/snippets" do |
558 | 442 | it "should return an array of project snippets" do |
559 | 443 | get api("/projects/#{project.id}/snippets", user) | ... | ... |
spec/services/git_push_service_spec.rb
... | ... | @@ -74,38 +74,19 @@ describe GitPushService do |
74 | 74 | end |
75 | 75 | |
76 | 76 | describe "Web Hooks" do |
77 | - context "with web hooks" do | |
78 | - before do | |
79 | - @project_hook = create(:project_hook) | |
80 | - @project_hook_2 = create(:project_hook) | |
81 | - project.hooks << [@project_hook, @project_hook_2] | |
82 | - | |
83 | - stub_request(:post, @project_hook.url) | |
84 | - stub_request(:post, @project_hook_2.url) | |
85 | - end | |
86 | - | |
87 | - it "executes multiple web hook" do | |
88 | - @project_hook.should_receive(:async_execute).once | |
89 | - @project_hook_2.should_receive(:async_execute).once | |
90 | - | |
91 | - service.execute(project, user, @oldrev, @newrev, @ref) | |
92 | - end | |
93 | - end | |
94 | - | |
95 | 77 | context "execute web hooks" do |
96 | - before do | |
97 | - @project_hook = create(:project_hook) | |
98 | - project.hooks << [@project_hook] | |
99 | - stub_request(:post, @project_hook.url) | |
100 | - end | |
101 | - | |
102 | 78 | it "when pushing a branch for the first time" do |
103 | - @project_hook.should_receive(:async_execute) | |
79 | + project.should_receive(:execute_hooks) | |
104 | 80 | service.execute(project, user, @blankrev, 'newrev', 'refs/heads/master') |
105 | 81 | end |
106 | 82 | |
83 | + it "when pushing new commits to existing branch" do | |
84 | + project.should_receive(:execute_hooks) | |
85 | + service.execute(project, user, 'oldrev', 'newrev', 'refs/heads/master') | |
86 | + end | |
87 | + | |
107 | 88 | it "when pushing tags" do |
108 | - @project_hook.should_not_receive(:async_execute) | |
89 | + project.should_not_receive(:execute_hooks) | |
109 | 90 | service.execute(project, user, 'newrev', 'newrev', 'refs/tags/v1.0.0') |
110 | 91 | end |
111 | 92 | end | ... | ... |