diff options
author | Võ Anh Duy <voanhduy1512@live.com> | 2014-02-23 06:42:04 +0700 |
---|---|---|
committer | Võ Anh Duy <voanhduy1512@live.com> | 2014-02-25 00:21:41 +0700 |
commit | f5fc67bc2c3f20f80aef701254542d17ea5ccd8a (patch) | |
tree | f4184b6cbbdc5eda524cfd261954c831e2c709fe | |
parent | a1ecd0fbd6742bf827efe8c869a8b9115d2e4c05 (diff) | |
download | gitlab-ci-f5fc67bc2c3f20f80aef701254542d17ea5ccd8a.tar.gz |
create web hook
-rw-r--r-- | app/models/build.rb | 3 | ||||
-rw-r--r-- | app/models/project.rb | 5 | ||||
-rw-r--r-- | app/models/web_hook.rb | 30 | ||||
-rw-r--r-- | app/services/web_hook_service.rb | 34 | ||||
-rw-r--r-- | app/workers/web_hook_worker.rb | 9 | ||||
-rw-r--r-- | db/migrate/20140222210357_create_web_hook.rb | 8 | ||||
-rw-r--r-- | db/schema.rb | 5 | ||||
-rw-r--r-- | spec/factories/web_hook.rb | 5 | ||||
-rw-r--r-- | spec/models/web_hook_spec.rb | 58 | ||||
-rw-r--r-- | spec/services/web_hook_service_spec.rb | 22 |
10 files changed, 179 insertions, 0 deletions
diff --git a/app/models/build.rb b/app/models/build.rb index 793400b..090af88 100644 --- a/app/models/build.rb +++ b/app/models/build.rb @@ -78,6 +78,9 @@ class Build < ActiveRecord::Base after_transition any => [:success, :failed, :canceled] do |build, transition| build.update_attributes finished_at: Time.now project = build.project + if project.web_hooks? + WebHookService.new.execute_hooks(build) + end if project.email_notification? if build.status.to_sym == :failed || !project.email_only_broken_builds diff --git a/app/models/project.rb b/app/models/project.rb index b916c9f..9486d23 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -31,6 +31,7 @@ class Project < ActiveRecord::Base has_many :builds, dependent: :destroy has_many :runner_projects, dependent: :destroy has_many :runners, through: :runner_projects + has_many :web_hooks # # Validations @@ -142,6 +143,10 @@ class Project < ActiveRecord::Base email_add_committer || email_recipients.present? end + def web_hooks? + web_hooks.any? + end + # onlu check for toggling build status within same ref. def last_build_changed_status? ref = last_build.ref diff --git a/app/models/web_hook.rb b/app/models/web_hook.rb new file mode 100644 index 0000000..dc1d1db --- /dev/null +++ b/app/models/web_hook.rb @@ -0,0 +1,30 @@ +class WebHook < ActiveRecord::Base + belongs_to :project + include HTTParty + + attr_accessible :url + + # HTTParty timeout + default_timeout 10 + + validates :url, presence: true, + format: { with: URI::regexp(%w(http https)), message: "should be a valid url" } + + def execute(data) + parsed_url = URI.parse(url) + if parsed_url.userinfo.blank? + WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" }, verify: false) + else + post_url = url.gsub("#{parsed_url.userinfo}@", "") + auth = { + username: URI.decode(parsed_url.user), + password: URI.decode(parsed_url.password), + } + WebHook.post(post_url, + body: data.to_json, + headers: {"Content-Type" => "application/json"}, + verify: false, + basic_auth: auth) + end + end +end diff --git a/app/services/web_hook_service.rb b/app/services/web_hook_service.rb new file mode 100644 index 0000000..2e42350 --- /dev/null +++ b/app/services/web_hook_service.rb @@ -0,0 +1,34 @@ +class WebHookService + def build_end(build) + execute_hooks(build.project, build_data(build)) + end + + def execute_hooks(project, data) + project.web_hooks.each do |wh| + async_execute_hook wh, data + end + end + + def async_execute_hook(hook, data) + Sidekiq::Client.enqueue(WebHookWorker, hook.id, data) + end + + def build_data(build) + project = build.project + data = {} + data.merge!({ + id: build.id, + project_id: project.id, + project_name: project.name, + gitlab_url: project.gitlab_url, + ref: build.ref, + status: build.status, + started_at: build.started_at, + finished_at: build.finished_at, + sha: build.sha, + before_sha: build.before_sha, + push_data: build.push_data + + }) + end +end diff --git a/app/workers/web_hook_worker.rb b/app/workers/web_hook_worker.rb new file mode 100644 index 0000000..f51b975 --- /dev/null +++ b/app/workers/web_hook_worker.rb @@ -0,0 +1,9 @@ +class WebHookWorker + include Sidekiq::Worker + + sidekiq_options queue: :web_hook + + def perform(hook_id, data) + WebHook.find(hook_id).execute data + end +end diff --git a/db/migrate/20140222210357_create_web_hook.rb b/db/migrate/20140222210357_create_web_hook.rb new file mode 100644 index 0000000..8edf8cf --- /dev/null +++ b/db/migrate/20140222210357_create_web_hook.rb @@ -0,0 +1,8 @@ +class CreateWebHook < ActiveRecord::Migration + def change + create_table :web_hooks do |t| + t.string "url" + t.integer "project_id" + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 6a40d19..8385d5b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -80,4 +80,9 @@ ActiveRecord::Schema.define(version: 20140130121538) do add_index "sessions", ["session_id"], name: "index_sessions_on_session_id", using: :btree add_index "sessions", ["updated_at"], name: "index_sessions_on_updated_at", using: :btree + create_table "web_hooks", force: true do |t| + t.string "url" + t.integer "project_id" + end + end diff --git a/spec/factories/web_hook.rb b/spec/factories/web_hook.rb new file mode 100644 index 0000000..23ca830 --- /dev/null +++ b/spec/factories/web_hook.rb @@ -0,0 +1,5 @@ +FactoryGirl.define do + factory :web_hook do + sequence(:url) { Faker::Internet.uri('http') } + end +end diff --git a/spec/models/web_hook_spec.rb b/spec/models/web_hook_spec.rb new file mode 100644 index 0000000..c8ca95d --- /dev/null +++ b/spec/models/web_hook_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper' + +describe WebHook do + describe "Associations" do + it { should belong_to :project } + end + + describe "Mass assignment" do + it { should_not allow_mass_assignment_of(:project_id) } + end + + describe "Validations" do + it { should validate_presence_of(:url) } + + context "url format" do + it { should allow_value("http://example.com").for(:url) } + it { should allow_value("https://excample.com").for(:url) } + it { should allow_value("http://test.com/api").for(:url) } + it { should allow_value("http://test.com/api?key=abc").for(:url) } + it { should allow_value("http://test.com/api?key=abc&type=def").for(:url) } + + it { should_not allow_value("example.com").for(:url) } + it { should_not allow_value("ftp://example.com").for(:url) } + it { should_not allow_value("herp-and-derp").for(:url) } + end + end + + describe "execute" do + before(:each) do + @web_hook = FactoryGirl.create(:web_hook) + @project = FactoryGirl.create(:project) + @project.web_hooks << [@web_hook] + @data = { before: 'oldrev', after: 'newrev', ref: 'ref'} + + WebMock.stub_request(:post, @web_hook.url) + end + + it "POSTs to the web hook URL" do + @web_hook.execute(@data) + WebMock.should have_requested(:post, @web_hook.url).once + end + + it "POSTs the data as JSON" do + json = @data.to_json + + @web_hook.execute(@data) + WebMock.should have_requested(:post, @web_hook.url).with(body: json).once + end + + it "catches exceptions" do + WebHook.should_receive(:post).and_raise("Some HTTP Post error") + + lambda { + @web_hook.execute(@data) + }.should raise_error + end + end +end diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb new file mode 100644 index 0000000..1ddb4dc --- /dev/null +++ b/spec/services/web_hook_service_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe WebHookService do + let (:project) { FactoryGirl.create :project } + let (:build) { FactoryGirl.create :build, project: project } + let (:hook) { FactoryGirl.create :web_hook, project: project } + + describe :execute do + it "should execute successfully" do + stub_request(:post, hook.url).to_return(status: 200) + WebHookService.new.build_end(build).should be_true + end + end + + context 'build_data' do + it { build_data(build).should include :id, :project_id, :ref, :status, :started_at, :finished_at, :before_sha, :project_name, :gitlab_url } + end + + def build_data(build) + WebHookService.new.send :build_data, build + end +end |