slack_message 3.2.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bdc04bd65ae2c98be234fbea88cec4d5373553bdb758a28959ef7c2515e8420c
4
- data.tar.gz: 68beb2e7a60e6cb6b4f2904e1f5335e934b447070b29c9362caca0ce2148fad5
3
+ metadata.gz: 494d2f51af95a53df19c42dcaa9f88168cf44509b1675dbe001a42fdf39250dc
4
+ data.tar.gz: 306050272f54d60a1b6820f315ac51c0b563af71862847efdb2940464ca201b8
5
5
  SHA512:
6
- metadata.gz: f6f5ca7c5ced4e495439429eb9aba32fbb4567f64eff04ff3eff9ba5e05caab3faac995d18e830e58a9bb432c40a4cc4ecc4e383cff71bf7fa7e4cf4f8b47e75
7
- data.tar.gz: 5ad0999dd393d908a3ddd1e8e83d82f01ff8296f5178f7817cabd73553987c37a82107034d50a20c52a671fd12af5d6771e20962dbc0dcccc7eb135865a9a465
6
+ metadata.gz: d8e43baeab730d3c6e7acbb1232b98d4a265b55260ab3c12bcc87325cb81d03f00b7ff05ff72667105e10e123c604630eac7331db238823374b8584d8242e611
7
+ data.tar.gz: 2c2709365ec780ca44c6960414861c9479eb3b2918d547ca3f3299745aa382e6affd6dfd46a364c27097f5e562939913e098c52bc72cb29911f7c9c669756d30
data/CHANGELOG.md CHANGED
@@ -1,7 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.4.0] - 2023-02-21
4
+ - Add full support for ruby 3.0.x kwargs
5
+
6
+ ## [3.3.0] - 2022-07-21
7
+ - Differentiate errors for bad JSON versus invalid block data.
8
+ - Expand tests to cover many more cases.
9
+ - Add validation for block size limit in Slack API.
10
+ - Add CI check to prevent checkin of `fit`, `fcontext` etc.
11
+
3
12
  ## [3.2.0] - 2022-06-23
4
- - Fix bugs introduced by accidental checkin of incomplete refactor
13
+ - Fix bugs introduced by accidental checkin of incomplete refactor.
5
14
 
6
15
  ## [3.1.0] - 2022-04-18
7
16
  - Methods from the calling context can now be called within a section block.
@@ -41,6 +41,10 @@ module SlackMessage::Api
41
41
  raise ArgumentError, "Tried to send an entirely empty message."
42
42
  end
43
43
 
44
+ if params[:blocks].length > 50
45
+ raise ArgumentError, "Message cannot contain more than 50 blocks."
46
+ end
47
+
44
48
  icon = payload.custom_bot_icon || profile[:icon]
45
49
  if icon =~ /^:\w+:$/
46
50
  params[:icon_emoji] = icon
@@ -80,15 +84,17 @@ module SlackMessage::Api
80
84
  raise ArgumentError, "Tried to send an entirely empty message."
81
85
  end
82
86
 
87
+ if params[:blocks].length > 50
88
+ raise ArgumentError, "Message cannot contain more than 50 blocks."
89
+ end
90
+
83
91
  if SlackMessage::Configuration.debugging?
84
92
  warn params.inspect
85
93
  end
86
94
 
87
95
  response = update_message(profile, params)
88
- body = JSON.parse(response.body)
89
- error = body.fetch("error", "")
90
96
 
91
- SlackMessage::ErrorHandling.raise_post_response_errors(response, message, profile)
97
+ SlackMessage::ErrorHandling.raise_update_response_errors(response, params, profile)
92
98
  SlackMessage::Response.new(response, profile[:handle])
93
99
  end
94
100
 
@@ -80,8 +80,8 @@ class SlackMessage::Dsl
80
80
  # delegation to allow terse syntax without e.g. `section`
81
81
 
82
82
  def text(*args); default_section.text(*args); end
83
- def link_button(*args); default_section.link_button(*args); end
84
- def accessory_image(*args); default_section.accessory_image(*args); end
83
+ def link_button(*args, **kwargs); default_section.link_button(*args, **kwargs); end
84
+ def accessory_image(*args, **kwargs); default_section.accessory_image(*args, **kwargs); end
85
85
  def blank_line(*args); default_section.blank_line(*args); end
86
86
  def link(*args); default_section.link(*args); end
87
87
  def list_item(*args); default_section.list_item(*args); end
@@ -7,8 +7,10 @@ class SlackMessage::ErrorHandling
7
7
  body = JSON.parse(response.body)
8
8
  error = body.fetch("error", "")
9
9
 
10
- if ["invalid_blocks", "invalid_blocks_format"].include?(error)
11
- raise SlackMessage::ApiError, "Couldn't send Slack message because the serialized message had an invalid format"
10
+ if error == "invalid_blocks"
11
+ raise SlackMessage::ApiError, "Couldn't send Slack message because the request contained invalid blocks:\n#{blocks_message(params[:blocks])}"
12
+ elsif error == "invalid_blocks_format"
13
+ raise SlackMessage::ApiError, "Couldn't send Slack message because blocks is not a valid JSON object or doesn't match the Block Kit syntax:\n#{blocks_message(params[:blocks])}"
12
14
  elsif error == "channel_not_found"
13
15
  raise SlackMessage::ApiError, "Tried to send Slack message to non-existent channel or user '#{params[:channel]}'"
14
16
 
@@ -34,21 +36,23 @@ class SlackMessage::ErrorHandling
34
36
  elsif response.code != "200"
35
37
  raise SlackMessage::ApiError, "Got an error back from the Slack API (HTTP #{response.code}):\n#{response.body}"
36
38
  elsif !(error.nil? || error == "")
37
- raise SlackMessage::ApiError, "Received error response from Slack during message posting:\n#{response.body}"
39
+ raise SlackMessage::ApiError, "Received error response '#{error}' from Slack during message posting:\n#{response.body}"
38
40
  end
39
41
  end
40
42
 
41
- def self.raise_update_response_errors(response, message, profile)
43
+ def self.raise_update_response_errors(response, params, profile)
42
44
  body = JSON.parse(response.body)
43
45
  error = body.fetch("error", "")
44
46
 
45
- if ["invalid_blocks", "invalid_blocks_format"].include?(error)
46
- raise SlackMessage::ApiError, "Couldn't update Slack message because the serialized message had an invalid format"
47
+ if error == "invalid_blocks"
48
+ raise SlackMessage::ApiError, "Couldn't update Slack message because the request contained invalid blocks:\n#{blocks_message(params[:blocks])}"
49
+ elsif error == "invalid_blocks_format"
50
+ raise SlackMessage::ApiError, "Couldn't update Slack message because blocks is not a valid JSON object or doesn't match the Block Kit syntax:\n#{blocks_message(params[:blocks])}"
47
51
  elsif error == "channel_not_found"
48
- raise SlackMessage::ApiError, "Tried to update Slack message to non-existent channel or user '#{message.channel}'"
52
+ raise SlackMessage::ApiError, "Tried to update Slack message to non-existent channel or user '#{params[:channel]}'"
49
53
 
50
54
  elsif error == "message_not_found"
51
- raise SlackMessage::ApiError, "Tried to update Slack message, but the message wasn't found (timestamp '#{message.timestamp}' for channel '#{message.channel}'"
55
+ raise SlackMessage::ApiError, "Tried to update Slack message, but the message wasn't found (timestamp '#{params[:ts]}' for channel '#{params[:channel]}'"
52
56
  elsif error == "cant_update_message"
53
57
  raise SlackMessage::ApiError, "Couldn't update message because the message type isn't able to be updated, or #{profile[:handle]} isn't allowed to update it"
54
58
  elsif error == "edit_window_closed"
@@ -68,7 +72,7 @@ class SlackMessage::ErrorHandling
68
72
  elsif response.code != "200"
69
73
  raise SlackMessage::ApiError, "Got an error back from the Slack API (HTTP #{response.code}):\n#{response.body}"
70
74
  elsif !(error.nil? || error == "")
71
- raise SlackMessage::ApiError, "Received error response from Slack during message update:\n#{response.body}"
75
+ raise SlackMessage::ApiError, "Received error response '#{error}' from Slack during message update:\n#{response.body}"
72
76
  end
73
77
  end
74
78
 
@@ -98,7 +102,7 @@ class SlackMessage::ErrorHandling
98
102
  elsif response.code != "200"
99
103
  raise SlackMessage::ApiError, "Got an error back from the Slack API (HTTP #{response.code}):\n#{response.body}"
100
104
  elsif !(error.nil? || error == "")
101
- raise SlackMessage::ApiError, "Received error response from Slack during message delete:\n#{response.body}"
105
+ raise SlackMessage::ApiError, "Received error response '#{error}' from Slack during message delete:\n#{response.body}"
102
106
  end
103
107
  end
104
108
 
@@ -127,4 +131,12 @@ class SlackMessage::ErrorHandling
127
131
  raise SlackMessage::ApiError, "Received error response from Slack during user lookup:\n#{response.body}"
128
132
  end
129
133
  end
134
+
135
+ def self.blocks_message(blocks)
136
+ if SlackMessage::Configuration.debugging?
137
+ JSON.pretty_generate(blocks)
138
+ else
139
+ "[Enable debugging in configuration to view block data.]"
140
+ end
141
+ end
130
142
  end
@@ -40,30 +40,32 @@ module SlackMessage::RSpec
40
40
  FauxResponse = Struct.new(:code, :body)
41
41
 
42
42
  def self.included(_)
43
- SlackMessage::Api.undef_method(:post_message)
44
- SlackMessage::Api.define_singleton_method(:post_message) do |profile, params|
45
- @@listeners.each do |listener|
46
- listener.record_call(params.merge(profile: profile))
43
+ [:post_message, :update_message, :delete_message].each do |method|
44
+ SlackMessage::Api.undef_method(method)
45
+ SlackMessage::Api.define_singleton_method(method) do |profile, params|
46
+ @@listeners.each do |listener|
47
+ listener.record_call(params.merge(profile: profile))
48
+ end
49
+
50
+ response = {
51
+ "ok" => true,
52
+ "channel" => "C12345678",
53
+ "ts" => "1635863996.002300",
54
+ "message" => { "type"=>"message", "subtype"=>"bot_message",
55
+ "text"=>"foo",
56
+ "ts"=>"1635863996.002300",
57
+ "username"=>"SlackMessage",
58
+ "icons"=>{"emoji"=>":successkid:"},
59
+ "bot_id"=>"B1234567890",
60
+ "blocks"=> [{"type"=>"section",
61
+ "block_id"=>"hAh7",
62
+ "text"=>{"type"=>"mrkdwn", "text"=>"foo", "verbatim"=>false}}
63
+ ]
64
+ }
65
+ }.merge(@@custom_response).to_json
66
+
67
+ return FauxResponse.new(@@response_code, response)
47
68
  end
48
-
49
- response = {
50
- "ok" => true,
51
- "channel" => "C12345678",
52
- "ts" => "1635863996.002300",
53
- "message" => { "type"=>"message", "subtype"=>"bot_message",
54
- "text"=>"foo",
55
- "ts"=>"1635863996.002300",
56
- "username"=>"SlackMessage",
57
- "icons"=>{"emoji"=>":successkid:"},
58
- "bot_id"=>"B1234567890",
59
- "blocks"=> [{"type"=>"section",
60
- "block_id"=>"hAh7",
61
- "text"=>{"type"=>"mrkdwn", "text"=>"foo", "verbatim"=>false}}
62
- ]
63
- }
64
- }.merge(@@custom_response).to_json
65
-
66
- return FauxResponse.new(@@response_code, response)
67
69
  end
68
70
 
69
71
  SlackMessage::Api.undef_method(:look_up_user_by_email)
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |gem|
2
2
  gem.name = 'slack_message'
3
- gem.version = "3.2.0"
3
+ gem.version = "3.4.0"
4
4
  gem.summary = "A nice DSL for composing rich messages in Slack"
5
5
  gem.authors = ["Joe Mastey"]
6
6
  gem.email = 'hello@joemastey.com'
@@ -127,6 +127,14 @@ RSpec.describe SlackMessage do
127
127
  SlackMessage.post_to('#general') { text "foo" }
128
128
  }.to post_to_slack.with_content_matching(/foo/)
129
129
  end
130
+
131
+ it "lets you send links with custom styles" do
132
+ expect {
133
+ SlackMessage.post_to('#general') do
134
+ link_button 'Does this person exist?', 'https://thispersondoesnotexist.com/image', style: :danger
135
+ end
136
+ }.to post_to_slack.with_content_matching(/danger/)
137
+ end
130
138
  end
131
139
 
132
140
  describe "API convenience" do
@@ -176,18 +184,6 @@ RSpec.describe SlackMessage do
176
184
  SlackMessage::RSpec.reset_mock_response
177
185
  end
178
186
 
179
- it "raises nice error messages when API methods return errors" do
180
- SlackMessage::RSpec.respond_with({'error' => 'nuffin'})
181
-
182
- expect {
183
- SlackMessage.post_to('#general') { text 'nuh uh' }
184
- }.to raise_error(SlackMessage::ApiError)
185
-
186
- expect {
187
- SlackMessage.post_as(:schmoebot) { text 'nuh uh' }
188
- }.to raise_error(SlackMessage::ApiError)
189
- end
190
-
191
187
  it "raises for redirects" do
192
188
  SlackMessage::RSpec.respond_with(code: '302')
193
189
 
@@ -196,24 +192,83 @@ RSpec.describe SlackMessage do
196
192
  }.to raise_error(SlackMessage::ApiError)
197
193
  end
198
194
 
199
- it "raises errors w/ updates too" do
195
+ it "even raises errors during deletes" do
200
196
  message = SlackMessage.post_to('#general') { text 'nuh uh' }
201
197
 
202
198
  SlackMessage::RSpec.respond_with({'error' => 'bad choice'})
203
199
 
204
200
  expect {
205
- SlackMessage.update(message) { text 'nuh uh' }
201
+ SlackMessage.delete(message)
206
202
  }.to raise_error(SlackMessage::ApiError)
207
203
  end
208
204
 
209
- it "even raises errors during deletes" do
210
- message = SlackMessage.post_to('#general') { text 'nuh uh' }
205
+ shared_examples 'post api error message' do |error, error_message|
206
+ it "responds to posts with error code '#{error}' with the expected message" do
207
+ SlackMessage::RSpec.respond_with({'error' => error})
211
208
 
212
- SlackMessage::RSpec.respond_with({'error' => 'bad choice'})
209
+ expect {
210
+ SlackMessage.post_to('#general') { text 'nuh uh' }
211
+ }.to raise_error(SlackMessage::ApiError).with_message(error_message)
213
212
 
214
- expect {
215
- SlackMessage.delete(message) { text 'nuh uh' }
216
- }.to raise_error(SlackMessage::ApiError)
213
+ expect {
214
+ SlackMessage.post_as(:schmoebot) { text 'nuh uh' }
215
+ }.to raise_error(SlackMessage::ApiError).with_message(error_message)
216
+ end
217
+ end
218
+
219
+ shared_examples 'update api error message' do |error, error_message|
220
+ it "responds to updates with error code '#{error}' with the expected message" do
221
+ message = SlackMessage.post_to('#general') { text 'nuh uh' }
222
+
223
+ SlackMessage::RSpec.respond_with({'error' => error})
224
+
225
+ expect {
226
+ SlackMessage.update(message) { text 'nuh uh' }
227
+ }.to raise_error(SlackMessage::ApiError).with_message(error_message)
228
+ end
217
229
  end
230
+
231
+ shared_examples 'delete api error message' do |error, error_message|
232
+ it "responds to updates with error code '#{error}' with the expected message" do
233
+ message = SlackMessage.post_to('#general') { text 'nuh uh' }
234
+
235
+ SlackMessage::RSpec.respond_with({'error' => error})
236
+
237
+ expect {
238
+ SlackMessage.delete(message)
239
+ }.to raise_error(SlackMessage::ApiError).with_message(error_message)
240
+ end
241
+ end
242
+
243
+ shared_examples 'block api error message' do |error, error_message|
244
+ include_examples 'post api error message', error, error_message
245
+ include_examples 'update api error message', error, error_message
246
+ end
247
+
248
+ include_examples 'block api error message', 'nuffin', /Received error response 'nuffin' from Slack/
249
+ include_examples 'block api error message', 'invalid_blocks', /because the request contained invalid blocks:\n\[Enable debugging in configuration to view block data\.\]/
250
+ include_examples 'block api error message', 'invalid_blocks_format', /because blocks is not a valid JSON object or doesn't match the Block Kit syntax:\n\[Enable debugging in configuration to view block data\.\]/
251
+ include_examples 'block api error message', 'channel_not_found', /Slack message to non-existent channel or user/
252
+ include_examples 'block api error message', 'invalid_auth', /because the API key for profile '.*' is wrong, or the app has insufficient permissions \(invalid_auth\)/
253
+ include_examples 'block api error message', 'message_too_long', /but the message was too long/
254
+ include_examples 'block api error message', 'invalid_arguments', /with invalid payload/
255
+ include_examples 'block api error message', 'rate_limited', /because you've reached your rate limit/
256
+
257
+ # Scheduling messages
258
+ include_examples 'post api error message', 'invalid_time', /because you requested an invalid time/
259
+ include_examples 'post api error message', 'time_in_past', /because you requested a time in the past \(or too close to now\)/
260
+ include_examples 'post api error message', 'time_too_far', /because you requested a time more than 120 days in the future/
261
+
262
+ # Updating messages
263
+ include_examples 'update api error message', 'message_not_found', /but the message wasn't found/
264
+
265
+ # Deleting messages
266
+ include_examples 'delete api error message', 'nuffin', /Received error response 'nuffin' from Slack/
267
+ include_examples 'delete api error message', 'invalid_scheduled_message_id', /Can't delete message because the ID was invalid, or the message has already posted/
268
+ include_examples 'delete api error message', 'message_not_found', /but the message wasn't found/
269
+ include_examples 'delete api error message', 'cant_delete_message', /Can't delete message because 'default' doesn't have permission to/
270
+ include_examples 'delete api error message', 'compliance_exports_prevent_deletion', /Can't delete message because team compliance settings prevent it/
271
+ include_examples 'delete api error message', 'invalid_auth', /because the API key for profile '.*' is wrong, or the app has insufficient permissions \(invalid_auth\)/
272
+ include_examples 'delete api error message', 'rate_limited', /because you've reached your rate limit/
218
273
  end
219
274
  end
data/spec/spec_helper.rb CHANGED
@@ -26,7 +26,11 @@ RSpec.configure do |config|
26
26
  # is tagged with `:focus`, all examples get run. RSpec also provides
27
27
  # aliases for `it`, `describe`, and `context` that include `:focus`
28
28
  # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
29
- config.filter_run_when_matching :focus
29
+ if ENV['CI'] == "true"
30
+ config.filter_run_when_matching :focus => lambda { raise ArgumentError, "Don't filter tests on checked in code." }
31
+ else
32
+ config.filter_run_when_matching :focus
33
+ end
30
34
 
31
35
  # Limits the available syntax to the non-monkey patched syntax that is
32
36
  # recommended. For more details, see:
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slack_message
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Mastey
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-23 00:00:00.000000000 Z
11
+ date: 2023-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec