slack_service.rb 3.77 KB
Newer Older
1
class SlackService < Service
2
  prop_accessor :webhook, :username, :channel
3
  boolean_accessor :notify_only_broken_builds
4
  validates :webhook, presence: true, url: true, if: :activated?
5

Kamil Trzcinski's avatar
Kamil Trzcinski committed
6
  def initialize_properties
7
    # Custom serialized properties initialization
8
    self.supported_events.each { |event| self.class.prop_accessor(event_channel_name(event)) }
9

Kamil Trzcinski's avatar
Kamil Trzcinski committed
10 11 12 13 14
    if properties.nil?
      self.properties = {}
      self.notify_only_broken_builds = true
    end
  end
15

16 17 18 19 20 21 22 23 24 25 26 27
  def title
    'Slack'
  end

  def description
    'A team communication tool for the 21st century'
  end

  def to_param
    'slack'
  end

28 29 30 31 32 33
  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.'
  end

34
  def fields
35 36 37 38
    default_fields =
      [
        { type: 'text', name: 'webhook',   placeholder: 'https://hooks.slack.com/services/...' },
        { type: 'text', name: 'username', placeholder: 'username' },
39
        { type: 'text', name: 'channel', placeholder: "#general" },
40 41 42 43
        { type: 'checkbox', name: 'notify_only_broken_builds' },
      ]

    default_fields + build_event_channels
44 45
  end

46
  def supported_events
47
    %w(push issue merge_request note tag_push build wiki_page)
48 49
  end

50
  def execute(data)
51
    return unless supported_events.include?(data[:object_kind])
52 53
    return unless webhook.present?

54 55 56
    object_kind = data[:object_kind]

    data = data.merge(
57 58
      project_url: project_url,
      project_name: project_name
59 60 61 62 63 64
    )

    # 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.

65 66
    message = \
      case object_kind
67
      when "push", "tag_push"
68 69 70 71 72
        PushMessage.new(data)
      when "issue"
        IssueMessage.new(data) unless is_update?(data)
      when "merge_request"
        MergeMessage.new(data) unless is_update?(data)
73 74
      when "note"
        NoteMessage.new(data)
75 76
      when "build"
        BuildMessage.new(data) if should_build_be_notified?(data)
77 78
      when "wiki_page"
        WikiPageMessage.new(data)
79
      end
80

81
    opt = {}
82 83 84 85

    event_channel = get_channel_field(object_kind) || channel

    opt[:channel] = event_channel if event_channel
86 87
    opt[:username] = username if username

88 89
    if message
      notifier = Slack::Notifier.new(webhook, opt)
90
      notifier.ping(message.pretext, attachments: message.attachments, fallback: message.fallback)
91
    end
92 93
  end

94 95 96 97
  def event_channel_names
    supported_events.map { |event| event_channel_name(event) }
  end

98 99 100 101 102 103 104 105
  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

106 107
  private

108 109
  def get_channel_field(event)
    field_name = event_channel_name(event)
110
    self.public_send(field_name)
111 112 113
  end

  def build_event_channels
114 115
    supported_events.reduce([]) do |channels, event|
      channels << { type: 'text', name: event_channel_name(event), placeholder: "#general" }
116 117 118 119 120 121 122
    end
  end

  def event_channel_name(event)
    "#{event}_channel"
  end

123 124 125 126 127 128 129
  def project_name
    project.name_with_namespace.gsub(/\s/, '')
  end

  def project_url
    project.web_url
  end
130 131 132 133

  def is_update?(data)
    data[:object_attributes][:action] == 'update'
  end
134 135 136 137 138 139 140 141 142 143 144

  def should_build_be_notified?(data)
    case data[:commit][:status]
    when 'success'
      !notify_only_broken_builds?
    when 'failed'
      true
    else
      false
    end
  end
145
end
146 147 148

require "slack_service/issue_message"
require "slack_service/push_message"
Douwe Maan's avatar
Douwe Maan committed
149
require "slack_service/merge_message"
150
require "slack_service/note_message"
151
require "slack_service/build_message"
152
require "slack_service/wiki_page_message"