Commit 0a0710a548b0d1f722c0f58b0fef5bbfc71ef831

Authored by Dmitriy Zaporozhets
1 parent 85954621

Event entity created

Observe issue, merge request, note creation - create event
register push event
app/models/activity_observer.rb 0 → 100644
@@ -0,0 +1,12 @@ @@ -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
app/models/event.rb 0 → 100644
@@ -0,0 +1,36 @@ @@ -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 &quot;grit&quot; @@ -3,6 +3,7 @@ require &quot;grit&quot;
3 class Project < ActiveRecord::Base 3 class Project < ActiveRecord::Base
4 belongs_to :owner, :class_name => "User" 4 belongs_to :owner, :class_name => "User"
5 5
  6 + has_many :events, :dependent => :destroy
6 has_many :merge_requests, :dependent => :destroy 7 has_many :merge_requests, :dependent => :destroy
7 has_many :issues, :dependent => :destroy, :order => "position" 8 has_many :issues, :dependent => :destroy, :order => "position"
8 has_many :users_projects, :dependent => :destroy 9 has_many :users_projects, :dependent => :destroy
@@ -89,6 +90,16 @@ class Project &lt; ActiveRecord::Base @@ -89,6 +90,16 @@ class Project &lt; ActiveRecord::Base
89 [GIT_HOST['host'], code].join("/") 90 [GIT_HOST['host'], code].join("/")
90 end 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 def execute_web_hooks(oldrev, newrev, ref) 103 def execute_web_hooks(oldrev, newrev, ref)
93 ref_parts = ref.split('/') 104 ref_parts = ref.split('/')
94 105
@@ -96,6 +107,7 @@ class Project &lt; ActiveRecord::Base @@ -96,6 +107,7 @@ class Project &lt; ActiveRecord::Base
96 return if ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000" 107 return if ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000"
97 108
98 data = web_hook_data(oldrev, newrev, ref) 109 data = web_hook_data(oldrev, newrev, ref)
  110 +
99 web_hooks.each { |web_hook| web_hook.execute(data) } 111 web_hooks.each { |web_hook| web_hook.execute(data) }
100 end 112 end
101 113
@@ -364,5 +376,6 @@ end @@ -364,5 +376,6 @@ end
364 # issues_enabled :boolean default(TRUE), not null 376 # issues_enabled :boolean default(TRUE), not null
365 # wall_enabled :boolean default(TRUE), not null 377 # wall_enabled :boolean default(TRUE), not null
366 # merge_requests_enabled :boolean default(TRUE), not null 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
@@ -80,7 +80,6 @@ end @@ -80,7 +80,6 @@ end
80 # project_id :integer not null 80 # project_id :integer not null
81 # created_at :datetime 81 # created_at :datetime
82 # updated_at :datetime 82 # updated_at :datetime
83 -# repo_access :integer default(0), not null  
84 # project_access :integer default(0), not null 83 # project_access :integer default(0), not null
85 # 84 #
86 85
app/models/wiki.rb
@@ -31,3 +31,17 @@ class Wiki &lt; ActiveRecord::Base @@ -31,3 +31,17 @@ class Wiki &lt; ActiveRecord::Base
31 31
32 end 32 end
33 end 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
@@ -5,6 +5,7 @@ class PostReceive @@ -5,6 +5,7 @@ class PostReceive
5 project = Project.find_by_path(reponame) 5 project = Project.find_by_path(reponame)
6 return false if project.nil? 6 return false if project.nil?
7 7
  8 + project.observe_push(oldrev, newrev, ref)
8 project.execute_web_hooks(oldrev, newrev, ref) 9 project.execute_web_hooks(oldrev, newrev, ref)
9 end 10 end
10 end 11 end
config/application.rb
@@ -23,7 +23,7 @@ module Gitlab @@ -23,7 +23,7 @@ module Gitlab
23 # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 23 # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
24 24
25 # Activate observers that should always be running. 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 # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 28 # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
29 # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 29 # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
db/migrate/20120228130210_create_events.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -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/migrate/20120228134252_add_action_to_event.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddActionToEvent < ActiveRecord::Migration
  2 + def change
  3 + add_column :events, :action, :integer, :null => true
  4 + end
  5 +end
@@ -11,7 +11,18 @@ @@ -11,7 +11,18 @@
11 # 11 #
12 # It's strongly recommended to check this file into your version control system. 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 create_table "issues", :force => true do |t| 27 create_table "issues", :force => true do |t|
17 t.string "title" 28 t.string "title"
spec/factories.rb
@@ -32,10 +32,14 @@ end @@ -32,10 +32,14 @@ end
32 32
33 Factory.add(:issue, Issue) do |obj| 33 Factory.add(:issue, Issue) do |obj|
34 obj.title = Faker::Lorem.sentence 34 obj.title = Faker::Lorem.sentence
  35 + obj.author = Factory :user
  36 + obj.assignee = Factory :user
35 end 37 end
36 38
37 Factory.add(:merge_request, MergeRequest) do |obj| 39 Factory.add(:merge_request, MergeRequest) do |obj|
38 obj.title = Faker::Lorem.sentence 40 obj.title = Faker::Lorem.sentence
  41 + obj.author = Factory :user
  42 + obj.assignee = Factory :user
39 obj.source_branch = "master" 43 obj.source_branch = "master"
40 obj.target_branch = "master" 44 obj.target_branch = "master"
41 obj.closed = false 45 obj.closed = false
@@ -64,3 +68,8 @@ Factory.add(:wikis, WebHook) do |obj| @@ -64,3 +68,8 @@ Factory.add(:wikis, WebHook) do |obj|
64 obj.title = Faker::Lorem.sentence 68 obj.title = Faker::Lorem.sentence
65 obj.content = Faker::Lorem.sentence 69 obj.content = Faker::Lorem.sentence
66 end 70 end
  71 +
  72 +Factory.add(:event, Event) do |obj|
  73 + obj.title = Faker::Lorem.sentence
  74 + obj.project = Factory(:project)
  75 +end
spec/models/activity_observer_spec.rb 0 → 100644
@@ -0,0 +1,44 @@ @@ -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
spec/models/event_spec.rb 0 → 100644
@@ -0,0 +1,32 @@ @@ -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
spec/models/project_hooks_spec.rb 0 → 100644
@@ -0,0 +1,115 @@ @@ -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 &#39;spec_helper&#39; @@ -2,6 +2,7 @@ require &#39;spec_helper&#39;
2 2
3 describe Project do 3 describe Project do
4 describe "Associations" do 4 describe "Associations" do
  5 + it { should have_many(:events) }
5 it { should have_many(:users) } 6 it { should have_many(:users) }
6 it { should have_many(:users_projects) } 7 it { should have_many(:users_projects) }
7 it { should have_many(:issues) } 8 it { should have_many(:issues) }
@@ -69,106 +70,6 @@ describe Project do @@ -69,106 +70,6 @@ describe Project do
69 end 70 end
70 end 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 describe "updates" do 73 describe "updates" do
173 let(:project) { Factory :project } 74 let(:project) { Factory :project }
174 75
@@ -303,5 +204,6 @@ end @@ -303,5 +204,6 @@ end
303 # issues_enabled :boolean default(TRUE), not null 204 # issues_enabled :boolean default(TRUE), not null
304 # wall_enabled :boolean default(TRUE), not null 205 # wall_enabled :boolean default(TRUE), not null
305 # merge_requests_enabled :boolean default(TRUE), not null 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
@@ -25,7 +25,6 @@ end @@ -25,7 +25,6 @@ end
25 # project_id :integer not null 25 # project_id :integer not null
26 # created_at :datetime 26 # created_at :datetime
27 # updated_at :datetime 27 # updated_at :datetime
28 -# repo_access :integer default(0), not null  
29 # project_access :integer default(0), not null 28 # project_access :integer default(0), not null
30 # 29 #
31 30