Commit 0a0710a548b0d1f722c0f58b0fef5bbfc71ef831
1 parent
85954621
Exists in
master
and in
4 other branches
Event entity created
Observe issue, merge request, note creation - create event register push event
Showing
16 changed files
with
310 additions
and
104 deletions
Show diff stats
... | ... | @@ -0,0 +1,12 @@ |
1 | +class ActivityObserver < ActiveRecord::Observer | |
2 | + observe :issue, :merge_request, :note | |
3 | + | |
4 | + def after_create(record) | |
5 | + Event.create( | |
6 | + :project => record.project, | |
7 | + :target_id => record.id, | |
8 | + :target_type => record.class.name, | |
9 | + :action => Event.determine_action(record) | |
10 | + ) | |
11 | + end | |
12 | +end | ... | ... |
... | ... | @@ -0,0 +1,36 @@ |
1 | +class Event < ActiveRecord::Base | |
2 | + Created = 1 | |
3 | + Updated = 2 | |
4 | + Closed = 3 | |
5 | + Reopened = 4 | |
6 | + Pushed = 5 | |
7 | + Commented = 6 | |
8 | + | |
9 | + belongs_to :project | |
10 | + belongs_to :target, :polymorphic => true | |
11 | + | |
12 | + serialize :data | |
13 | + | |
14 | + def self.determine_action(record) | |
15 | + if [Issue, MergeRequest].include? record.class | |
16 | + Event::Created | |
17 | + elsif record.kind_of? Note | |
18 | + Event::Commented | |
19 | + end | |
20 | + end | |
21 | +end | |
22 | +# == Schema Information | |
23 | +# | |
24 | +# Table name: events | |
25 | +# | |
26 | +# id :integer not null, primary key | |
27 | +# target_type :string(255) | |
28 | +# target_id :integer | |
29 | +# title :string(255) | |
30 | +# data :text | |
31 | +# project_id :integer | |
32 | +# created_at :datetime not null | |
33 | +# updated_at :datetime not null | |
34 | +# action :integer | |
35 | +# | |
36 | + | ... | ... |
app/models/project.rb
... | ... | @@ -3,6 +3,7 @@ require "grit" |
3 | 3 | class Project < ActiveRecord::Base |
4 | 4 | belongs_to :owner, :class_name => "User" |
5 | 5 | |
6 | + has_many :events, :dependent => :destroy | |
6 | 7 | has_many :merge_requests, :dependent => :destroy |
7 | 8 | has_many :issues, :dependent => :destroy, :order => "position" |
8 | 9 | has_many :users_projects, :dependent => :destroy |
... | ... | @@ -89,6 +90,16 @@ class Project < ActiveRecord::Base |
89 | 90 | [GIT_HOST['host'], code].join("/") |
90 | 91 | end |
91 | 92 | |
93 | + def observe_push(oldrev, newrev, ref) | |
94 | + data = web_hook_data(oldrev, newrev, ref) | |
95 | + | |
96 | + Event.create( | |
97 | + :project => self, | |
98 | + :action => Event::Pushed, | |
99 | + :data => data | |
100 | + ) | |
101 | + end | |
102 | + | |
92 | 103 | def execute_web_hooks(oldrev, newrev, ref) |
93 | 104 | ref_parts = ref.split('/') |
94 | 105 | |
... | ... | @@ -96,6 +107,7 @@ class Project < ActiveRecord::Base |
96 | 107 | return if ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000" |
97 | 108 | |
98 | 109 | data = web_hook_data(oldrev, newrev, ref) |
110 | + | |
99 | 111 | web_hooks.each { |web_hook| web_hook.execute(data) } |
100 | 112 | end |
101 | 113 | |
... | ... | @@ -364,5 +376,6 @@ end |
364 | 376 | # issues_enabled :boolean default(TRUE), not null |
365 | 377 | # wall_enabled :boolean default(TRUE), not null |
366 | 378 | # merge_requests_enabled :boolean default(TRUE), not null |
379 | +# wiki_enabled :boolean default(TRUE), not null | |
367 | 380 | # |
368 | 381 | ... | ... |
app/models/users_project.rb
app/models/wiki.rb
... | ... | @@ -31,3 +31,17 @@ class Wiki < ActiveRecord::Base |
31 | 31 | |
32 | 32 | end |
33 | 33 | end |
34 | +# == Schema Information | |
35 | +# | |
36 | +# Table name: wikis | |
37 | +# | |
38 | +# id :integer not null, primary key | |
39 | +# title :string(255) | |
40 | +# content :text | |
41 | +# project_id :integer | |
42 | +# created_at :datetime not null | |
43 | +# updated_at :datetime not null | |
44 | +# slug :string(255) | |
45 | +# user_id :integer | |
46 | +# | |
47 | + | ... | ... |
app/workers/post_receive.rb
config/application.rb
... | ... | @@ -23,7 +23,7 @@ module Gitlab |
23 | 23 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] |
24 | 24 | |
25 | 25 | # Activate observers that should always be running. |
26 | - config.active_record.observers = :mailer_observer | |
26 | + config.active_record.observers = :mailer_observer, :activity_observer | |
27 | 27 | |
28 | 28 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. |
29 | 29 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. | ... | ... |
... | ... | @@ -0,0 +1,14 @@ |
1 | +class CreateEvents < ActiveRecord::Migration | |
2 | + def change | |
3 | + create_table :events do |t| | |
4 | + t.string :target_type, :null => true | |
5 | + t.integer :target_id, :null => true | |
6 | + | |
7 | + t.string :title, :null => true | |
8 | + t.text :data, :null => true | |
9 | + t.integer :project_id, :null => true | |
10 | + | |
11 | + t.timestamps | |
12 | + end | |
13 | + end | |
14 | +end | ... | ... |
db/schema.rb
... | ... | @@ -11,7 +11,18 @@ |
11 | 11 | # |
12 | 12 | # It's strongly recommended to check this file into your version control system. |
13 | 13 | |
14 | -ActiveRecord::Schema.define(:version => 20120219193300) do | |
14 | +ActiveRecord::Schema.define(:version => 20120228134252) do | |
15 | + | |
16 | + create_table "events", :force => true do |t| | |
17 | + t.string "target_type" | |
18 | + t.integer "target_id" | |
19 | + t.string "title" | |
20 | + t.text "data" | |
21 | + t.integer "project_id" | |
22 | + t.datetime "created_at", :null => false | |
23 | + t.datetime "updated_at", :null => false | |
24 | + t.integer "action" | |
25 | + end | |
15 | 26 | |
16 | 27 | create_table "issues", :force => true do |t| |
17 | 28 | t.string "title" | ... | ... |
spec/factories.rb
... | ... | @@ -32,10 +32,14 @@ end |
32 | 32 | |
33 | 33 | Factory.add(:issue, Issue) do |obj| |
34 | 34 | obj.title = Faker::Lorem.sentence |
35 | + obj.author = Factory :user | |
36 | + obj.assignee = Factory :user | |
35 | 37 | end |
36 | 38 | |
37 | 39 | Factory.add(:merge_request, MergeRequest) do |obj| |
38 | 40 | obj.title = Faker::Lorem.sentence |
41 | + obj.author = Factory :user | |
42 | + obj.assignee = Factory :user | |
39 | 43 | obj.source_branch = "master" |
40 | 44 | obj.target_branch = "master" |
41 | 45 | obj.closed = false |
... | ... | @@ -64,3 +68,8 @@ Factory.add(:wikis, WebHook) do |obj| |
64 | 68 | obj.title = Faker::Lorem.sentence |
65 | 69 | obj.content = Faker::Lorem.sentence |
66 | 70 | end |
71 | + | |
72 | +Factory.add(:event, Event) do |obj| | |
73 | + obj.title = Faker::Lorem.sentence | |
74 | + obj.project = Factory(:project) | |
75 | +end | ... | ... |
... | ... | @@ -0,0 +1,44 @@ |
1 | +require 'spec_helper' | |
2 | + | |
3 | +describe ActivityObserver do | |
4 | + let(:project) { Factory :project } | |
5 | + | |
6 | + def self.it_should_be_valid_event | |
7 | + it { @event.should_not be_nil } | |
8 | + it { @event.project.should == project } | |
9 | + end | |
10 | + | |
11 | + describe "Merge Request created" do | |
12 | + before do | |
13 | + @merge_request = Factory :merge_request, :project => project | |
14 | + @event = Event.last | |
15 | + end | |
16 | + | |
17 | + it_should_be_valid_event | |
18 | + it { @event.action.should == Event::Created } | |
19 | + it { @event.target.should == @merge_request } | |
20 | + end | |
21 | + | |
22 | + describe "Issue created" do | |
23 | + before do | |
24 | + @issue = Factory :issue, :project => project | |
25 | + @event = Event.last | |
26 | + end | |
27 | + | |
28 | + it_should_be_valid_event | |
29 | + it { @event.action.should == Event::Created } | |
30 | + it { @event.target.should == @issue } | |
31 | + end | |
32 | + | |
33 | + describe "Issue commented" do | |
34 | + before do | |
35 | + @issue = Factory :issue, :project => project | |
36 | + @note = Factory :note, :noteable => @issue, :project => project | |
37 | + @event = Event.last | |
38 | + end | |
39 | + | |
40 | + it_should_be_valid_event | |
41 | + it { @event.action.should == Event::Commented } | |
42 | + it { @event.target.should == @note } | |
43 | + end | |
44 | +end | ... | ... |
... | ... | @@ -0,0 +1,32 @@ |
1 | +# == Schema Information | |
2 | +# | |
3 | +# Table name: events | |
4 | +# | |
5 | +# id :integer not null, primary key | |
6 | +# target_type :string(255) | |
7 | +# target_id :integer | |
8 | +# title :string(255) | |
9 | +# data :text | |
10 | +# project_id :integer | |
11 | +# created_at :datetime not null | |
12 | +# updated_at :datetime not null | |
13 | +# action :integer | |
14 | +# | |
15 | + | |
16 | +require 'spec_helper' | |
17 | + | |
18 | +describe Event do | |
19 | + describe "Associations" do | |
20 | + it { should belong_to(:project) } | |
21 | + end | |
22 | + | |
23 | + describe "Creation" do | |
24 | + before do | |
25 | + @event = Factory :event | |
26 | + end | |
27 | + | |
28 | + it "should create a valid event" do | |
29 | + @event.should be_valid | |
30 | + end | |
31 | + end | |
32 | +end | ... | ... |
... | ... | @@ -0,0 +1,115 @@ |
1 | +require 'spec_helper' | |
2 | + | |
3 | +describe Project, "Hooks" do | |
4 | + let(:project) { Factory :project } | |
5 | + | |
6 | + describe "Post Receive Event" do | |
7 | + it "should create push event" do | |
8 | + oldrev, newrev, ref = '00000000000000000000000000000000', 'newrev', 'refs/heads/master' | |
9 | + project.observe_push(oldrev, newrev, ref) | |
10 | + event = Event.last | |
11 | + | |
12 | + event.should_not be_nil | |
13 | + event.project.should == project | |
14 | + event.action.should == Event::Pushed | |
15 | + event.data == project.web_hook_data(oldrev, newrev, ref) | |
16 | + end | |
17 | + end | |
18 | + | |
19 | + describe "Web hooks" do | |
20 | + context "with no web hooks" do | |
21 | + it "raises no errors" do | |
22 | + lambda { | |
23 | + project.execute_web_hooks('oldrev', 'newrev', 'ref') | |
24 | + }.should_not raise_error | |
25 | + end | |
26 | + end | |
27 | + | |
28 | + context "with web hooks" do | |
29 | + before do | |
30 | + @webhook = Factory(:web_hook) | |
31 | + @webhook_2 = Factory(:web_hook) | |
32 | + project.web_hooks << [@webhook, @webhook_2] | |
33 | + end | |
34 | + | |
35 | + it "executes multiple web hook" do | |
36 | + @webhook.should_receive(:execute).once | |
37 | + @webhook_2.should_receive(:execute).once | |
38 | + | |
39 | + project.execute_web_hooks('oldrev', 'newrev', 'refs/heads/master') | |
40 | + end | |
41 | + end | |
42 | + | |
43 | + context "does not execute web hooks" do | |
44 | + before do | |
45 | + @webhook = Factory(:web_hook) | |
46 | + project.web_hooks << [@webhook] | |
47 | + end | |
48 | + | |
49 | + it "when pushing a branch for the first time" do | |
50 | + @webhook.should_not_receive(:execute) | |
51 | + project.execute_web_hooks('00000000000000000000000000000000', 'newrev', 'refs/heads/master') | |
52 | + end | |
53 | + | |
54 | + it "when pushing tags" do | |
55 | + @webhook.should_not_receive(:execute) | |
56 | + project.execute_web_hooks('oldrev', 'newrev', 'refs/tags/v1.0.0') | |
57 | + end | |
58 | + end | |
59 | + | |
60 | + context "when pushing new branches" do | |
61 | + | |
62 | + end | |
63 | + | |
64 | + context "when gathering commit data" do | |
65 | + before do | |
66 | + @oldrev, @newrev, @ref = project.fresh_commits(2).last.sha, project.fresh_commits(2).first.sha, 'refs/heads/master' | |
67 | + @commit = project.fresh_commits(2).first | |
68 | + | |
69 | + # Fill nil/empty attributes | |
70 | + project.description = "This is a description" | |
71 | + | |
72 | + @data = project.web_hook_data(@oldrev, @newrev, @ref) | |
73 | + end | |
74 | + | |
75 | + subject { @data } | |
76 | + | |
77 | + it { should include(before: @oldrev) } | |
78 | + it { should include(after: @newrev) } | |
79 | + it { should include(ref: @ref) } | |
80 | + | |
81 | + context "with repository data" do | |
82 | + subject { @data[:repository] } | |
83 | + | |
84 | + it { should include(name: project.name) } | |
85 | + it { should include(url: project.web_url) } | |
86 | + it { should include(description: project.description) } | |
87 | + it { should include(homepage: project.web_url) } | |
88 | + it { should include(private: project.private?) } | |
89 | + end | |
90 | + | |
91 | + context "with commits" do | |
92 | + subject { @data[:commits] } | |
93 | + | |
94 | + it { should be_an(Array) } | |
95 | + it { should have(1).element } | |
96 | + | |
97 | + context "the commit" do | |
98 | + subject { @data[:commits].first } | |
99 | + | |
100 | + it { should include(id: @commit.id) } | |
101 | + it { should include(message: @commit.safe_message) } | |
102 | + it { should include(timestamp: @commit.date.xmlschema) } | |
103 | + it { should include(url: "http://localhost/#{project.code}/commits/#{@commit.id}") } | |
104 | + | |
105 | + context "with a author" do | |
106 | + subject { @data[:commits].first[:author] } | |
107 | + | |
108 | + it { should include(name: @commit.author_name) } | |
109 | + it { should include(email: @commit.author_email) } | |
110 | + end | |
111 | + end | |
112 | + end | |
113 | + end | |
114 | + end | |
115 | +end | ... | ... |
spec/models/project_spec.rb
... | ... | @@ -2,6 +2,7 @@ require 'spec_helper' |
2 | 2 | |
3 | 3 | describe Project do |
4 | 4 | describe "Associations" do |
5 | + it { should have_many(:events) } | |
5 | 6 | it { should have_many(:users) } |
6 | 7 | it { should have_many(:users_projects) } |
7 | 8 | it { should have_many(:issues) } |
... | ... | @@ -69,106 +70,6 @@ describe Project do |
69 | 70 | end |
70 | 71 | end |
71 | 72 | |
72 | - describe "web hooks" do | |
73 | - let(:project) { Factory :project } | |
74 | - | |
75 | - context "with no web hooks" do | |
76 | - it "raises no errors" do | |
77 | - lambda { | |
78 | - project.execute_web_hooks('oldrev', 'newrev', 'ref') | |
79 | - }.should_not raise_error | |
80 | - end | |
81 | - end | |
82 | - | |
83 | - context "with web hooks" do | |
84 | - before do | |
85 | - @webhook = Factory(:web_hook) | |
86 | - @webhook_2 = Factory(:web_hook) | |
87 | - project.web_hooks << [@webhook, @webhook_2] | |
88 | - end | |
89 | - | |
90 | - it "executes multiple web hook" do | |
91 | - @webhook.should_receive(:execute).once | |
92 | - @webhook_2.should_receive(:execute).once | |
93 | - | |
94 | - project.execute_web_hooks('oldrev', 'newrev', 'refs/heads/master') | |
95 | - end | |
96 | - end | |
97 | - | |
98 | - context "does not execute web hooks" do | |
99 | - before do | |
100 | - @webhook = Factory(:web_hook) | |
101 | - project.web_hooks << [@webhook] | |
102 | - end | |
103 | - | |
104 | - it "when pushing a branch for the first time" do | |
105 | - @webhook.should_not_receive(:execute) | |
106 | - project.execute_web_hooks('00000000000000000000000000000000', 'newrev', 'refs/heads/master') | |
107 | - end | |
108 | - | |
109 | - it "when pushing tags" do | |
110 | - @webhook.should_not_receive(:execute) | |
111 | - project.execute_web_hooks('oldrev', 'newrev', 'refs/tags/v1.0.0') | |
112 | - end | |
113 | - end | |
114 | - | |
115 | - context "when pushing new branches" do | |
116 | - | |
117 | - end | |
118 | - | |
119 | - context "when gathering commit data" do | |
120 | - before do | |
121 | - @oldrev, @newrev, @ref = project.fresh_commits(2).last.sha, project.fresh_commits(2).first.sha, 'refs/heads/master' | |
122 | - @commit = project.fresh_commits(2).first | |
123 | - | |
124 | - # Fill nil/empty attributes | |
125 | - project.description = "This is a description" | |
126 | - | |
127 | - @data = project.web_hook_data(@oldrev, @newrev, @ref) | |
128 | - end | |
129 | - | |
130 | - subject { @data } | |
131 | - | |
132 | - it { should include(before: @oldrev) } | |
133 | - it { should include(after: @newrev) } | |
134 | - it { should include(ref: @ref) } | |
135 | - | |
136 | - context "with repository data" do | |
137 | - subject { @data[:repository] } | |
138 | - | |
139 | - it { should include(name: project.name) } | |
140 | - it { should include(url: project.web_url) } | |
141 | - it { should include(description: project.description) } | |
142 | - it { should include(homepage: project.web_url) } | |
143 | - it { should include(private: project.private?) } | |
144 | - end | |
145 | - | |
146 | - context "with commits" do | |
147 | - subject { @data[:commits] } | |
148 | - | |
149 | - it { should be_an(Array) } | |
150 | - it { should have(1).element } | |
151 | - | |
152 | - context "the commit" do | |
153 | - subject { @data[:commits].first } | |
154 | - | |
155 | - it { should include(id: @commit.id) } | |
156 | - it { should include(message: @commit.safe_message) } | |
157 | - it { should include(timestamp: @commit.date.xmlschema) } | |
158 | - it { should include(url: "http://localhost/#{project.code}/commits/#{@commit.id}") } | |
159 | - | |
160 | - context "with a author" do | |
161 | - subject { @data[:commits].first[:author] } | |
162 | - | |
163 | - it { should include(name: @commit.author_name) } | |
164 | - it { should include(email: @commit.author_email) } | |
165 | - end | |
166 | - end | |
167 | - end | |
168 | - | |
169 | - end | |
170 | - end | |
171 | - | |
172 | 73 | describe "updates" do |
173 | 74 | let(:project) { Factory :project } |
174 | 75 | |
... | ... | @@ -303,5 +204,6 @@ end |
303 | 204 | # issues_enabled :boolean default(TRUE), not null |
304 | 205 | # wall_enabled :boolean default(TRUE), not null |
305 | 206 | # merge_requests_enabled :boolean default(TRUE), not null |
207 | +# wiki_enabled :boolean default(TRUE), not null | |
306 | 208 | # |
307 | 209 | ... | ... |
spec/models/users_project_spec.rb