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