slack_message 3.0.2 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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