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 @@
  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 @@
  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 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 &lt; 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 &lt; 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
... ... @@ -80,7 +80,6 @@ end
80 80 # project_id :integer not null
81 81 # created_at :datetime
82 82 # updated_at :datetime
83   -# repo_access :integer default(0), not null
84 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 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
... ... @@ -5,6 +5,7 @@ class PostReceive
5 5 project = Project.find_by_path(reponame)
6 6 return false if project.nil?
7 7  
  8 + project.observe_push(oldrev, newrev, ref)
8 9 project.execute_web_hooks(oldrev, newrev, ref)
9 10 end
10 11 end
... ...
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.
... ...
db/migrate/20120228130210_create_events.rb 0 → 100644
... ... @@ -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 @@
  1 +class AddActionToEvent < ActiveRecord::Migration
  2 + def change
  3 + add_column :events, :action, :integer, :null => true
  4 + end
  5 +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
... ...
spec/models/activity_observer_spec.rb 0 → 100644
... ... @@ -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 @@
  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 @@
  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 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
... ... @@ -25,7 +25,6 @@ end
25 25 # project_id :integer not null
26 26 # created_at :datetime
27 27 # updated_at :datetime
28   -# repo_access :integer default(0), not null
29 28 # project_access :integer default(0), not null
30 29 #
31 30  
... ...