diff options
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/project.rb | 1 | ||||
-rw-r--r-- | app/models/project_services/chat_message/base_message.rb (renamed from app/models/project_services/slack_service/base_message.rb) | 2 | ||||
-rw-r--r-- | app/models/project_services/chat_message/build_message.rb (renamed from app/models/project_services/slack_service/build_message.rb) | 2 | ||||
-rw-r--r-- | app/models/project_services/chat_message/issue_message.rb (renamed from app/models/project_services/slack_service/issue_message.rb) | 2 | ||||
-rw-r--r-- | app/models/project_services/chat_message/merge_message.rb (renamed from app/models/project_services/slack_service/merge_message.rb) | 2 | ||||
-rw-r--r-- | app/models/project_services/chat_message/note_message.rb (renamed from app/models/project_services/slack_service/note_message.rb) | 2 | ||||
-rw-r--r-- | app/models/project_services/chat_message/pipeline_message.rb (renamed from app/models/project_services/slack_service/pipeline_message.rb) | 2 | ||||
-rw-r--r-- | app/models/project_services/chat_message/push_message.rb (renamed from app/models/project_services/slack_service/push_message.rb) | 2 | ||||
-rw-r--r-- | app/models/project_services/chat_message/wiki_page_message.rb (renamed from app/models/project_services/slack_service/wiki_page_message.rb) | 2 | ||||
-rw-r--r-- | app/models/project_services/chat_service.rb | 141 | ||||
-rw-r--r-- | app/models/project_services/mattermost_service.rb | 41 | ||||
-rw-r--r-- | app/models/project_services/mattermost_slash_commands_service.rb | 12 | ||||
-rw-r--r-- | app/models/project_services/slack_service.rb | 174 | ||||
-rw-r--r-- | app/models/service.rb | 1 |
14 files changed, 215 insertions, 171 deletions
diff --git a/app/models/project.rb b/app/models/project.rb index 2c726cfc5df..19c2d24212d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -95,6 +95,7 @@ class Project < ActiveRecord::Base has_one :asana_service, dependent: :destroy has_one :gemnasium_service, dependent: :destroy has_one :mattermost_slash_commands_service, dependent: :destroy + has_one :mattermost_service, dependent: :destroy has_one :slack_service, dependent: :destroy has_one :buildkite_service, dependent: :destroy has_one :bamboo_service, dependent: :destroy diff --git a/app/models/project_services/slack_service/base_message.rb b/app/models/project_services/chat_message/base_message.rb index f1182824687..a03605d01fb 100644 --- a/app/models/project_services/slack_service/base_message.rb +++ b/app/models/project_services/chat_message/base_message.rb @@ -1,6 +1,6 @@ require 'slack-notifier' -class SlackService +module ChatMessage class BaseMessage def initialize(params) raise NotImplementedError diff --git a/app/models/project_services/slack_service/build_message.rb b/app/models/project_services/chat_message/build_message.rb index 0fca4267bad..53e35cb21bf 100644 --- a/app/models/project_services/slack_service/build_message.rb +++ b/app/models/project_services/chat_message/build_message.rb @@ -1,4 +1,4 @@ -class SlackService +module ChatMessage class BuildMessage < BaseMessage attr_reader :sha attr_reader :ref_type diff --git a/app/models/project_services/slack_service/issue_message.rb b/app/models/project_services/chat_message/issue_message.rb index cd87a79d0c6..14fd64e5332 100644 --- a/app/models/project_services/slack_service/issue_message.rb +++ b/app/models/project_services/chat_message/issue_message.rb @@ -1,4 +1,4 @@ -class SlackService +module ChatMessage class IssueMessage < BaseMessage attr_reader :user_name attr_reader :title diff --git a/app/models/project_services/slack_service/merge_message.rb b/app/models/project_services/chat_message/merge_message.rb index b7615c96068..ab5e8b24167 100644 --- a/app/models/project_services/slack_service/merge_message.rb +++ b/app/models/project_services/chat_message/merge_message.rb @@ -1,4 +1,4 @@ -class SlackService +module ChatMessage class MergeMessage < BaseMessage attr_reader :user_name attr_reader :project_name diff --git a/app/models/project_services/slack_service/note_message.rb b/app/models/project_services/chat_message/note_message.rb index 797c5937f09..ca1d7207034 100644 --- a/app/models/project_services/slack_service/note_message.rb +++ b/app/models/project_services/chat_message/note_message.rb @@ -1,4 +1,4 @@ -class SlackService +module ChatMessage class NoteMessage < BaseMessage attr_reader :message attr_reader :user_name diff --git a/app/models/project_services/slack_service/pipeline_message.rb b/app/models/project_services/chat_message/pipeline_message.rb index b6355fc4171..210027565a8 100644 --- a/app/models/project_services/slack_service/pipeline_message.rb +++ b/app/models/project_services/chat_message/pipeline_message.rb @@ -1,4 +1,4 @@ -class SlackService +module ChatMessage class PipelineMessage < BaseMessage attr_reader :ref_type, :ref, :status, :project_name, :project_url, :user_name, :duration, :pipeline_id diff --git a/app/models/project_services/slack_service/push_message.rb b/app/models/project_services/chat_message/push_message.rb index b26f3e9ddce..2d73b71ec37 100644 --- a/app/models/project_services/slack_service/push_message.rb +++ b/app/models/project_services/chat_message/push_message.rb @@ -1,4 +1,4 @@ -class SlackService +module ChatMessage class PushMessage < BaseMessage attr_reader :after attr_reader :before diff --git a/app/models/project_services/slack_service/wiki_page_message.rb b/app/models/project_services/chat_message/wiki_page_message.rb index 160ca3ac115..134083e4504 100644 --- a/app/models/project_services/slack_service/wiki_page_message.rb +++ b/app/models/project_services/chat_message/wiki_page_message.rb @@ -1,4 +1,4 @@ -class SlackService +module ChatMessage class WikiPageMessage < BaseMessage attr_reader :user_name attr_reader :title diff --git a/app/models/project_services/chat_service.rb b/app/models/project_services/chat_service.rb index d36beff5fa6..8ac049ba939 100644 --- a/app/models/project_services/chat_service.rb +++ b/app/models/project_services/chat_service.rb @@ -1,21 +1,148 @@ # Base class for Chat services # This class is not meant to be used directly, but only to inherrit from. class ChatService < Service + include ChatMessage + default_value_for :category, 'chat' - has_many :chat_names, foreign_key: :service_id + prop_accessor :webhook, :username, :channel + boolean_accessor :notify_only_broken_builds, :notify_only_broken_pipelines + + validates :webhook, presence: true, url: true, if: :activated? + + def initialize_properties + # Custom serialized properties initialization + self.supported_events.each { |event| self.class.prop_accessor(event_channel_name(event)) } - def valid_token?(token) - self.respond_to?(:token) && - self.token.present? && - ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token) + if properties.nil? + self.properties = {} + self.notify_only_broken_builds = true + self.notify_only_broken_pipelines = true + end + end + + def can_test? + valid? end def supported_events - [] + %w[push issue confidential_issue merge_request note tag_push + build pipeline wiki_page] end - def trigger(params) + def execute(data) + return unless supported_events.include?(data[:object_kind]) + return unless webhook.present? + + object_kind = data[:object_kind] + + data = data.merge( + project_url: project_url, + 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 = get_message(object_kind, data) + + return false unless message + + opt = {} + + opt[:channel] = get_channel_field(object_kind).presence || channel || default_channel + opt[:username] = username if username + + notifier = Slack::Notifier.new(webhook, opt) + notifier.ping(message.pretext, attachments: message.attachments, fallback: message.fallback) + + true + end + + def event_channel_names + supported_events.map { |event| event_channel_name(event) } + end + + def event_field(event) + fields.find { |field| field[:name] == event_channel_name(event) } + end + + def global_fields + fields.reject { |field| field[:name].end_with?('channel') } + end + + def default_channel raise NotImplementedError end + + private + + def get_message(object_kind, data) + case object_kind + when "push", "tag_push" + PushMessage.new(data) + when "issue" + IssueMessage.new(data) unless is_update?(data) + when "merge_request" + MergeMessage.new(data) unless is_update?(data) + when "note" + NoteMessage.new(data) + when "build" + BuildMessage.new(data) if should_build_be_notified?(data) + when "pipeline" + PipelineMessage.new(data) if should_pipeline_be_notified?(data) + when "wiki_page" + WikiPageMessage.new(data) + end + end + + def get_channel_field(event) + field_name = event_channel_name(event) + self.public_send(field_name) + end + + def build_event_channels + supported_events.reduce([]) do |channels, event| + channels << { type: 'text', name: event_channel_name(event), placeholder: default_channel } + end + end + + def event_channel_name(event) + "#{event}_channel" + end + + def project_name + project.name_with_namespace.gsub(/\s/, '') + end + + def project_url + project.web_url + end + + def is_update?(data) + data[:object_attributes][:action] == 'update' + end + + def should_build_be_notified?(data) + case data[:commit][:status] + when 'success' + !notify_only_broken_builds? + when 'failed' + true + else + false + end + end + + def should_pipeline_be_notified?(data) + case data[:object_attributes][:status] + when 'success' + !notify_only_broken_pipelines? + when 'failed' + true + else + false + end + end end diff --git a/app/models/project_services/mattermost_service.rb b/app/models/project_services/mattermost_service.rb new file mode 100644 index 00000000000..9d61c251a32 --- /dev/null +++ b/app/models/project_services/mattermost_service.rb @@ -0,0 +1,41 @@ +class MattermostService < ChatService + def title + 'Mattermost notifications' + end + + def description + 'Receive event notifications in Mattermost' + end + + def to_param + 'mattermost' + end + + def help + 'This service sends notifications about projects events to Mattermost channels.<br /> + To set up this service: + <ol> + <li><a href="https://docs.mattermost.com/developer/webhooks-incoming.html#enabling-incoming-webhooks">Enable incoming webhooks</a> in your Mattermost installation. </li> + <li><a href="https://docs.mattermost.com/developer/webhooks-incoming.html#creating-integrations-using-incoming-webhooks">Add an incoming webhook</a> in your Mattermost team. The default channel can be overridden for each event. </li> + <li>Paste the webhook <strong>URL</strong> into the field bellow. </li> + <li>Select events below to enable notifications. The channel and username are optional. </li> + </ol>' + end + + def fields + default_fields + build_event_channels + end + + def default_fields + [ + { type: 'text', name: 'webhook', placeholder: 'http://mattermost_host/hooks/...' }, + { type: 'text', name: 'username', placeholder: 'username' }, + { type: 'checkbox', name: 'notify_only_broken_builds' }, + { type: 'checkbox', name: 'notify_only_broken_pipelines' }, + ] + end + + def default_channel + "#town-square" + end +end diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb index 33431f41dc2..3993dfbda17 100644 --- a/app/models/project_services/mattermost_slash_commands_service.rb +++ b/app/models/project_services/mattermost_slash_commands_service.rb @@ -1,8 +1,18 @@ -class MattermostSlashCommandsService < ChatService +class MattermostSlashCommandsService < Service include TriggersHelper prop_accessor :token + def valid_token?(token) + self.respond_to?(:token) && + self.token.present? && + ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token) + end + + def supported_events + [] + end + def can_test? false end diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index e1b937817f4..0df1743c4ba 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -1,25 +1,10 @@ -class SlackService < Service - prop_accessor :webhook, :username, :channel - boolean_accessor :notify_only_broken_builds, :notify_only_broken_pipelines - validates :webhook, presence: true, url: true, if: :activated? - - def initialize_properties - # Custom serialized properties initialization - self.supported_events.each { |event| self.class.prop_accessor(event_channel_name(event)) } - - if properties.nil? - self.properties = {} - self.notify_only_broken_builds = true - self.notify_only_broken_pipelines = true - end - end - +class SlackService < ChatService def title - 'Slack' + 'Slack notifications' end def description - 'A team communication tool for the 21st century' + 'Receive event notifications in Slack' end def to_param @@ -27,150 +12,29 @@ class SlackService < Service end def help - 'This service sends notifications to your Slack channel.<br/> - To setup this Service you need to create a new <b>"Incoming webhook"</b> in your Slack integration panel, - and enter the Webhook URL below.' + 'This service sends notifications about projects events to Slack channels.<br /> + To setup this service: + <ol> + <li><a href="https://slack.com/apps/A0F7XDUAZ-incoming-webhooks">Add an incoming webhook</a> in your Slack team. The default channel can be overridden for each event. </li> + <li>Paste the <strong>Webhook URL</strong> into the field below. </li> + <li>Select events below to enable notifications. The channel and username are optional. </li> + </ol>' end def fields - default_fields = - [ - { type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' }, - { type: 'text', name: 'username', placeholder: 'username' }, - { type: 'text', name: 'channel', placeholder: "#general" }, - { type: 'checkbox', name: 'notify_only_broken_builds' }, - { type: 'checkbox', name: 'notify_only_broken_pipelines' }, - ] - default_fields + build_event_channels end - def supported_events - %w[push issue confidential_issue merge_request note tag_push - build pipeline wiki_page] - end - - def execute(data) - return unless supported_events.include?(data[:object_kind]) - return unless webhook.present? - - object_kind = data[:object_kind] - - data = data.merge( - project_url: project_url, - 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 = get_message(object_kind, data) - - if message - opt = {} - - event_channel = get_channel_field(object_kind) || channel - - opt[:channel] = event_channel if event_channel - opt[:username] = username if username - - notifier = Slack::Notifier.new(webhook, opt) - notifier.ping(message.pretext, attachments: message.attachments, fallback: message.fallback) - - true - else - false - end - end - - def event_channel_names - supported_events.map { |event| event_channel_name(event) } - end - - def event_field(event) - fields.find { |field| field[:name] == event_channel_name(event) } + def default_fields + [ + { type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' }, + { type: 'text', name: 'username', placeholder: 'username' }, + { type: 'checkbox', name: 'notify_only_broken_builds' }, + { type: 'checkbox', name: 'notify_only_broken_pipelines' }, + ] end - def global_fields - fields.reject { |field| field[:name].end_with?('channel') } - end - - private - - def get_message(object_kind, data) - case object_kind - when "push", "tag_push" - PushMessage.new(data) - when "issue" - IssueMessage.new(data) unless is_update?(data) - when "merge_request" - MergeMessage.new(data) unless is_update?(data) - when "note" - NoteMessage.new(data) - when "build" - BuildMessage.new(data) if should_build_be_notified?(data) - when "pipeline" - PipelineMessage.new(data) if should_pipeline_be_notified?(data) - when "wiki_page" - WikiPageMessage.new(data) - end - end - - def get_channel_field(event) - field_name = event_channel_name(event) - self.public_send(field_name) - end - - def build_event_channels - supported_events.reduce([]) do |channels, event| - channels << { type: 'text', name: event_channel_name(event), placeholder: "#general" } - end - end - - def event_channel_name(event) - "#{event}_channel" - end - - def project_name - project.name_with_namespace.gsub(/\s/, '') - end - - def project_url - project.web_url - end - - def is_update?(data) - data[:object_attributes][:action] == 'update' - end - - def should_build_be_notified?(data) - case data[:commit][:status] - when 'success' - !notify_only_broken_builds? - when 'failed' - true - else - false - end - end - - def should_pipeline_be_notified?(data) - case data[:object_attributes][:status] - when 'success' - !notify_only_broken_pipelines? - when 'failed' - true - else - false - end + def default_channel + "#general" end end - -require "slack_service/issue_message" -require "slack_service/push_message" -require "slack_service/merge_message" -require "slack_service/note_message" -require "slack_service/build_message" -require "slack_service/pipeline_message" -require "slack_service/wiki_page_message" diff --git a/app/models/service.rb b/app/models/service.rb index e49a8fa2904..8e58f2a1925 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -220,6 +220,7 @@ class Service < ActiveRecord::Base pivotaltracker pushover redmine + mattermost slack teamcity ] |