fluent-plugin-slack-stakater 0.6.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,289 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'logger'
5
+ require_relative 'slack_client/error'
6
+
7
+ module Fluent
8
+ module SlackClient
9
+ # The base framework of slack client
10
+ class Base
11
+ attr_accessor :log, :debug_dev
12
+ attr_reader :endpoint, :https_proxy
13
+
14
+ # @param [String] endpoint
15
+ #
16
+ # (Incoming Webhook) required
17
+ # https://hooks.slack.com/services/XXX/XXX/XXX
18
+ #
19
+ # (Slackbot) required
20
+ # https://xxxx.slack.com/services/hooks/slackbot?token=XXXXX
21
+ #
22
+ # (Web API) optional and default to be
23
+ # https://slack.com/api/
24
+ #
25
+ # @param [String] https_proxy (optional)
26
+ #
27
+ # https://proxy.foo.bar:port
28
+ #
29
+ def initialize(endpoint = nil, https_proxy = nil)
30
+ self.endpoint = endpoint if endpoint
31
+ self.https_proxy = https_proxy if https_proxy
32
+ @log = Logger.new('/dev/null')
33
+ end
34
+
35
+ def endpoint=(endpoint)
36
+ @endpoint = URI.parse(endpoint)
37
+ end
38
+
39
+ def https_proxy=(https_proxy)
40
+ @https_proxy = URI.parse(https_proxy)
41
+ @proxy_class = Net::HTTP.Proxy(@https_proxy.host, @https_proxy.port)
42
+ end
43
+
44
+ def proxy_class
45
+ @proxy_class ||= Net::HTTP
46
+ end
47
+
48
+ def post(endpoint, params)
49
+ http = proxy_class.new(endpoint.host, endpoint.port)
50
+ http.use_ssl = (endpoint.scheme == 'https')
51
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
52
+ http.set_debug_output(debug_dev) if debug_dev
53
+
54
+ req = Net::HTTP::Post.new(endpoint.request_uri)
55
+ req['Host'] = endpoint.host
56
+ req['Accept'] = 'application/json; charset=utf-8'
57
+ req['User-Agent'] = 'fluent-plugin-slack'
58
+ req.body = encode_body(params)
59
+
60
+ res = http.request(req)
61
+ response_check(res, params)
62
+ end
63
+
64
+ private
65
+
66
+ def encode_body(params)
67
+ raise NotImplementedError
68
+ end
69
+
70
+ def response_check(res, params)
71
+ if res.code != "200"
72
+ raise Error.new(res, params)
73
+ end
74
+ end
75
+
76
+ def filter_params(params)
77
+ params.dup.tap {|p| p[:token] = '[FILTERED]' if p[:token] }
78
+ end
79
+
80
+ # Required to implement to use #with_channels_create
81
+ # channels.create API is available from only Slack Web API
82
+ def api
83
+ raise NotImplementedError
84
+ end
85
+
86
+ def with_channels_create(params = {}, opts = {})
87
+ retries = 1
88
+ begin
89
+ yield
90
+ rescue ChannelNotFoundError => e
91
+ if params[:token] and opts[:auto_channels_create]
92
+ log.warn "out_slack: channel \"#{params[:channel]}\" is not found. try to create the channel, and then retry to post the message."
93
+ api.channels_create({name: params[:channel], token: params[:token]})
94
+ retry if (retries -= 1) >= 0 # one time retry
95
+ else
96
+ raise e
97
+ end
98
+ end
99
+ end
100
+
101
+ def to_json_with_scrub!(params)
102
+ retries = 1
103
+ begin
104
+ params.to_json
105
+ rescue Encoding::UndefinedConversionError => e
106
+ recursive_scrub!(params)
107
+ if (retries -= 1) >= 0 # one time retry
108
+ log.warn "out_slack: to_json `#{params}` failed. retry after scrub!. #{e.message}"
109
+ retry
110
+ else
111
+ raise e
112
+ end
113
+ end
114
+ end
115
+
116
+ def recursive_scrub!(params)
117
+ case params
118
+ when Hash
119
+ params.each {|k, v| recursive_scrub!(v)}
120
+ when Array
121
+ params.each {|elm| recursive_scrub!(elm)}
122
+ when String
123
+ params.force_encoding(Encoding::UTF_8) if params.encoding == Encoding::ASCII_8BIT
124
+ params.scrub!('?') if params.respond_to?(:scrub!)
125
+ else
126
+ params
127
+ end
128
+ end
129
+ end
130
+
131
+ # Slack client for Incoming Webhook
132
+ # https://api.slack.com/incoming-webhooks
133
+ class IncomingWebhook < Base
134
+ def initialize(endpoint, https_proxy = nil)
135
+ super
136
+ end
137
+
138
+ def post_message(params = {}, opts = {})
139
+ log.info { "out_slack: post_message #{params}" }
140
+ post(endpoint, params)
141
+ end
142
+
143
+ private
144
+
145
+ def encode_body(params = {})
146
+ # https://api.slack.com/docs/formatting
147
+ to_json_with_scrub!(params).gsub(/&/, '&amp;').gsub(/</, '&lt;').gsub(/>/, '&gt;')
148
+ end
149
+
150
+ def response_check(res, params)
151
+ super
152
+ unless res.body == 'ok'
153
+ raise Error.new(res, params)
154
+ end
155
+ end
156
+ end
157
+
158
+ # Slack client for Slackbot Remote Control
159
+ # https://api.slack.com/slackbot
160
+ class Slackbot < Base
161
+ def initialize(endpoint, https_proxy = nil)
162
+ super
163
+ end
164
+
165
+ def api
166
+ @api ||= WebApi.new(nil, https_proxy)
167
+ end
168
+
169
+ def post_message(params = {}, opts = {})
170
+ raise ArgumentError, "channel parameter is required" unless params[:channel]
171
+ with_channels_create(params, opts) do
172
+ log.info { "out_slack: post_message #{filter_params(params)}" }
173
+ post(slackbot_endpoint(params), params)
174
+ end
175
+ end
176
+
177
+ private
178
+
179
+ def slackbot_endpoint(params)
180
+ endpoint.dup.tap {|e| e.query += "&channel=#{URI.encode(params[:channel])}" }
181
+ end
182
+
183
+ def encode_body(params = {})
184
+ return params[:text]if params[:text]
185
+ unless params[:attachments]
186
+ raise ArgumentError, 'params[:text] or params[:attachments] is required'
187
+ end
188
+ # handle params[:attachments]
189
+ attachment = Array(params[:attachments]).first # see only the first for now
190
+ # {
191
+ # attachments: [{
192
+ # text: "HERE",
193
+ # }]
194
+ # }
195
+ text = attachment[:text]
196
+ # {
197
+ # attachments: [{
198
+ # fields: [{
199
+ # title: "title",
200
+ # value: "HERE",
201
+ # }]
202
+ # }]
203
+ # }
204
+ if text.nil? and attachment[:fields]
205
+ text = Array(attachment[:fields]).first[:value] # see only the first for now
206
+ end
207
+ text
208
+ end
209
+
210
+ def response_check(res, params)
211
+ if res.body == 'channel_not_found'
212
+ raise ChannelNotFoundError.new(res, params)
213
+ elsif res.body != 'ok'
214
+ raise Error.new(res, params)
215
+ end
216
+ end
217
+ end
218
+
219
+ # Slack client for Web API
220
+ class WebApi < Base
221
+ DEFAULT_ENDPOINT = "https://slack.com/api/".freeze
222
+
223
+ def api
224
+ self
225
+ end
226
+
227
+ def endpoint
228
+ @endpoint ||= URI.parse(DEFAULT_ENDPOINT)
229
+ end
230
+
231
+ def post_message_endpoint
232
+ @post_message_endpoint ||= URI.join(endpoint, "chat.postMessage")
233
+ end
234
+
235
+ def channels_create_endpoint
236
+ @channels_create_endpoint ||= URI.join(endpoint, "channels.create")
237
+ end
238
+
239
+ # Sends a message to a channel.
240
+ #
241
+ # @see https://api.slack.com/methods/chat.postMessage
242
+ # @see https://github.com/slackhq/slack-api-docs/blob/master/methods/chat.postMessage.md
243
+ # @see https://github.com/slackhq/slack-api-docs/blob/master/methods/chat.postMessage.json
244
+ def post_message(params = {}, opts = {})
245
+ with_channels_create(params, opts) do
246
+ log.info { "out_slack: post_message #{filter_params(params)}" }
247
+ post(post_message_endpoint, params)
248
+ end
249
+ end
250
+
251
+ # Creates a channel.
252
+ #
253
+ # NOTE: Bot user can not create a channel. Token must be issued by Normal User Account
254
+ # @see https://api.slack.com/bot-users
255
+ #
256
+ # @see https://api.slack.com/methods/channels.create
257
+ # @see https://github.com/slackhq/slack-api-docs/blob/master/methods/channels.create.md
258
+ # @see https://github.com/slackhq/slack-api-docs/blob/master/methods/channels.create.json
259
+ def channels_create(params = {}, opts = {})
260
+ log.info { "out_slack: channels_create #{filter_params(params)}" }
261
+ post(channels_create_endpoint, params)
262
+ end
263
+
264
+ private
265
+
266
+ def encode_body(params = {})
267
+ body = params.dup
268
+ if params[:attachments]
269
+ body[:attachments] = to_json_with_scrub!(params[:attachments])
270
+ end
271
+ URI.encode_www_form(body)
272
+ end
273
+
274
+ def response_check(res, params)
275
+ super
276
+ res_params = JSON.parse(res.body)
277
+ return if res_params['ok']
278
+ case res_params['error']
279
+ when 'channel_not_found'
280
+ raise ChannelNotFoundError.new(res, params)
281
+ when 'name_taken'
282
+ raise NameTakenError.new(res, params)
283
+ else
284
+ raise Error.new(res, params)
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end
@@ -0,0 +1,24 @@
1
+ require 'net/http'
2
+
3
+ module Fluent
4
+ module SlackClient
5
+ class Error < StandardError
6
+ attr_reader :res, :req_params
7
+
8
+ def initialize(res, req_params = {})
9
+ @res = res
10
+ @req_params = req_params.dup
11
+ end
12
+
13
+ def message
14
+ @req_params[:token] = '[FILTERED]' if @req_params[:token]
15
+ "res.code:#{@res.code}, res.body:#{@res.body}, req_params:#{@req_params}"
16
+ end
17
+
18
+ alias :to_s :message
19
+ end
20
+
21
+ class ChannelNotFoundError < Error; end
22
+ class NameTakenError < Error; end
23
+ end
24
+ end
data/test.sh ADDED
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ echo '{"message":"message"}' | bundle exec fluent-cat tag
@@ -0,0 +1,566 @@
1
+ require_relative '../test_helper'
2
+ require 'fluent/plugin/out_slack'
3
+ require 'time'
4
+
5
+ class SlackOutputTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+ super
9
+ Fluent::Test.setup
10
+ @icon_url = 'http://www.google.com/s2/favicons?domain=www.google.de'
11
+ end
12
+
13
+ CONFIG = %[
14
+ channel channel
15
+ webhook_url https://hooks.slack.com/services/XXXX/XXXX/XXX
16
+ ]
17
+
18
+ def default_payload
19
+ {
20
+ channel: '#channel',
21
+ mrkdwn: true,
22
+ link_names: true,
23
+ }
24
+ end
25
+
26
+ def default_attachment
27
+ {
28
+ mrkdwn_in: %w[text fields],
29
+ }
30
+ end
31
+
32
+ def create_driver(conf = CONFIG)
33
+ Fluent::Test::BufferedOutputTestDriver.new(Fluent::SlackOutput).configure(conf)
34
+ end
35
+
36
+ # old version compatibility with v0.4.0"
37
+ def test_old_config
38
+ # default check
39
+ d = create_driver
40
+ assert_equal true, d.instance.localtime
41
+ assert_equal nil, d.instance.username # 'fluentd' break lower version compatibility
42
+ assert_equal nil, d.instance.color # 'good' break lower version compatibility
43
+ assert_equal nil, d.instance.icon_emoji # ':question:' break lower version compatibility
44
+ assert_equal nil, d.instance.icon_url
45
+ assert_equal true, d.instance.mrkdwn
46
+ assert_equal true, d.instance.link_names
47
+ assert_equal nil, d.instance.parse
48
+
49
+ assert_nothing_raised do
50
+ create_driver(CONFIG + %[api_key testtoken])
51
+ end
52
+
53
+ # incoming webhook endpoint was changed. team option should be ignored
54
+ assert_nothing_raised do
55
+ create_driver(CONFIG + %[team sowasowa])
56
+ end
57
+
58
+ # rtm? it was not calling `rtm.start`. rtm option was removed and should be ignored
59
+ assert_nothing_raised do
60
+ create_driver(CONFIG + %[rtm true])
61
+ end
62
+
63
+ # channel should be URI.unescape-ed
64
+ d = create_driver(CONFIG + %[channel %23test])
65
+ assert_equal '#test', d.instance.channel
66
+
67
+ # timezone should work
68
+ d = create_driver(CONFIG + %[timezone Asia/Tokyo])
69
+ assert_equal 'Asia/Tokyo', d.instance.timezone
70
+ end
71
+
72
+ def test_configure
73
+ d = create_driver(%[
74
+ channel channel
75
+ time_format %Y/%m/%d %H:%M:%S
76
+ username username
77
+ color bad
78
+ icon_emoji :ghost:
79
+ token XX-XX-XX
80
+ title slack notice!
81
+ message %s
82
+ message_keys message
83
+ ])
84
+ assert_equal '#channel', d.instance.channel
85
+ assert_equal '%Y/%m/%d %H:%M:%S', d.instance.time_format
86
+ assert_equal 'username', d.instance.username
87
+ assert_equal 'bad', d.instance.color
88
+ assert_equal ':ghost:', d.instance.icon_emoji
89
+ assert_equal 'XX-XX-XX', d.instance.token
90
+ assert_equal '%s', d.instance.message
91
+ assert_equal ['message'], d.instance.message_keys
92
+
93
+ # Allow DM
94
+ d = create_driver(CONFIG + %[channel @test])
95
+ assert_equal '@test', d.instance.channel
96
+
97
+ assert_raise(Fluent::ConfigError) do
98
+ create_driver(CONFIG + %[title %s %s\ntitle_keys foo])
99
+ end
100
+
101
+ assert_raise(Fluent::ConfigError) do
102
+ create_driver(CONFIG + %[message %s %s\nmessage_keys foo])
103
+ end
104
+
105
+ assert_raise(Fluent::ConfigError) do
106
+ create_driver(CONFIG + %[channel %s %s\nchannel_keys foo])
107
+ end
108
+ end
109
+
110
+ def test_slack_configure
111
+ # One of webhook_url or slackbot_url, or token is required
112
+ assert_raise(Fluent::ConfigError) do
113
+ create_driver(%[channel foo])
114
+ end
115
+
116
+ # webhook_url is an empty string
117
+ assert_raise(Fluent::ConfigError) do
118
+ create_driver(%[channel foo\nwebhook_url])
119
+ end
120
+
121
+ # webhook without channel (it works because webhook has a default channel)
122
+ assert_nothing_raised do
123
+ create_driver(%[webhook_url https://example.com/path/to/webhook])
124
+ end
125
+
126
+ # slackbot_url is an empty string
127
+ assert_raise(Fluent::ConfigError) do
128
+ create_driver(%[channel foo\nslackbot_url])
129
+ end
130
+
131
+ # slackbot without channel
132
+ assert_raise(Fluent::ConfigError) do
133
+ create_driver(%[slackbot_url https://example.com/path/to/slackbot])
134
+ end
135
+
136
+ # token is an empty string
137
+ assert_raise(Fluent::ConfigError) do
138
+ create_driver(%[channel foo\ntoken])
139
+ end
140
+
141
+ # slack webapi token without channel
142
+ assert_raise(Fluent::ConfigError) do
143
+ create_driver(%[token some_token])
144
+ end
145
+ end
146
+
147
+ def test_timezone_configure
148
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
149
+
150
+ d = create_driver(CONFIG + %[localtime])
151
+ with_timezone('Asia/Tokyo') do
152
+ assert_equal true, d.instance.localtime
153
+ assert_equal "07:00:00", d.instance.timef.format(time)
154
+ end
155
+
156
+ d = create_driver(CONFIG + %[utc])
157
+ with_timezone('Asia/Tokyo') do
158
+ assert_equal false, d.instance.localtime
159
+ assert_equal "22:00:00", d.instance.timef.format(time)
160
+ end
161
+
162
+ d = create_driver(CONFIG + %[timezone Asia/Taipei])
163
+ with_timezone('Asia/Tokyo') do
164
+ assert_equal "Asia/Taipei", d.instance.timezone
165
+ assert_equal "06:00:00", d.instance.timef.format(time)
166
+ end
167
+ end
168
+
169
+ def test_time_format_configure
170
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
171
+
172
+ d = create_driver(CONFIG + %[time_format %Y/%m/%d %H:%M:%S])
173
+ with_timezone('Asia/Tokyo') do
174
+ assert_equal "2014/01/02 07:00:00", d.instance.timef.format(time)
175
+ end
176
+ end
177
+
178
+ def test_buffer_configure
179
+ assert_nothing_raised do
180
+ create_driver(CONFIG + %[buffer_type file\nbuffer_path tmp/])
181
+ end
182
+ end
183
+
184
+ def test_icon_configure
185
+ # default
186
+ d = create_driver(CONFIG)
187
+ assert_equal nil, d.instance.icon_emoji
188
+ assert_equal nil, d.instance.icon_url
189
+
190
+ # either of icon_emoji or icon_url can be specified
191
+ assert_raise(Fluent::ConfigError) do
192
+ d = create_driver(CONFIG + %[icon_emoji :ghost:\nicon_url #{@icon_url}])
193
+ end
194
+
195
+ # icon_emoji
196
+ d = create_driver(CONFIG + %[icon_emoji :ghost:])
197
+ assert_equal ':ghost:', d.instance.icon_emoji
198
+ assert_equal nil, d.instance.icon_url
199
+
200
+ # icon_url
201
+ d = create_driver(CONFIG + %[icon_url #{@icon_url}])
202
+ assert_equal nil, d.instance.icon_emoji
203
+ assert_equal @icon_url, d.instance.icon_url
204
+ end
205
+
206
+ def test_link_names_configure
207
+ # default
208
+ d = create_driver(CONFIG)
209
+ assert_equal true, d.instance.link_names
210
+
211
+ # true
212
+ d = create_driver(CONFIG + %[link_names true])
213
+ assert_equal true, d.instance.link_names
214
+
215
+ # false
216
+ d = create_driver(CONFIG + %[link_names false])
217
+ assert_equal false, d.instance.link_names
218
+ end
219
+
220
+ def test_parse_configure
221
+ # default
222
+ d = create_driver(CONFIG)
223
+ assert_equal nil, d.instance.parse
224
+
225
+ # none
226
+ d = create_driver(CONFIG + %[parse none])
227
+ assert_equal 'none', d.instance.parse
228
+
229
+ # full
230
+ d = create_driver(CONFIG + %[parse full])
231
+ assert_equal 'full', d.instance.parse
232
+
233
+ # invalid
234
+ assert_raise(Fluent::ConfigError) do
235
+ d = create_driver(CONFIG + %[parse invalid])
236
+ end
237
+ end
238
+
239
+ def test_mrkwn_configure
240
+ # default
241
+ d = create_driver(CONFIG)
242
+ assert_equal true, d.instance.mrkdwn
243
+ assert_equal %w[text fields], d.instance.mrkdwn_in
244
+
245
+ # true
246
+ d = create_driver(CONFIG + %[mrkdwn true])
247
+ assert_equal true, d.instance.mrkdwn
248
+ assert_equal %w[text fields], d.instance.mrkdwn_in
249
+
250
+ # false
251
+ d = create_driver(CONFIG + %[mrkdwn false])
252
+ assert_equal false, d.instance.mrkdwn
253
+ assert_equal nil, d.instance.mrkdwn_in
254
+ end
255
+
256
+ def test_https_proxy_configure
257
+ # default
258
+ d = create_driver(CONFIG)
259
+ assert_equal nil, d.instance.slack.https_proxy
260
+ assert_equal Net::HTTP, d.instance.slack.proxy_class
261
+
262
+ # https_proxy
263
+ d = create_driver(CONFIG + %[https_proxy https://proxy.foo.bar:443])
264
+ assert_equal URI.parse('https://proxy.foo.bar:443'), d.instance.slack.https_proxy
265
+ assert_not_equal Net::HTTP, d.instance.slack.proxy_class # Net::HTTP.Proxy
266
+ end
267
+
268
+ def test_auto_channels_create_configure
269
+ # default
270
+ d = create_driver(CONFIG)
271
+ assert_equal false, d.instance.auto_channels_create
272
+ assert_equal({}, d.instance.post_message_opts)
273
+
274
+ # require `token`
275
+ assert_raise(Fluent::ConfigError) do
276
+ d = create_driver(CONFIG + %[auto_channels_create true])
277
+ end
278
+
279
+ # auto_channels_create
280
+ d = create_driver(CONFIG + %[auto_channels_create true\ntoken XXX-XX-XXX])
281
+ assert_equal true, d.instance.auto_channels_create
282
+ assert_equal({auto_channels_create: true}, d.instance.post_message_opts)
283
+ end
284
+
285
+ def test_default_incoming_webhook
286
+ d = create_driver(%[
287
+ channel channel
288
+ webhook_url https://hooks.slack.com/services/XXX/XXX/XXX
289
+ ])
290
+ assert_equal Fluent::SlackClient::IncomingWebhook, d.instance.slack.class
291
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
292
+ d.tag = 'test'
293
+ mock(d.instance.slack).post_message(default_payload.merge({
294
+ text: "sowawa1\nsowawa2\n",
295
+ }), {})
296
+ with_timezone('Asia/Tokyo') do
297
+ d.emit({message: 'sowawa1'}, time)
298
+ d.emit({message: 'sowawa2'}, time)
299
+ d.run
300
+ end
301
+ end
302
+
303
+ def test_default_slackbot
304
+ d = create_driver(%[
305
+ channel channel
306
+ slackbot_url https://xxxxx.slack.com/services/hooks/slackbot?token=XXXXXXX
307
+ ])
308
+ assert_equal Fluent::SlackClient::Slackbot, d.instance.slack.class
309
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
310
+ d.tag = 'test'
311
+ mock(d.instance.slack).post_message(default_payload.merge({
312
+ text: "sowawa1\nsowawa2\n",
313
+ }), {})
314
+ with_timezone('Asia/Tokyo') do
315
+ d.emit({message: 'sowawa1'}, time)
316
+ d.emit({message: 'sowawa2'}, time)
317
+ d.run
318
+ end
319
+ end
320
+
321
+ def test_default_slack_api
322
+ d = create_driver(%[
323
+ channel channel
324
+ token XX-XX-XX
325
+ ])
326
+ assert_equal Fluent::SlackClient::WebApi, d.instance.slack.class
327
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
328
+ d.tag = 'test'
329
+ mock(d.instance.slack).post_message(default_payload.merge({
330
+ token: 'XX-XX-XX',
331
+ text: "sowawa1\nsowawa2\n",
332
+ }), {})
333
+ with_timezone('Asia/Tokyo') do
334
+ d.emit({message: 'sowawa1'}, time)
335
+ d.emit({message: 'sowawa2'}, time)
336
+ d.run
337
+ end
338
+ end
339
+
340
+ def test_title_payload
341
+ title = "mytitle"
342
+ d = create_driver(CONFIG + %[title #{title}])
343
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
344
+ d.tag = 'test'
345
+ # attachments field should be changed to show the title
346
+ mock(d.instance.slack).post_message(default_payload.merge({
347
+ attachments: [default_attachment.merge({
348
+ fallback: title,
349
+ fields: [
350
+ {
351
+ title: title,
352
+ value: "sowawa1\nsowawa2\n",
353
+ }
354
+ ],
355
+ })]
356
+ }), {})
357
+ with_timezone('Asia/Tokyo') do
358
+ d.emit({message: 'sowawa1'}, time)
359
+ d.emit({message: 'sowawa2'}, time)
360
+ d.run
361
+ end
362
+ end
363
+
364
+ def test_title_payload_with_verbose_fallback_option
365
+ title = "mytitle"
366
+ d = create_driver(CONFIG + %[title #{title}\nverbose_fallback true])
367
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
368
+ d.tag = 'test'
369
+ # attachments field should be changed to show the title
370
+ mock(d.instance.slack).post_message(default_payload.merge({
371
+ attachments: [default_attachment.merge({
372
+ fallback: "#{title} sowawa1\nsowawa2\n",
373
+ fields: [
374
+ {
375
+ title: title,
376
+ value: "sowawa1\nsowawa2\n",
377
+ }
378
+ ],
379
+ })]
380
+ }), {})
381
+ with_timezone('Asia/Tokyo') do
382
+ d.emit({message: 'sowawa1'}, time)
383
+ d.emit({message: 'sowawa2'}, time)
384
+ d.run
385
+ end
386
+ end
387
+
388
+ def test_color_payload
389
+ color = 'good'
390
+ d = create_driver(CONFIG + %[color #{color}])
391
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
392
+ d.tag = 'test'
393
+ # attachments field should be changed to show the title
394
+ mock(d.instance.slack).post_message(default_payload.merge({
395
+ attachments: [default_attachment.merge({
396
+ color: color,
397
+ fallback: "sowawa1\nsowawa2\n",
398
+ text: "sowawa1\nsowawa2\n",
399
+ })]
400
+ }), {})
401
+ with_timezone('Asia/Tokyo') do
402
+ d.emit({message: 'sowawa1'}, time)
403
+ d.emit({message: 'sowawa2'}, time)
404
+ d.run
405
+ end
406
+ end
407
+
408
+ def test_plain_payload
409
+ d = create_driver(CONFIG)
410
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
411
+ d.tag = 'test'
412
+ # attachments field should be changed to show the title
413
+ mock(d.instance.slack).post_message(default_payload.merge({
414
+ text: "sowawa1\nsowawa2\n",
415
+ }), {})
416
+ with_timezone('Asia/Tokyo') do
417
+ d.emit({message: 'sowawa1'}, time)
418
+ d.emit({message: 'sowawa2'}, time)
419
+ d.run
420
+ end
421
+ end
422
+
423
+ def test_title_keys
424
+ d = create_driver(CONFIG + %[title [%s] %s\ntitle_keys time,tag])
425
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
426
+ d.tag = 'test'
427
+ # attachments field should be changed to show the title
428
+ mock(d.instance.slack).post_message(default_payload.merge({
429
+ attachments: [default_attachment.merge({
430
+ fallback: "[07:00:00] #{d.tag}",
431
+ fields: [
432
+ {
433
+ title: "[07:00:00] #{d.tag}",
434
+ value: "sowawa1\nsowawa2\n",
435
+ }
436
+ ],
437
+ })]
438
+ }), {})
439
+ with_timezone('Asia/Tokyo') do
440
+ d.emit({message: 'sowawa1'}, time)
441
+ d.emit({message: 'sowawa2'}, time)
442
+ d.run
443
+ end
444
+ end
445
+
446
+ def test_message_keys
447
+ d = create_driver(CONFIG + %[message [%s] %s %s\nmessage_keys time,tag,message])
448
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
449
+ d.tag = 'test'
450
+ mock(d.instance.slack).post_message(default_payload.merge({
451
+ text: "[07:00:00] test sowawa1\n[07:00:00] test sowawa2\n",
452
+ }), {})
453
+ with_timezone('Asia/Tokyo') do
454
+ d.emit({message: 'sowawa1'}, time)
455
+ d.emit({message: 'sowawa2'}, time)
456
+ d.run
457
+ end
458
+ end
459
+
460
+ def test_channel_keys
461
+ d = create_driver(CONFIG + %[channel %s\nchannel_keys channel])
462
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
463
+ d.tag = 'test'
464
+ mock(d.instance.slack).post_message(default_payload.merge({
465
+ channel: '#channel1',
466
+ text: "sowawa1\n",
467
+ }), {})
468
+ mock(d.instance.slack).post_message(default_payload.merge({
469
+ channel: '#channel2',
470
+ text: "sowawa2\n",
471
+ }), {})
472
+ with_timezone('Asia/Tokyo') do
473
+ d.emit({message: 'sowawa1', channel: 'channel1'}, time)
474
+ d.emit({message: 'sowawa2', channel: 'channel2'}, time)
475
+ d.run
476
+ end
477
+ end
478
+
479
+ def test_icon_emoji
480
+ d = create_driver(CONFIG + %[icon_emoji :ghost:])
481
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
482
+ d.tag = 'test'
483
+ mock(d.instance.slack).post_message(default_payload.merge({
484
+ icon_emoji: ':ghost:',
485
+ text: "foo\n",
486
+ }), {})
487
+ with_timezone('Asia/Tokyo') do
488
+ d.emit({message: 'foo'}, time)
489
+ d.run
490
+ end
491
+ end
492
+
493
+ def test_icon_url
494
+ d = create_driver(CONFIG + %[icon_url #{@icon_url}])
495
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
496
+ d.tag = 'test'
497
+ mock(d.instance.slack).post_message(default_payload.merge({
498
+ icon_url: @icon_url,
499
+ text: "foo\n",
500
+ }), {})
501
+ with_timezone('Asia/Tokyo') do
502
+ d.emit({message: 'foo'}, time)
503
+ d.run
504
+ end
505
+ end
506
+
507
+ def test_mrkdwn
508
+ d = create_driver(CONFIG + %[mrkdwn true])
509
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
510
+ d.tag = 'test'
511
+ mock(d.instance.slack).post_message(default_payload.merge({
512
+ mrkdwn: true,
513
+ text: "foo\n",
514
+ }), {})
515
+ with_timezone('Asia/Tokyo') do
516
+ d.emit({message: 'foo'}, time)
517
+ d.run
518
+ end
519
+ end
520
+
521
+ def test_mrkdwn_in
522
+ d = create_driver(CONFIG + %[mrkdwn true\ncolor good])
523
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
524
+ d.tag = 'test'
525
+ mock(d.instance.slack).post_message(default_payload.merge({
526
+ attachments: [default_attachment.merge({
527
+ color: "good",
528
+ fallback: "foo\n",
529
+ text: "foo\n",
530
+ mrkdwn_in: ["text", "fields"],
531
+ })]
532
+ }), {})
533
+ with_timezone('Asia/Tokyo') do
534
+ d.emit({message: 'foo'}, time)
535
+ d.run
536
+ end
537
+ end
538
+
539
+ def test_link_names
540
+ d = create_driver(CONFIG + %[link_names true])
541
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
542
+ d.tag = 'test'
543
+ mock(d.instance.slack).post_message(default_payload.merge({
544
+ link_names: true,
545
+ text: "foo\n",
546
+ }), {})
547
+ with_timezone('Asia/Tokyo') do
548
+ d.emit({message: 'foo'}, time)
549
+ d.run
550
+ end
551
+ end
552
+
553
+ def test_parse
554
+ d = create_driver(CONFIG + %[parse full])
555
+ time = Time.parse("2014-01-01 22:00:00 UTC").to_i
556
+ d.tag = 'test'
557
+ mock(d.instance.slack).post_message(default_payload.merge({
558
+ parse: "full",
559
+ text: "foo\n",
560
+ }), {})
561
+ with_timezone('Asia/Tokyo') do
562
+ d.emit({message: 'foo'}, time)
563
+ d.run
564
+ end
565
+ end
566
+ end