slack_message 3.0.2 → 3.3.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: 100c64a0e3204d4715ffde4c0a11e0b380c7966289b8b841ef2ab7cb73f657b2
4
- data.tar.gz: 3213f9c1817a211f57685002025dc72929f41652ba7c9e69e62d5314508fc3dd
3
+ metadata.gz: 0b35e974e04457e2d9e3b55af0c5c7562d0381c76477e9a85143c66e8d605071
4
+ data.tar.gz: 21892df8e52b365ade154424d2c98a55434efb541e144d046d208fe8b1243f24
5
5
  SHA512:
6
- metadata.gz: 238cdf0160b6c2101cbabdadef3818dd0bcd2163d9a3ababba854affa0fbaca2533489aa203b27a2309af8e85aa7e814786a9ce8eb29dd51d949cefc1d209921
7
- data.tar.gz: b0ad7654cfcac475f32eb81ede6e730046ef3721190ae1f255ceaff58331fbf6d4ee570a290dc16e660ca32611cc8639998677c5980f2eaaedd72388293b22df
6
+ metadata.gz: 720e5bcf8d91f355939474b09f9d2a9830178037dca5c067ecfc4c6585631673e2ea13d888338ff1fb7a3d25ae1009b5f1a97f7a8244f1f06696b437227f6260
7
+ data.tar.gz: b7fa2c16e3d5a31ac18605bda1e1eacdd367c6e94f34779c50964a92c679b8edcdc1168d3fa808392401669f55bbeae30aec607ec44a2cfd55b916d0ad0fe70d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.3.0] - 2022-07-21
4
+ - Differentiate errors for bad JSON versus invalid block data.
5
+ - Expand tests to cover many more cases.
6
+ - Add validation for block size limit in Slack API.
7
+ - Add CI check to prevent checkin of `fit`, `fcontext` etc.
8
+
9
+ ## [3.2.0] - 2022-06-23
10
+ - Fix bugs introduced by accidental checkin of incomplete refactor.
11
+
12
+ ## [3.1.0] - 2022-04-18
13
+ - Methods from the calling context can now be called within a section block.
14
+
3
15
  ## [3.0.2] - 2022-04-16
4
16
  - Fix tests on ruby 3.0.
5
17
  - More adjustments and additions to docs.
@@ -22,13 +22,10 @@ module SlackMessage::Api
22
22
  raise SlackMessage::ApiError, "Received empty 200 response from Slack when looking up user info. Check your API key."
23
23
  end
24
24
 
25
- begin
26
- payload = JSON.parse(response.body)
27
- rescue
28
- raise SlackMessage::ApiError, "Unable to parse JSON response from Slack API\n#{response.body}"
29
- end
25
+ SlackMessage::ErrorHandling.raise_user_lookup_response_errors(response, email, profile)
26
+
27
+ payload = JSON.parse(response.body)
30
28
 
31
- SlackMessage::ErrorHandling.raise_user_lookup_errors(response, target, profile)
32
29
  payload["user"]["id"]
33
30
  end
34
31
 
@@ -44,6 +41,10 @@ module SlackMessage::Api
44
41
  raise ArgumentError, "Tried to send an entirely empty message."
45
42
  end
46
43
 
44
+ if params[:blocks].length > 50
45
+ raise ArgumentError, "Message cannot contain more than 50 blocks."
46
+ end
47
+
47
48
  icon = payload.custom_bot_icon || profile[:icon]
48
49
  if icon =~ /^:\w+:$/
49
50
  params[:icon_emoji] = icon
@@ -83,15 +84,17 @@ module SlackMessage::Api
83
84
  raise ArgumentError, "Tried to send an entirely empty message."
84
85
  end
85
86
 
87
+ if params[:blocks].length > 50
88
+ raise ArgumentError, "Message cannot contain more than 50 blocks."
89
+ end
90
+
86
91
  if SlackMessage::Configuration.debugging?
87
92
  warn params.inspect
88
93
  end
89
94
 
90
95
  response = update_message(profile, params)
91
- body = JSON.parse(response.body)
92
- error = body.fetch("error", "")
93
96
 
94
- SlackMessage::ErrorHandling.raise_post_response_errors(response, message, profile)
97
+ SlackMessage::ErrorHandling.raise_update_response_errors(response, params, profile)
95
98
  SlackMessage::Response.new(response, profile[:handle])
96
99
  end
97
100
 
@@ -272,6 +272,10 @@ class SlackMessage::Dsl
272
272
 
273
273
  body
274
274
  end
275
+
276
+ def method_missing(meth, *args, &blk)
277
+ @parent.send meth, *args, &blk
278
+ end
275
279
  end
276
280
 
277
281
  class List
@@ -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,11 +102,17 @@ 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
 
105
- def self.raise_user_lookup_response_errors(payload)
109
+ def self.raise_user_lookup_response_errors(response, email, profile)
110
+ begin
111
+ payload = JSON.parse(response.body)
112
+ rescue
113
+ raise SlackMessage::ApiError, "Unable to parse JSON response from Slack API\n#{response.body}"
114
+ end
115
+
106
116
  error = payload["error"]
107
117
 
108
118
  if error == "users_not_found"
@@ -121,4 +131,12 @@ class SlackMessage::ErrorHandling
121
131
  raise SlackMessage::ApiError, "Received error response from Slack during user lookup:\n#{response.body}"
122
132
  end
123
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
124
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.0.2"
3
+ gem.version = "3.3.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'
@@ -4,6 +4,10 @@ RSpec.describe SlackMessage do
4
4
 
5
5
  describe "DSL" do
6
6
  describe "#build" do
7
+ def outer_method
8
+ "foo"
9
+ end
10
+
7
11
  it "renders some JSON" do
8
12
  SlackMessage.configure do |config|
9
13
  config.clear_profiles!
@@ -13,11 +17,17 @@ RSpec.describe SlackMessage do
13
17
  expected_output = [
14
18
  { type: "section",
15
19
  text: { text: "foo", type: "mrkdwn" }
16
- }
20
+ },
21
+ { type: "section",
22
+ text: { text: "foo", type: "mrkdwn" }
23
+ },
17
24
  ]
18
25
 
19
26
  output = SlackMessage.build do
20
- text "foo"
27
+ text outer_method()
28
+ section do
29
+ text outer_method()
30
+ end
21
31
  end
22
32
 
23
33
  expect(output).to eq(expected_output)
@@ -50,13 +60,6 @@ RSpec.describe SlackMessage do
50
60
  end
51
61
  end
52
62
 
53
- fit do
54
- SlackMessage.build do
55
- notification_text 'one'
56
- notification_text 'two'
57
- end
58
- end
59
-
60
63
  it "can assert expectations against posts" do
61
64
  expect {
62
65
  SlackMessage.post_to('#lieutenant') { text "foo" }
@@ -173,18 +176,6 @@ RSpec.describe SlackMessage do
173
176
  SlackMessage::RSpec.reset_mock_response
174
177
  end
175
178
 
176
- it "raises nice error messages when API methods return errors" do
177
- SlackMessage::RSpec.respond_with({'error' => 'nuffin'})
178
-
179
- expect {
180
- SlackMessage.post_to('#general') { text 'nuh uh' }
181
- }.to raise_error(SlackMessage::ApiError)
182
-
183
- expect {
184
- SlackMessage.post_as(:schmoebot) { text 'nuh uh' }
185
- }.to raise_error(SlackMessage::ApiError)
186
- end
187
-
188
179
  it "raises for redirects" do
189
180
  SlackMessage::RSpec.respond_with(code: '302')
190
181
 
@@ -193,24 +184,83 @@ RSpec.describe SlackMessage do
193
184
  }.to raise_error(SlackMessage::ApiError)
194
185
  end
195
186
 
196
- it "raises errors w/ updates too" do
187
+ it "even raises errors during deletes" do
197
188
  message = SlackMessage.post_to('#general') { text 'nuh uh' }
198
189
 
199
190
  SlackMessage::RSpec.respond_with({'error' => 'bad choice'})
200
191
 
201
192
  expect {
202
- SlackMessage.update(message) { text 'nuh uh' }
193
+ SlackMessage.delete(message)
203
194
  }.to raise_error(SlackMessage::ApiError)
204
195
  end
205
196
 
206
- it "even raises errors during deletes" do
207
- message = SlackMessage.post_to('#general') { text 'nuh uh' }
197
+ shared_examples 'post api error message' do |error, error_message|
198
+ it "responds to posts with error code '#{error}' with the expected message" do
199
+ SlackMessage::RSpec.respond_with({'error' => error})
208
200
 
209
- SlackMessage::RSpec.respond_with({'error' => 'bad choice'})
201
+ expect {
202
+ SlackMessage.post_to('#general') { text 'nuh uh' }
203
+ }.to raise_error(SlackMessage::ApiError).with_message(error_message)
210
204
 
211
- expect {
212
- SlackMessage.delete(message) { text 'nuh uh' }
213
- }.to raise_error(SlackMessage::ApiError)
205
+ expect {
206
+ SlackMessage.post_as(:schmoebot) { text 'nuh uh' }
207
+ }.to raise_error(SlackMessage::ApiError).with_message(error_message)
208
+ end
209
+ end
210
+
211
+ shared_examples 'update api error message' do |error, error_message|
212
+ it "responds to updates with error code '#{error}' with the expected message" do
213
+ message = SlackMessage.post_to('#general') { text 'nuh uh' }
214
+
215
+ SlackMessage::RSpec.respond_with({'error' => error})
216
+
217
+ expect {
218
+ SlackMessage.update(message) { text 'nuh uh' }
219
+ }.to raise_error(SlackMessage::ApiError).with_message(error_message)
220
+ end
214
221
  end
222
+
223
+ shared_examples 'delete api error message' do |error, error_message|
224
+ it "responds to updates with error code '#{error}' with the expected message" do
225
+ message = SlackMessage.post_to('#general') { text 'nuh uh' }
226
+
227
+ SlackMessage::RSpec.respond_with({'error' => error})
228
+
229
+ expect {
230
+ SlackMessage.delete(message)
231
+ }.to raise_error(SlackMessage::ApiError).with_message(error_message)
232
+ end
233
+ end
234
+
235
+ shared_examples 'block api error message' do |error, error_message|
236
+ include_examples 'post api error message', error, error_message
237
+ include_examples 'update api error message', error, error_message
238
+ end
239
+
240
+ include_examples 'block api error message', 'nuffin', /Received error response 'nuffin' from Slack/
241
+ include_examples 'block api error message', 'invalid_blocks', /because the request contained invalid blocks:\n\[Enable debugging in configuration to view block data\.\]/
242
+ 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\.\]/
243
+ include_examples 'block api error message', 'channel_not_found', /Slack message to non-existent channel or user/
244
+ include_examples 'block api error message', 'invalid_auth', /because the API key for profile '.*' is wrong, or the app has insufficient permissions \(invalid_auth\)/
245
+ include_examples 'block api error message', 'message_too_long', /but the message was too long/
246
+ include_examples 'block api error message', 'invalid_arguments', /with invalid payload/
247
+ include_examples 'block api error message', 'rate_limited', /because you've reached your rate limit/
248
+
249
+ # Scheduling messages
250
+ include_examples 'post api error message', 'invalid_time', /because you requested an invalid time/
251
+ include_examples 'post api error message', 'time_in_past', /because you requested a time in the past \(or too close to now\)/
252
+ include_examples 'post api error message', 'time_too_far', /because you requested a time more than 120 days in the future/
253
+
254
+ # Updating messages
255
+ include_examples 'update api error message', 'message_not_found', /but the message wasn't found/
256
+
257
+ # Deleting messages
258
+ include_examples 'delete api error message', 'nuffin', /Received error response 'nuffin' from Slack/
259
+ 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/
260
+ include_examples 'delete api error message', 'message_not_found', /but the message wasn't found/
261
+ include_examples 'delete api error message', 'cant_delete_message', /Can't delete message because 'default' doesn't have permission to/
262
+ include_examples 'delete api error message', 'compliance_exports_prevent_deletion', /Can't delete message because team compliance settings prevent it/
263
+ include_examples 'delete api error message', 'invalid_auth', /because the API key for profile '.*' is wrong, or the app has insufficient permissions \(invalid_auth\)/
264
+ include_examples 'delete api error message', 'rate_limited', /because you've reached your rate limit/
215
265
  end
216
266
  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.0.2
4
+ version: 3.3.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-04-16 00:00:00.000000000 Z
11
+ date: 2022-07-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec