Commit 63178a95 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'add-more-slack-notifications' into 'master'

Add more Slack notifications for issue and merge request events

From https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/237 by Stan Hu.

See merge request !1556
parents 00778c07 fc616081
...@@ -32,6 +32,7 @@ v 7.8.2 ...@@ -32,6 +32,7 @@ v 7.8.2
- Fix response of push to repository to return "Not found" if user doesn't have access - Fix response of push to repository to return "Not found" if user doesn't have access
- Fix check if user is allowed to view the file attachment - Fix check if user is allowed to view the file attachment
- Fix import check for case sensetive namespaces - Fix import check for case sensetive namespaces
- Added issue and merge request events to Slack service (Stan Hu)
v 7.8.1 v 7.8.1
- Fix run of custom post receive hooks - Fix run of custom post receive hooks
......
...@@ -51,7 +51,8 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -51,7 +51,8 @@ class Projects::ServicesController < Projects::ApplicationController
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password, :user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server, :teamcity_url, :build_type, :build_key, :server, :teamcity_url, :build_type,
:description, :issues_url, :new_issue_url, :restrict_to_branch, :channel, :description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
:colorize_messages, :channels :colorize_messages, :channels,
:push_events, :issues_events, :merge_requests_events, :tag_push_events
) )
end end
end end
...@@ -479,8 +479,9 @@ class Project < ActiveRecord::Base ...@@ -479,8 +479,9 @@ class Project < ActiveRecord::Base
end end
end end
def execute_services(data) def execute_services(data, hooks_scope = :push_hooks)
services.select(&:active).each do |service| # Call only service hooks that are active for this scope
services.send(hooks_scope).each do |service|
service.async_execute(data) service.async_execute(data)
end end
end end
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean default(TRUE)
# issues_events :boolean default(TRUE)
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# #
require 'asana' require 'asana'
...@@ -61,13 +65,19 @@ automatically inspected. Leave blank to include all branches.' ...@@ -61,13 +65,19 @@ automatically inspected. Leave blank to include all branches.'
] ]
end end
def execute(push) def supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
Asana.configure do |client| Asana.configure do |client|
client.api_key = api_key client.api_key = api_key
end end
user = push[:user_name] user = data[:user_name]
branch = push[:ref].gsub('refs/heads/', '') branch = data[:ref].gsub('refs/heads/', '')
branch_restriction = restrict_to_branch.to_s branch_restriction = restrict_to_branch.to_s
...@@ -79,7 +89,7 @@ automatically inspected. Leave blank to include all branches.' ...@@ -79,7 +89,7 @@ automatically inspected. Leave blank to include all branches.'
project_name = project.name_with_namespace project_name = project.name_with_namespace
push_msg = user + ' pushed to branch ' + branch + ' of ' + project_name push_msg = user + ' pushed to branch ' + branch + ' of ' + project_name
push[:commits].each do |commit| data[:commits].each do |commit|
check_commit(' ( ' + commit[:url] + ' ): ' + commit[:message], push_msg) check_commit(' ( ' + commit[:url] + ' ): ' + commit[:message], push_msg)
end end
end end
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean default(TRUE)
# issues_events :boolean default(TRUE)
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# #
class AssemblaService < Service class AssemblaService < Service
...@@ -38,8 +42,14 @@ class AssemblaService < Service ...@@ -38,8 +42,14 @@ class AssemblaService < Service
] ]
end end
def execute(push) def supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
url = "https://atlas.assembla.com/spaces/#{subdomain}/github_tool?secret_key=#{token}" url = "https://atlas.assembla.com/spaces/#{subdomain}/github_tool?secret_key=#{token}"
AssemblaService.post(url, body: { payload: push }.to_json, headers: { 'Content-Type' => 'application/json' }) AssemblaService.post(url, body: { payload: data }.to_json, headers: { 'Content-Type' => 'application/json' })
end end
end end
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean default(TRUE)
# issues_events :boolean default(TRUE)
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# #
class BambooService < CiService class BambooService < CiService
...@@ -69,6 +73,10 @@ class BambooService < CiService ...@@ -69,6 +73,10 @@ class BambooService < CiService
] ]
end end
def supported_events
%w(push)
end
def build_info(sha) def build_info(sha)
url = URI.parse("#{bamboo_url}/rest/api/latest/result?label=#{sha}") url = URI.parse("#{bamboo_url}/rest/api/latest/result?label=#{sha}")
...@@ -118,7 +126,9 @@ class BambooService < CiService ...@@ -118,7 +126,9 @@ class BambooService < CiService
end end
end end
def execute(_data) def execute(data)
return unless supported_events.include?(data[:object_kind])
# Bamboo requires a GET and does not take any data. # Bamboo requires a GET and does not take any data.
self.class.get("#{bamboo_url}/updateAndBuild.action?buildKey=#{build_key}", self.class.get("#{bamboo_url}/updateAndBuild.action?buildKey=#{build_key}",
verify: false) verify: false)
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean default(TRUE)
# issues_events :boolean default(TRUE)
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# #
require "addressable/uri" require "addressable/uri"
...@@ -32,7 +36,13 @@ class BuildboxService < CiService ...@@ -32,7 +36,13 @@ class BuildboxService < CiService
hook.save hook.save
end end
def supported_events
%w(push)
end
def execute(data) def execute(data)
return unless supported_events.include?(data[:object_kind])
service_hook.execute(data) service_hook.execute(data)
end end
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean default(TRUE)
# issues_events :boolean default(TRUE)
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# #
class CampfireService < Service class CampfireService < Service
...@@ -37,11 +41,17 @@ class CampfireService < Service ...@@ -37,11 +41,17 @@ class CampfireService < Service
] ]
end end
def execute(push_data) def supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
room = gate.find_room_by_name(self.room) room = gate.find_room_by_name(self.room)
return true unless room return true unless room
message = build_message(push_data) message = build_message(data)
room.speak(message) room.speak(message)
end end
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean
# issues_events :boolean
# merge_requests_events :boolean
# tag_push_events :boolean
# #
# Base class for CI services # Base class for CI services
...@@ -21,6 +25,10 @@ class CiService < Service ...@@ -21,6 +25,10 @@ class CiService < Service
:ci :ci
end end
def supported_events
%w(push)
end
# Return complete url to build page # Return complete url to build page
# #
# Ex. # Ex.
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean
# issues_events :boolean
# merge_requests_events :boolean
# tag_push_events :boolean
# #
class EmailsOnPushService < Service class EmailsOnPushService < Service
...@@ -29,8 +33,14 @@ class EmailsOnPushService < Service ...@@ -29,8 +33,14 @@ class EmailsOnPushService < Service
'emails_on_push' 'emails_on_push'
end end
def execute(push_data) def supported_events
EmailsOnPushWorker.perform_async(project_id, recipients, push_data) %w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
EmailsOnPushWorker.perform_async(project_id, recipients, data)
end end
def fields def fields
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean
# issues_events :boolean
# merge_requests_events :boolean
# tag_push_events :boolean
# #
require "flowdock-git-hook" require "flowdock-git-hook"
...@@ -37,11 +41,17 @@ class FlowdockService < Service ...@@ -37,11 +41,17 @@ class FlowdockService < Service
] ]
end end
def execute(push_data) def supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
Flowdock::Git.post( Flowdock::Git.post(
push_data[:ref], data[:ref],
push_data[:before], data[:before],
push_data[:after], data[:after],
token: token, token: token,
repo: project.repository.path_to_repo, repo: project.repository.path_to_repo,
repo_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}", repo_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}",
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean
# issues_events :boolean
# merge_requests_events :boolean
# tag_push_events :boolean
# #
require "gemnasium/gitlab_service" require "gemnasium/gitlab_service"
...@@ -38,11 +42,17 @@ class GemnasiumService < Service ...@@ -38,11 +42,17 @@ class GemnasiumService < Service
] ]
end end
def execute(push_data) def supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
Gemnasium::GitlabService.execute( Gemnasium::GitlabService.execute(
ref: push_data[:ref], ref: data[:ref],
before: push_data[:before], before: data[:before],
after: push_data[:after], after: data[:after],
token: token, token: token,
api_key: api_key, api_key: api_key,
repo: project.repository.path_to_repo repo: project.repository.path_to_repo
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean
# issues_events :boolean
# merge_requests_events :boolean
# tag_push_events :boolean
# #
class GitlabCiService < CiService class GitlabCiService < CiService
...@@ -18,8 +22,6 @@ class GitlabCiService < CiService ...@@ -18,8 +22,6 @@ class GitlabCiService < CiService
validates :project_url, presence: true, if: :activated? validates :project_url, presence: true, if: :activated?
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
delegate :execute, to: :service_hook, prefix: nil
after_save :compose_service_hook, if: :activated? after_save :compose_service_hook, if: :activated?
def compose_service_hook def compose_service_hook
...@@ -28,6 +30,16 @@ class GitlabCiService < CiService ...@@ -28,6 +30,16 @@ class GitlabCiService < CiService
hook.save hook.save
end end
def supported_events
%w(push tag_push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
service_hook.execute(data)
end
def commit_status_path(sha) def commit_status_path(sha)
project_url + "/commits/#{sha}/status.json?token=#{token}" project_url + "/commits/#{sha}/status.json?token=#{token}"
end end
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean default(TRUE)
# issues_events :boolean default(TRUE)
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# #
class GitlabIssueTrackerService < IssueTrackerService class GitlabIssueTrackerService < IssueTrackerService
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean
# issues_events :boolean
# merge_requests_events :boolean
# tag_push_events :boolean
# #
class HipchatService < Service class HipchatService < Service
...@@ -40,8 +44,14 @@ class HipchatService < Service ...@@ -40,8 +44,14 @@ class HipchatService < Service
] ]
end end
def execute(push_data) def supported_events
gate[room].send('GitLab', create_message(push_data)) %w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
gate[room].send('GitLab', create_message(data))
end end
private private
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean default(TRUE)
# issues_events :boolean default(TRUE)
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# #
class IssueTrackerService < Service class IssueTrackerService < Service
...@@ -65,7 +69,13 @@ class IssueTrackerService < Service ...@@ -65,7 +69,13 @@ class IssueTrackerService < Service
end end
end end
def supported_events
%w(push)
end
def execute(data) def execute(data)
return unless supported_events.include?(data[:object_kind])
message = "#{self.type} was unable to reach #{self.project_url}. Check the url and try again." message = "#{self.type} was unable to reach #{self.project_url}. Check the url and try again."
result = false result = false
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean default(TRUE)
# issues_events :boolean default(TRUE)
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# #
class JiraService < IssueTrackerService class JiraService < IssueTrackerService
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean default(TRUE)
# issues_events :boolean default(TRUE)
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# #
class PivotaltrackerService < Service class PivotaltrackerService < Service
...@@ -37,9 +41,15 @@ class PivotaltrackerService < Service ...@@ -37,9 +41,15 @@ class PivotaltrackerService < Service
] ]
end end
def execute(push) def supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
url = 'https://www.pivotaltracker.com/services/v5/source_commits' url = 'https://www.pivotaltracker.com/services/v5/source_commits'
push[:commits].each do |commit| data[:commits].each do |commit|
message = { message = {
'source_commit' => { 'source_commit' => {
'commit_id' => commit[:id], 'commit_id' => commit[:id],
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean default(TRUE)
# issues_events :boolean default(TRUE)
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# #
class PushoverService < Service class PushoverService < Service
...@@ -76,21 +80,27 @@ class PushoverService < Service ...@@ -76,21 +80,27 @@ class PushoverService < Service
] ]
end end
def execute(push_data) def supported_events
ref = push_data[:ref].gsub('refs/heads/', '') %w(push)
before = push_data[:before] end
after = push_data[:after]
def execute(data)
return unless supported_events.include?(data[:object_kind])
ref = data[:ref].gsub('refs/heads/', '')
before = data[:before]
after = data[:after]
if before.include?('000000') if before.include?('000000')
message = "#{push_data[:user_name]} pushed new branch \"#{ref}\"." message = "#{data[:user_name]} pushed new branch \"#{ref}\"."
elsif after.include?('000000') elsif after.include?('000000')
message = "#{push_data[:user_name]} deleted branch \"#{ref}\"." message = "#{data[:user_name]} deleted branch \"#{ref}\"."
else else
message = "#{push_data[:user_name]} push to branch \"#{ref}\"." message = "#{data[:user_name]} push to branch \"#{ref}\"."
end end
if push_data[:total_commits_count] > 0 if data[:total_commits_count] > 0
message << "\nTotal commits count: #{push_data[:total_commits_count]}" message << "\nTotal commits count: #{data[:total_commits_count]}"
end end
pushover_data = { pushover_data = {
...@@ -100,7 +110,7 @@ class PushoverService < Service ...@@ -100,7 +110,7 @@ class PushoverService < Service
priority: priority, priority: priority,
title: "#{project.name_with_namespace}", title: "#{project.name_with_namespace}",
message: message, message: message,
url: push_data[:repository][:homepage], url: data[:repository][:homepage],
url_title: "See project #{project.name_with_namespace}" url_title: "See project #{project.name_with_namespace}"
} }
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean default(TRUE)
# issues_events :boolean default(TRUE)
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# #
class RedmineService < IssueTrackerService class RedmineService < IssueTrackerService
......
require 'slack-notifier'
class SlackMessage
attr_reader :after
attr_reader :before
attr_reader :commits
attr_reader :project_name
attr_reader :project_url
attr_reader :ref
attr_reader :username
def initialize(params)
@after = params.fetch(:after)
@before = params.fetch(:before)
@commits = params.fetch(:commits, [])
@project_name = params.fetch(:project_name)
@project_url = params.fetch(:project_url)
@ref = params.fetch(:ref).gsub('refs/heads/', '')
@username = params.fetch(:user_name)
end
def pretext
format(message)
end
def attachments
return [] if new_branch? || removed_branch?
commit_message_attachments
end
private
def message
if new_branch?
new_branch_message
elsif removed_branch?
removed_branch_message
else
push_message
end
end
def format(string)
Slack::Notifier::LinkFormatter.format(string)
end
def new_branch_message
"#{username} pushed new branch #{branch_link} to #{project_link}"
end
def removed_branch_message
"#{username} removed branch #{ref} from #{project_link}"
end
def push_message
"#{username} pushed to branch #{branch_link} of #{project_link} (#{compare_link})"
end
def commit_messages
commits.each_with_object('') do |commit, str|
str << compose_commit_message(commit)
end.chomp
end
def commit_message_attachments
[{ text: format(commit_messages), color: attachment_color }]
end
def compose_commit_message(commit)
author = commit.fetch(:author).fetch(:name)
id = commit.fetch(:id)[0..8]
message = commit.fetch(:message)
url = commit.fetch(:url)
"[#{id}](#{url}): #{message} - #{author}\n"
end
def new_branch?
before.include?('000000')
end
def removed_branch?
after.include?('000000')
end
def branch_url
"#{project_url}/commits/#{ref}"
end
def compare_url
"#{project_url}/compare/#{before}...#{after}"
end
def branch_link
"[#{ref}](#{branch_url})"
end
def project_link
"[#{project_name}](#{project_url})"
end
def compare_link
"[Compare changes](#{compare_url})"
end
def attachment_color
'#345'
end
end
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean default(TRUE)
# issues_events :boolean default(TRUE)
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# #
class SlackService < Service class SlackService < Service
...@@ -38,20 +42,43 @@ class SlackService < Service ...@@ -38,20 +42,43 @@ class SlackService < Service
] ]
end end
def execute(push_data) def supported_events
%w(push issue merge_request)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
return unless webhook.present? return unless webhook.present?
message = SlackMessage.new(push_data.merge( object_kind = data[:object_kind]
data = data.merge(
project_url: project_url, project_url: project_url,
project_name: project_name project_name: project_name
)) )
# WebHook events often have an 'update' event that follows a 'open' or
# 'close' action. Ignore update events for now to prevent duplicate
# messages from arriving.
message = \
case object_kind
when "push"
PushMessage.new(data)
when "issue"
IssueMessage.new(data) unless is_update?(data)
when "merge_request"
MergeMessage.new(data) unless is_update?(data)
end
opt = {} opt = {}
opt[:channel] = channel if channel opt[:channel] = channel if channel
opt[:username] = username if username opt[:username] = username if username
notifier = Slack::Notifier.new(webhook, opt) if message
notifier.ping(message.pretext, attachments: message.attachments) notifier = Slack::Notifier.new(webhook, opt)
notifier.ping(message.pretext, attachments: message.attachments)
end
end end
private private
...@@ -63,4 +90,12 @@ class SlackService < Service ...@@ -63,4 +90,12 @@ class SlackService < Service
def project_url def project_url
project.web_url project.web_url
end end
def is_update?(data)
data[:object_attributes][:action] == 'update'
end
end end
require "slack_service/issue_message"
require "slack_service/push_message"
require "slack_service/merge_message"
require 'slack-notifier'
class SlackService
class BaseMessage
def initialize(params)
raise NotImplementedError
end
def pretext
format(message)
end
def attachments
raise NotImplementedError
end
private
def message
raise NotImplementedError
end
def format(string)
Slack::Notifier::LinkFormatter.format(string)
end
def attachment_color
'#345'
end
end
end
class SlackService
class IssueMessage < BaseMessage
attr_reader :username
attr_reader :title
attr_reader :project_name
attr_reader :project_url
attr_reader :issue_iid
attr_reader :issue_url
attr_reader :action
attr_reader :state
attr_reader :description
def initialize(params)
@username = params[:user][:username]
@project_name = params[:project_name]
@project_url = params[:project_url]
obj_attr = params[:object_attributes]
obj_attr = HashWithIndifferentAccess.new(obj_attr)
@title = obj_attr[:title]
@issue_iid = obj_attr[:iid]
@issue_url = obj_attr[:url]
@action = obj_attr[:action]
@state = obj_attr[:state]
@description = obj_attr[:description]
end
def attachments
return [] unless opened_issue?
description_message
end
private
def message
"#{username} #{state} issue #{issue_link} in #{project_link}: #{title}"
end
def opened_issue?
action == "open"
end
def description_message
[{ text: format(description), color: attachment_color }]
end
def project_link
"[#{project_name}](#{project_url})"
end
def issue_link
"[##{issue_iid}](#{issue_url})"
end
end
end
class SlackService
class MergeMessage < BaseMessage
attr_reader :username
attr_reader :project_name
attr_reader :project_url
attr_reader :merge_request_id
attr_reader :source_branch
attr_reader :target_branch
attr_reader :state
def initialize(params)
@username = params[:user][:username]
@project_name = params[:project_name]
@project_url = params[:project_url]
obj_attr = params[:object_attributes]
obj_attr = HashWithIndifferentAccess.new(obj_attr)
@merge_request_id = obj_attr[:iid]
@source_branch = obj_attr[:source_branch]
@target_branch = obj_attr[:target_branch]
@state = obj_attr[:state]
end
def pretext
format(message)
end
def attachments
[]
end
private
def message
merge_request_message
end
def project_link
"[#{project_name}](#{project_url})"
end
def merge_request_message
"#{username} #{state} merge request #{merge_request_link} in #{project_link}"
end
def merge_request_link
"[##{merge_request_id}](#{merge_request_url})"
end
def merge_request_url
"#{project_url}/merge_requests/#{merge_request_id}"
end
end
end
class SlackService
class PushMessage < BaseMessage
attr_reader :after
attr_reader :before
attr_reader :commits
attr_reader :project_name
attr_reader :project_url
attr_reader :ref
attr_reader :username
def initialize(params)
@after = params[:after]
@before = params[:before]
@commits = params.fetch(:commits, [])
@project_name = params[:project_name]
@project_url = params[:project_url]
@ref = params[:ref].gsub('refs/heads/', '')
@username = params[:user_name]
end
def pretext
format(message)
end
def attachments
return [] if new_branch? || removed_branch?
commit_message_attachments
end
private
def message
if new_branch?
new_branch_message
elsif removed_branch?
removed_branch_message
else
push_message
end
end
def format(string)
Slack::Notifier::LinkFormatter.format(string)
end
def new_branch_message
"#{username} pushed new branch #{branch_link} to #{project_link}"
end
def removed_branch_message
"#{username} removed branch #{ref} from #{project_link}"
end
def push_message
"#{username} pushed to branch #{branch_link} of #{project_link} (#{compare_link})"
end
def commit_messages
commits.map { |commit| compose_commit_message(commit) }.join("\n")
end
def commit_message_attachments
[{ text: format(commit_messages), color: attachment_color }]
end
def compose_commit_message(commit)
author = commit[:author][:name]
id = Commit.truncate_sha(commit[:id])
message = commit[:message]
url = commit[:url]
"[#{id}](#{url}): #{message} - #{author}"
end
def new_branch?
before.include?('000000')
end
def removed_branch?
after.include?('000000')
end
def branch_url
"#{project_url}/commits/#{ref}"
end
def compare_url
"#{project_url}/compare/#{before}...#{after}"
end
def branch_link
"[#{ref}](#{branch_url})"
end
def project_link
"[#{project_name}](#{project_url})"
end
def compare_link
"[Compare changes](#{compare_url})"
end
def attachment_color
'#345'
end
end
end
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean default(TRUE)
# issues_events :boolean default(TRUE)
# merge_requests_events :boolean default(TRUE)
# tag_push_events :boolean default(TRUE)
# #
class TeamcityService < CiService class TeamcityService < CiService
...@@ -57,6 +61,10 @@ class TeamcityService < CiService ...@@ -57,6 +61,10 @@ class TeamcityService < CiService
'teamcity' 'teamcity'
end end
def supported_events
%w(push)
end
def fields def fields
[ [
{ type: 'text', name: 'teamcity_url', { type: 'text', name: 'teamcity_url',
...@@ -115,13 +123,15 @@ class TeamcityService < CiService ...@@ -115,13 +123,15 @@ class TeamcityService < CiService
end end
end end
def execute(push) def execute(data)
return unless supported_events.include?(data[:object_kind])
auth = { auth = {
username: username, username: username,
password: password, password: password,
} }
branch = push[:ref].gsub('refs/heads/', '') branch = data[:ref].gsub('refs/heads/', '')
self.class.post("#{teamcity_url}/httpAuth/app/rest/buildQueue", self.class.post("#{teamcity_url}/httpAuth/app/rest/buildQueue",
body: "<build branchName=\"#{branch}\">"\ body: "<build branchName=\"#{branch}\">"\
......
...@@ -11,6 +11,11 @@ ...@@ -11,6 +11,11 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean
# issues_events :boolean
# merge_requests_events :boolean
# tag_push_events :boolean
#
# To add new service you should build a class inherited from Service # To add new service you should build a class inherited from Service
# and implement a set of methods # and implement a set of methods
...@@ -19,6 +24,10 @@ class Service < ActiveRecord::Base ...@@ -19,6 +24,10 @@ class Service < ActiveRecord::Base
serialize :properties, JSON serialize :properties, JSON
default_value_for :active, false default_value_for :active, false
default_value_for :push_events, true
default_value_for :issues_events, true
default_value_for :merge_requests_events, true
default_value_for :tag_push_events, true
after_initialize :initialize_properties after_initialize :initialize_properties
...@@ -29,6 +38,11 @@ class Service < ActiveRecord::Base ...@@ -29,6 +38,11 @@ class Service < ActiveRecord::Base
scope :visible, -> { where.not(type: 'GitlabIssueTrackerService') } scope :visible, -> { where.not(type: 'GitlabIssueTrackerService') }
scope :push_hooks, -> { where(push_events: true, active: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true, active: true) }
scope :issue_hooks, -> { where(issues_events: true, active: true) }
scope :merge_request_hooks, -> { where(merge_requests_events: true, active: true) }
def activated? def activated?
active active
end end
...@@ -66,6 +80,10 @@ class Service < ActiveRecord::Base ...@@ -66,6 +80,10 @@ class Service < ActiveRecord::Base
[] []
end end
def supported_events
%w(push tag_push issue merge_request)
end
def execute def execute
# implement inside child # implement inside child
end end
...@@ -91,6 +109,8 @@ class Service < ActiveRecord::Base ...@@ -91,6 +109,8 @@ class Service < ActiveRecord::Base
end end
def async_execute(data) def async_execute(data)
return unless supported_events.include?(data[:object_kind])
Sidekiq::Client.enqueue(ProjectServiceWorker, id, data) Sidekiq::Client.enqueue(ProjectServiceWorker, id, data)
end end
......
...@@ -21,12 +21,12 @@ class CreateTagService < BaseService ...@@ -21,12 +21,12 @@ class CreateTagService < BaseService
new_tag = repository.find_tag(tag_name) new_tag = repository.find_tag(tag_name)
if new_tag if new_tag
if project.gitlab_ci?
push_data = create_push_data(project, current_user, new_tag)
project.gitlab_ci_service.async_execute(push_data)
end
EventCreateService.new.push_ref(project, current_user, new_tag, 'add', 'refs/tags') EventCreateService.new.push_ref(project, current_user, new_tag, 'add', 'refs/tags')
push_data = create_push_data(project, current_user, new_tag)
project.execute_hooks(push_data.dup, :tag_push_hooks)
project.execute_services(push_data.dup, :tag_push_hooks)
success(new_tag) success(new_tag)
else else
error('Invalid reference name') error('Invalid reference name')
...@@ -40,7 +40,9 @@ class CreateTagService < BaseService ...@@ -40,7 +40,9 @@ class CreateTagService < BaseService
end end
def create_push_data(project, user, tag) def create_push_data(project, user, tag)
Gitlab::PushDataBuilder. data = Gitlab::PushDataBuilder.
build(project, user, Gitlab::Git::BLANK_SHA, tag.target, 'refs/tags/' + tag.name, []) build(project, user, Gitlab::Git::BLANK_SHA, tag.target, 'refs/tags/' + tag.name, [])
data[:object_kind] = "tag_push"
data
end end
end end
...@@ -54,7 +54,7 @@ class GitPushService ...@@ -54,7 +54,7 @@ class GitPushService
@push_data = post_receive_data(oldrev, newrev, ref) @push_data = post_receive_data(oldrev, newrev, ref)
EventCreateService.new.push(project, user, @push_data) EventCreateService.new.push(project, user, @push_data)
project.execute_hooks(@push_data.dup, :push_hooks) project.execute_hooks(@push_data.dup, :push_hooks)
project.execute_services(@push_data.dup) project.execute_services(@push_data.dup, :push_hooks)
end end
end end
......
...@@ -8,10 +8,7 @@ class GitTagPushService ...@@ -8,10 +8,7 @@ class GitTagPushService
EventCreateService.new.push(project, user, @push_data) EventCreateService.new.push(project, user, @push_data)
project.repository.expire_cache project.repository.expire_cache
project.execute_hooks(@push_data.dup, :tag_push_hooks) project.execute_hooks(@push_data.dup, :tag_push_hooks)
project.execute_services(@push_data.dup, :tag_push_hooks)
if project.gitlab_ci?
project.gitlab_ci_service.async_execute(@push_data)
end
true true
end end
...@@ -19,7 +16,8 @@ class GitTagPushService ...@@ -19,7 +16,8 @@ class GitTagPushService
private private
def create_push_data(oldrev, newrev, ref) def create_push_data(oldrev, newrev, ref)
Gitlab::PushDataBuilder. data = Gitlab::PushDataBuilder.build(project, user, oldrev, newrev, ref, [])
build(project, user, oldrev, newrev, ref, []) data[:object_kind] = "tag_push"
data
end end
end end
module Issues module Issues
class BaseService < ::IssuableBaseService class BaseService < ::IssuableBaseService
private def hook_data(issue, action)
def execute_hooks(issue, action = 'open')
issue_data = issue.to_hook_data(current_user) issue_data = issue.to_hook_data(current_user)
issue_url = Gitlab::UrlBuilder.new(:issue).build(issue.id) issue_url = Gitlab::UrlBuilder.new(:issue).build(issue.id)
issue_data[:object_attributes].merge!(url: issue_url, action: action) issue_data[:object_attributes].merge!(url: issue_url, action: action)
issue_data
end
private
def execute_hooks(issue, action = 'open')
issue_data = hook_data(issue, action)
issue.project.execute_hooks(issue_data, :issue_hooks) issue.project.execute_hooks(issue_data, :issue_hooks)
issue.project.execute_services(issue_data, :issue_hooks)
end end
end end
end end
...@@ -5,13 +5,19 @@ module MergeRequests ...@@ -5,13 +5,19 @@ module MergeRequests
Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil) Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil)
end end
def hook_data(merge_request, action)
hook_data = merge_request.to_hook_data(current_user)
merge_request_url = Gitlab::UrlBuilder.new(:merge_request).build(merge_request.id)
hook_data[:object_attributes][:url] = merge_request_url
hook_data[:object_attributes][:action] = action
hook_data
end
def execute_hooks(merge_request, action = 'open') def execute_hooks(merge_request, action = 'open')
if merge_request.project if merge_request.project
hook_data = merge_request.to_hook_data(current_user) merge_data = hook_data(merge_request, action)
merge_request_url = Gitlab::UrlBuilder.new(:merge_request).build(merge_request.id) merge_request.project.execute_hooks(merge_data, :merge_request_hooks)
hook_data[:object_attributes][:url] = merge_request_url merge_request.project.execute_services(merge_data, :merge_request_hooks)
hook_data[:object_attributes][:action] = action
merge_request.project.execute_hooks(hook_data, :merge_request_hooks)
end end
end end
end end
......
...@@ -14,6 +14,43 @@ ...@@ -14,6 +14,43 @@
= preserve do = preserve do
= markdown @service.help = markdown @service.help
.form-group
= f.label :url, "Trigger", class: 'control-label'
- if @service.supported_events.length > 1
.col-sm-10
- if @service.supported_events.include?("push")
%div
= f.check_box :push_events, class: 'pull-left'
.prepend-left-20
= f.label :push_events, class: 'list-label' do
%strong Push events
%p.light
This url will be triggered by a push to the repository
- if @service.supported_events.include?("tag_push")
%div
= f.check_box :tag_push_events, class: 'pull-left'
.prepend-left-20
= f.label :tag_push_events, class: 'list-label' do
%strong Tag push events
%p.light
This url will be triggered when a new tag is pushed to the repository
- if @service.supported_events.include?("issue")
%div
= f.check_box :issues_events, class: 'pull-left'
.prepend-left-20
= f.label :issues_events, class: 'list-label' do
%strong Issues events
%p.light
This url will be triggered when an issue is created
- if @service.supported_events.include?("merge_request")
%div
= f.check_box :merge_requests_events, class: 'pull-left'
.prepend-left-20
= f.label :merge_requests_events, class: 'list-label' do
%strong Merge Request events
%p.light
This url will be triggered when a merge request is created
- @service.fields.each do |field| - @service.fields.each do |field|
- name = field[:name] - name = field[:name]
- value = @service.send(name) unless field[:type] == 'password' - value = @service.send(name) unless field[:type] == 'password'
......
...@@ -27,6 +27,43 @@ ...@@ -27,6 +27,43 @@
.col-sm-10 .col-sm-10
= f.check_box :active = f.check_box :active
.form-group
= f.label :url, "Trigger", class: 'control-label'
- if @service.supported_events.length > 1
.col-sm-10
- if @service.supported_events.include?("push")
%div
= f.check_box :push_events, class: 'pull-left'
.prepend-left-20
= f.label :push_events, class: 'list-label' do
%strong Push events
%p.light
This url will be triggered by a push to the repository
- if @service.supported_events.include?("tag_push")
%div
= f.check_box :tag_push_events, class: 'pull-left'
.prepend-left-20
= f.label :tag_push_events, class: 'list-label' do
%strong Tag push events
%p.light
This url will be triggered when a new tag is pushed to the repository
- if @service.supported_events.include?("issue")
%div
= f.check_box :issues_events, class: 'pull-left'
.prepend-left-20
= f.label :issues_events, class: 'list-label' do
%strong Issues events
%p.light
This url will be triggered when an issue is created
- if @service.supported_events.include?("merge_request")
%div
= f.check_box :merge_requests_events, class: 'pull-left'
.prepend-left-20
= f.label :merge_requests_events, class: 'list-label' do
%strong Merge Request events
%p.light
This url will be triggered when a merge request is created
- @service.fields.each do |field| - @service.fields.each do |field|
- name = field[:name] - name = field[:name]
- value = @service.send(name) unless field[:type] == 'password' - value = @service.send(name) unless field[:type] == 'password'
......
class AddEventsToServices < ActiveRecord::Migration
def change
add_column :services, :push_events, :boolean, :default => true
add_column :services, :issues_events, :boolean, :default => true
add_column :services, :merge_requests_events, :boolean, :default => true
add_column :services, :tag_push_events, :boolean, :default => true
end
end
...@@ -364,9 +364,13 @@ ActiveRecord::Schema.define(version: 20150223022001) do ...@@ -364,9 +364,13 @@ ActiveRecord::Schema.define(version: 20150223022001) do
t.integer "project_id" t.integer "project_id"
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
t.boolean "active", default: false, null: false t.boolean "active", default: false, null: false
t.text "properties" t.text "properties"
t.boolean "template", default: false t.boolean "template", default: false
t.boolean "push_events", default: true
t.boolean "issues_events", default: true
t.boolean "merge_requests_events", default: true
t.boolean "tag_push_events", default: true
end end
add_index "services", ["created_at", "id"], name: "index_services_on_created_at_and_id", using: :btree add_index "services", ["created_at", "id"], name: "index_services_on_created_at_and_id", using: :btree
......
...@@ -30,6 +30,7 @@ module Gitlab ...@@ -30,6 +30,7 @@ module Gitlab
# Hash to be passed as post_receive_data # Hash to be passed as post_receive_data
data = { data = {
object_kind: "push",
before: oldrev, before: oldrev,
after: newrev, after: newrev,
ref: ref, ref: ref,
......
...@@ -2,14 +2,18 @@ ...@@ -2,14 +2,18 @@
# #
# Table name: services # Table name: services
# #
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# push_events :boolean
# issues_events :boolean
# merge_requests_events :boolean
# tag_push_events :boolean
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -2,14 +2,18 @@ ...@@ -2,14 +2,18 @@
# #
# Table name: services # Table name: services
# #
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# push_events :boolean
# issues_events :boolean
# merge_requests_events :boolean
# tag_push_events :boolean
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -2,14 +2,18 @@ ...@@ -2,14 +2,18 @@
# #
# Table name: services # Table name: services
# #
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# push_events :boolean
# issues_events :boolean
# merge_requests_events :boolean
# tag_push_events :boolean
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -2,14 +2,18 @@ ...@@ -2,14 +2,18 @@
# #
# Table name: services # Table name: services
# #
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# push_events :boolean
# issues_events :boolean
# merge_requests_events :boolean
# tag_push_events :boolean
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -2,14 +2,18 @@ ...@@ -2,14 +2,18 @@
# #
# Table name: services # Table name: services
# #
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# push_events :boolean
# issues_events :boolean
# merge_requests_events :boolean
# tag_push_events :boolean
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -2,14 +2,18 @@ ...@@ -2,14 +2,18 @@
# #
# Table name: services # Table name: services
# #
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# push_events :boolean
# issues_events :boolean
# merge_requests_events :boolean
# tag_push_events :boolean
# #
require 'spec_helper' require 'spec_helper'
......
require 'spec_helper'
describe SlackService::IssueMessage do
subject { SlackService::IssueMessage.new(args) }
let(:args) {
{
user: {
username: 'username'
},
project_name: 'project_name',
project_url: 'somewhere.com',
object_attributes: {
title: 'Issue title',
id: 10,
iid: 100,
assignee_id: 1,
url: 'url',
action: 'open',
state: 'opened',
description: 'issue description'
}
}
}
let(:color) { '#345' }
context 'open' do
it 'returns a message regarding opening of issues' do
expect(subject.pretext).to eq(
'username opened issue <url|#100> in <somewhere.com|project_name>: '\
'Issue title')
expect(subject.attachments).to eq([
{
text: "issue description",
color: color,
}
])
end
end
context 'close' do
before do
args[:object_attributes][:action] = 'close'
args[:object_attributes][:state] = 'closed'
end
it 'returns a message regarding closing of issues' do
expect(subject.pretext). to eq(
'username closed issue <url|#100> in <somewhere.com|project_name>: '\
'Issue title')
expect(subject.attachments).to be_empty
end
end
end
require 'spec_helper'
describe SlackService::MergeMessage do
subject { SlackService::MergeMessage.new(args) }
let(:args) {
{
user: {
username: 'username'
},
project_name: 'project_name',
project_url: 'somewhere.com',
object_attributes: {
title: 'Issue title',
id: 10,
iid: 100,
assignee_id: 1,
url: 'url',
state: 'opened',
description: 'issue description',
source_branch: 'source_branch',
target_branch: 'target_branch',
}
}
}
let(:color) { '#345' }
context 'open' do
it 'returns a message regarding opening of merge requests' do
expect(subject.pretext).to eq(
'username opened merge request <somewhere.com/merge_requests/100|#100> '\
'in <somewhere.com|project_name>')
expect(subject.attachments).to be_empty
end
end
context 'close' do
before do
args[:object_attributes][:state] = 'closed'
end
it 'returns a message regarding closing of merge requests' do
expect(subject.pretext).to eq(
'username closed merge request <somewhere.com/merge_requests/100|#100> '\
'in <somewhere.com|project_name>')
expect(subject.attachments).to be_empty
end
end
end
require 'spec_helper' require 'spec_helper'
describe SlackMessage do describe SlackService::PushMessage do
subject { SlackMessage.new(args) } subject { SlackService::PushMessage.new(args) }
let(:args) { let(:args) {
{ {
...@@ -31,8 +31,8 @@ describe SlackMessage do ...@@ -31,8 +31,8 @@ describe SlackMessage do
) )
expect(subject.attachments).to eq([ expect(subject.attachments).to eq([
{ {
text: "<url1|abcdefghi>: message1 - author1\n"\ text: "<url1|abcdefgh>: message1 - author1\n"\
"<url2|123456789>: message2 - author2", "<url2|12345678>: message2 - author2",
color: color, color: color,
} }
]) ])
......
...@@ -2,14 +2,18 @@ ...@@ -2,14 +2,18 @@
# #
# Table name: services # Table name: services
# #
# id :integer not null, primary key # id :integer not null, primary key
# type :string(255) # type :string(255)
# title :string(255) # title :string(255)
# project_id :integer not null # project_id :integer not null
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# push_events :boolean
# issues_events :boolean
# merge_requests_events :boolean
# tag_push_events :boolean
# #
require 'spec_helper' require 'spec_helper'
...@@ -34,7 +38,7 @@ describe SlackService do ...@@ -34,7 +38,7 @@ describe SlackService do
let(:slack) { SlackService.new } let(:slack) { SlackService.new }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project) } let(:project) { create(:project) }
let(:sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) } let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) }
let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' } let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' }
let(:username) { 'slack_username' } let(:username) { 'slack_username' }
let(:channel) { 'slack_channel' } let(:channel) { 'slack_channel' }
...@@ -48,10 +52,43 @@ describe SlackService do ...@@ -48,10 +52,43 @@ describe SlackService do
) )
WebMock.stub_request(:post, webhook_url) WebMock.stub_request(:post, webhook_url)
opts = {
title: 'Awesome issue',
description: 'please fix'
}
issue_service = Issues::CreateService.new(project, user, opts)
@issue = issue_service.execute
@issues_sample_data = issue_service.hook_data(@issue, 'open')
opts = {
title: 'Awesome merge_request',
description: 'please fix',
source_branch: 'stable',
target_branch: 'master'
}
merge_service = MergeRequests::CreateService.new(project,
user, opts)
@merge_request = merge_service.execute
@merge_sample_data = merge_service.hook_data(@merge_request,
'open')
end
it "should call Slack API for pull requests" do
slack.execute(push_sample_data)
WebMock.should have_requested(:post, webhook_url).once
end end
it "should call Slack API" do it "should call Slack API for issue events" do
slack.execute(sample_data) slack.execute(@issues_sample_data)
WebMock.should have_requested(:post, webhook_url).once
end
it "should call Slack API for merge requests events" do
slack.execute(@merge_sample_data)
expect(WebMock).to have_requested(:post, webhook_url).once expect(WebMock).to have_requested(:post, webhook_url).once
end end
...@@ -62,8 +99,8 @@ describe SlackService do ...@@ -62,8 +99,8 @@ describe SlackService do
with(webhook_url, username: username). with(webhook_url, username: username).
and_return( and_return(
double(:slack_service).as_null_object double(:slack_service).as_null_object
) )
slack.execute(sample_data) slack.execute(push_sample_data)
end end
it 'should use the channel as an option when it is configured' do it 'should use the channel as an option when it is configured' do
...@@ -73,7 +110,7 @@ describe SlackService do ...@@ -73,7 +110,7 @@ describe SlackService do
and_return( and_return(
double(:slack_service).as_null_object double(:slack_service).as_null_object
) )
slack.execute(sample_data) slack.execute(push_sample_data)
end end
end end
end end
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
# active :boolean default(FALSE), not null # active :boolean default(FALSE), not null
# properties :text # properties :text
# template :boolean default(FALSE) # template :boolean default(FALSE)
# push_events :boolean
# issues_events :boolean
# merge_requests_events :boolean
# tag_push_events :boolean
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -49,6 +49,7 @@ describe GitPushService do ...@@ -49,6 +49,7 @@ describe GitPushService do
subject { @push_data } subject { @push_data }
it { is_expected.to include(object_kind: 'push') }
it { is_expected.to include(before: @oldrev) } it { is_expected.to include(before: @oldrev) }
it { is_expected.to include(after: @newrev) } it { is_expected.to include(after: @newrev) }
it { is_expected.to include(ref: @ref) } it { is_expected.to include(ref: @ref) }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment