slack_message 3.0.0 → 3.0.1
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 +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +14 -17
- data/docs/01_configuration.md +12 -12
- data/docs/03_message_dsl.md +51 -8
- data/docs/_config.yml +6 -0
- data/docs/index.md +43 -1
- data/lib/slack_message/api.rb +13 -32
- data/lib/slack_message/error_handling.rb +124 -0
- data/lib/slack_message/rspec.rb +39 -17
- data/lib/slack_message.rb +1 -0
- data/slack_message.gemspec +1 -1
- data/spec/slack_message_spec.rb +53 -7
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '098b332b278741ef63ef105fc7e79cbc7ef131a03dca79b0ed40a1d86001cae1'
|
4
|
+
data.tar.gz: 40a4028d38c154fdb1d1835e0f536adc2e57671223290e43a139be1d70a359c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f99700c2fcb33cc7ad9e671f250dc23efffd47e6f4e36d9fdc7740c9ed1a8d4b224b523b6870b13cb3b164a0ae720f93824fae803368083919d776ec2c221236
|
7
|
+
data.tar.gz: 1fb9ccc9bf2f9b9fabca5e1cdcdbcde87100f1c056613f0af1450f344bc82adcc5f7366d2f6ab8fb54e34f241018464283c750f2a0d022a67112b9570fc1287e
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [3.0.1] - 2021-12-22
|
4
|
+
- Major overhaul of error handling and expansion on which errors trigger
|
5
|
+
friendly messages for users.
|
6
|
+
- More additions to the docs, and working github pages integration.
|
7
|
+
- It's my birthday!
|
8
|
+
|
3
9
|
## [3.0.0] - 2021-12-19
|
4
10
|
- Return a more structured object from successful message sends.
|
5
11
|
- Add the ability to edit or delete a message.
|
data/README.md
CHANGED
@@ -12,8 +12,14 @@ SlackMessage.post_to('#general') do
|
|
12
12
|
text "We did it @here! :thumbsup:"
|
13
13
|
end
|
14
14
|
```
|
15
|
+
|
16
|
+
### The Docs
|
15
17
|
|
16
|
-
|
18
|
+
You'll find much more information about how to use SlackMessage by visiting
|
19
|
+
[the docs](https://jmmastey.github.io/slack_message).
|
20
|
+
|
21
|
+
|
22
|
+
### Posting
|
17
23
|
|
18
24
|
SlackMessage is able to build all kinds of rich messages for you, and has been
|
19
25
|
a real joy to use for the author at least. To understand a bit more about the
|
@@ -48,12 +54,7 @@ SlackMessage.post_to('#general') do
|
|
48
54
|
end
|
49
55
|
```
|
50
56
|
|
51
|
-
###
|
52
|
-
|
53
|
-
You'll find much more information about how to use SlackMessage by visiting
|
54
|
-
[the docs](https://jmmastey.github.io/slack_message).
|
55
|
-
|
56
|
-
#### Opinionated Stances
|
57
|
+
### Opinionated Stances
|
57
58
|
|
58
59
|
This gem is intended to stay simple. Other Slack gems have lots of config
|
59
60
|
options and abilities, which makes them powerful, but makes them a pain to use.
|
@@ -61,6 +62,8 @@ options and abilities, which makes them powerful, but makes them a pain to use.
|
|
61
62
|
Accordingly, SlackMessage is developed with some strong opinions in mind:
|
62
63
|
|
63
64
|
* SlackMessage has no dependencies. Your lockfile is enough of a mess already.
|
65
|
+
* A DSL that focuses on code that is easy to write, read, and maintain. Only
|
66
|
+
features that can be implemented with that in mind are included.
|
64
67
|
* The code to build a message should look a lot like the message itself. Code
|
65
68
|
that is simple to read and understand is a priority.
|
66
69
|
* Webhooks are passé. Only Slack Apps are supported now.
|
@@ -71,28 +74,22 @@ Accordingly, SlackMessage is developed with some strong opinions in mind:
|
|
71
74
|
to look it up as an email address.
|
72
75
|
* A few little hacks on the block syntax, such as adding a `blank_line` (which
|
73
76
|
doesn't exist in the API), or leading spaces.
|
74
|
-
* Configuration is kept
|
75
|
-
possible should occur just once via configuration and not on every call.
|
77
|
+
* Configuration is kept simple, with helpers for frequently reused bots.
|
76
78
|
|
77
79
|
Some behaviors that are still planned but not yet added:
|
78
80
|
|
79
81
|
* any interactive elements at all: https://api.slack.com/interactivity/handling
|
80
82
|
* multiple recipients: https://api.slack.com/methods/conversations.open
|
81
|
-
* more interesting return types for your message
|
82
|
-
* richer text formatting (for instance, `ul` is currently a hack)
|
83
83
|
* more mrkdwn syntax, like quotes or code blocks
|
84
|
-
* more and better organized testing capability
|
84
|
+
* more and better organized testing capability (scheduled messages, editing, deleting)
|
85
85
|
* posting ephemeral messages: https://api.slack.com/methods/chat.postEphemeral
|
86
|
-
* some Rspec test harness for scheduled messages, editing, deleting (probably going to need a test overhaul)
|
87
86
|
|
88
|
-
Contributing
|
89
|
-
------------
|
87
|
+
### Contributing
|
90
88
|
|
91
89
|
Contributions are very welcome. Fork, fix, submit pull. Since simplicity of API is a strong priority, so opening an issue to discuss possible interface changes would be wise.
|
92
90
|
|
93
91
|
Contribution is expected to conform to the [Contributor Covenant](https://github.com/jmmastey/slack_message/blob/master/CODE_OF_CONDUCT.md).
|
94
92
|
|
95
|
-
License
|
96
|
-
------------
|
93
|
+
### License
|
97
94
|
|
98
95
|
This software is released under the [MIT License](https://github.com/jmmastey/slack_message/blob/master/MIT-LICENSE).
|
data/docs/01_configuration.md
CHANGED
@@ -95,18 +95,18 @@ end
|
|
95
95
|
You will now see warnings detailing all params sent to the API.
|
96
96
|
|
97
97
|
```ruby
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
98
|
+
{
|
99
|
+
:channel=>"#general",
|
100
|
+
:username=>"Builds",
|
101
|
+
:blocks=>[
|
102
|
+
{:type=>"section", :text=>{
|
103
|
+
:type=>"mrkdwn",
|
104
|
+
:text=>"Build Stability is Looking Ruff :dog:"
|
105
|
+
}}
|
106
|
+
],
|
107
|
+
:text=>"Build Issues",
|
108
|
+
:post_at=>1639421171,
|
109
|
+
}
|
110
110
|
```
|
111
111
|
|
112
112
|
Note this includes data that is not included in `SlackMessage.build`.
|
data/docs/03_message_dsl.md
CHANGED
@@ -152,11 +152,54 @@ end
|
|
152
152
|
# :style=>:danger}}]
|
153
153
|
```
|
154
154
|
|
155
|
-
####
|
155
|
+
#### Ordered and Unordered Lists
|
156
156
|
|
157
|
-
-
|
157
|
+
The Slack API doesn't have native support for HTML-style ordered and unordered
|
158
|
+
lists, but there are convenience methods in SlackMessage to render a close
|
159
|
+
approximation.
|
158
160
|
|
159
|
-
|
161
|
+
```ruby
|
162
|
+
SlackMessage.build do
|
163
|
+
section do
|
164
|
+
text '*Pet Goodness Tiers*'
|
165
|
+
|
166
|
+
ol([
|
167
|
+
'tiny pigs',
|
168
|
+
'reptiles',
|
169
|
+
'dogs',
|
170
|
+
'cats',
|
171
|
+
])
|
172
|
+
end
|
173
|
+
|
174
|
+
section do
|
175
|
+
text '_voted by_'
|
176
|
+
ul(['Joe', 'Emily', 'Sophia', 'Matt'])
|
177
|
+
end
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
181
|
+
Because Slack automatically collapses leading whitespace, indention of lists is
|
182
|
+
handled using unicode emspaces. Bullets for unordered lists are also unicode
|
183
|
+
characters to avoid being read as markdown.
|
184
|
+
|
185
|
+
#### List Items (e.g. HTML dt & dd)
|
186
|
+
|
187
|
+
When trying to represent title / value lists, you can use the "list item" block
|
188
|
+
type to pass a set of values. Slack does not allow you to customize how many
|
189
|
+
items are shown per line, so you'll just have to work with it.
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
SlackMessage.build do
|
193
|
+
text 'Import results are available!'
|
194
|
+
|
195
|
+
list_item 'Import Date', Date.today.to_s
|
196
|
+
list_item 'Items Imported', 55_000
|
197
|
+
list_item 'Errors', 23
|
198
|
+
list_item 'Bad Values', errors.map(&:to_s)
|
199
|
+
end
|
200
|
+
```
|
201
|
+
|
202
|
+
#### Including Multiple Sections
|
160
203
|
|
161
204
|
Adding more sections is trivial. Simply declare each section and it will be
|
162
205
|
separated in the rendered message. This can often occur when looping.
|
@@ -200,10 +243,10 @@ section. Because of how implicit sections are built, it may look like this works
|
|
200
243
|
for simple messages. You may have troubles when you start adding more
|
201
244
|
complicated elements to your messages.
|
202
245
|
|
203
|
-
|
204
|
-
|
246
|
+
#### Images
|
247
|
+
TODO: image, accessory_image
|
205
248
|
|
206
|
-
|
249
|
+
#### Footers (Context)
|
207
250
|
|
208
251
|
Slack allows you to add a small additional piece of text to your message, which
|
209
252
|
will be rendered in italics and small text. It can support both links and emoji,
|
@@ -228,7 +271,7 @@ end
|
|
228
271
|
Context does not belong to a section, and is per-message, not per-section.
|
229
272
|
Specifying more than one context will simply overwrite previous calls.
|
230
273
|
|
231
|
-
|
274
|
+
#### Bot Customization
|
232
275
|
|
233
276
|
By default - and with scheduled messages - Slack will use the name and icon of
|
234
277
|
the Slack app whose API key you configured. As seen before, it's
|
@@ -253,7 +296,7 @@ The `bot_icon` can be specified as either an emoji (`:example:`), or a URL
|
|
253
296
|
pointing to an image (`http://mysite.com/shipit.png`). Any other value seems to
|
254
297
|
cause an error.
|
255
298
|
|
256
|
-
|
299
|
+
#### Custom Notification Text
|
257
300
|
|
258
301
|
For users who have notifications turned on, Slack will provide a small message
|
259
302
|
preview when you send them a message. By default, this preview will take the
|
data/docs/_config.yml
ADDED
data/docs/index.md
CHANGED
@@ -1,7 +1,49 @@
|
|
1
|
-
* [Configuration](https://jmmastey.github.io/slack_message/01_configuration)
|
1
|
+
* [Getting Started / Configuration](https://jmmastey.github.io/slack_message/01_configuration)
|
2
2
|
* [Posting a Message](https://jmmastey.github.io/slack_message/02_posting_a_message)
|
3
3
|
* [The SlackMessage DSL](https://jmmastey.github.io/slack_message/03_message_dsl)
|
4
4
|
* [Editing Messages](https://jmmastey.github.io/slack_message/04_editing_messages)
|
5
5
|
* [Deleting Messages](https://jmmastey.github.io/slack_message/05_deleting_messages)
|
6
6
|
* [Mentions / Notifying Users](https://jmmastey.github.io/slack_message/06_notifying_users)
|
7
7
|
* [Testing](https://jmmastey.github.io/slack_message/07_testing)
|
8
|
+
|
9
|
+
SlackMessage is a gem that makes it easy to send messages to Slack from your
|
10
|
+
application. _Really_ easy.
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
SlackMessage.post_to('#general') do
|
14
|
+
text "We did it @here! :thumbsup:"
|
15
|
+
end
|
16
|
+
```
|
17
|
+
|
18
|
+
And not just simple messages. You can compose complicated messages quickly in a
|
19
|
+
DSL that's focused on usability and maintainability. It can be tough to
|
20
|
+
maintain code in other similar gems, but not here.
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
SlackMessage.post_to('hello@joemastey.com') do
|
24
|
+
section do
|
25
|
+
text "A job has generated some output for you to review."
|
26
|
+
text 'And More' * 10
|
27
|
+
link_button "See Results", "https://google.com"
|
28
|
+
end
|
29
|
+
|
30
|
+
section do
|
31
|
+
text ":unlock-new: New Data Summary"
|
32
|
+
|
33
|
+
list_item "Date", "09/05/2021"
|
34
|
+
list_item "Total Imported", 45_004
|
35
|
+
list_item "Total Errors", 5
|
36
|
+
end
|
37
|
+
|
38
|
+
divider
|
39
|
+
|
40
|
+
section do
|
41
|
+
text "See more here: #{link('result', 'https://google.com')}"
|
42
|
+
end
|
43
|
+
|
44
|
+
context "Kicked off by <hello@joemastey.com> at **9:05am**"
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
It has no dependencies and minimal configuration needs, so you can get up and
|
49
|
+
running quickly.
|
data/lib/slack_message/api.rb
CHANGED
@@ -10,6 +10,10 @@ module SlackMessage::Api
|
|
10
10
|
raise ArgumentError, "Tried to find profile by invalid email address '#{email}'"
|
11
11
|
end
|
12
12
|
|
13
|
+
if SlackMessage::Configuration.debugging?
|
14
|
+
warn [email, profile].inspect
|
15
|
+
end
|
16
|
+
|
13
17
|
response = look_up_user_by_email(email, profile)
|
14
18
|
|
15
19
|
if response.code != "200"
|
@@ -24,14 +28,7 @@ module SlackMessage::Api
|
|
24
28
|
raise SlackMessage::ApiError, "Unable to parse JSON response from Slack API\n#{response.body}"
|
25
29
|
end
|
26
30
|
|
27
|
-
|
28
|
-
raise SlackMessage::ApiError, "Received an error because your authentication token isn't properly configured."
|
29
|
-
elsif payload.include?("error") && payload["error"] == "users_not_found"
|
30
|
-
raise SlackMessage::ApiError, "Couldn't find a user with the email '#{email}'."
|
31
|
-
elsif payload.include?("error")
|
32
|
-
raise SlackMessage::ApiError, "Received error response from Slack during user lookup:\n#{response.body}"
|
33
|
-
end
|
34
|
-
|
31
|
+
SlackMessage::ErrorHandling.raise_user_lookup_errors(response, target, profile)
|
35
32
|
payload["user"]["id"]
|
36
33
|
end
|
37
34
|
|
@@ -69,26 +66,8 @@ module SlackMessage::Api
|
|
69
66
|
end
|
70
67
|
|
71
68
|
response = post_message(profile, params)
|
72
|
-
body = JSON.parse(response.body)
|
73
|
-
error = body.fetch("error", "")
|
74
|
-
|
75
|
-
# TODO: if a scheduled message w/ a short timer is "time_in_past", warn the user?
|
76
|
-
|
77
|
-
# let's try to be helpful about error messages
|
78
|
-
if ["token_revoked", "token_expired", "invalid_auth", "not_authed"].include?(error)
|
79
|
-
raise SlackMessage::ApiError, "Couldn't send slack message because the API key for profile '#{profile[:handle]}' is wrong."
|
80
|
-
elsif ["no_permission", "ekm_access_denied"].include?(error)
|
81
|
-
raise SlackMessage::ApiError, "Couldn't send slack message because the API key for profile '#{profile[:handle]}' isn't allowed to post messages."
|
82
|
-
elsif error == "channel_not_found"
|
83
|
-
raise SlackMessage::ApiError, "Tried to send Slack message to non-existent channel or user '#{target}'"
|
84
|
-
elsif error == "invalid_arguments"
|
85
|
-
raise SlackMessage::ApiError, "Tried to send Slack message with invalid payload."
|
86
|
-
elsif response.code == "302"
|
87
|
-
raise SlackMessage::ApiError, "Got 302 response while posting to Slack. Check your API key for profile '#{profile[:handle]}'."
|
88
|
-
elsif response.code != "200"
|
89
|
-
raise SlackMessage::ApiError, "Got an error back from the Slack API (HTTP #{response.code}):\n#{response.body}"
|
90
|
-
end
|
91
69
|
|
70
|
+
SlackMessage::ErrorHandling.raise_post_response_errors(response, params, profile)
|
92
71
|
SlackMessage::Response.new(response, profile[:handle])
|
93
72
|
end
|
94
73
|
|
@@ -97,7 +76,7 @@ module SlackMessage::Api
|
|
97
76
|
channel: message.channel,
|
98
77
|
ts: message.timestamp,
|
99
78
|
blocks: payload.render,
|
100
|
-
text: payload.custom_notification
|
79
|
+
text: payload.custom_notification
|
101
80
|
}
|
102
81
|
|
103
82
|
if params[:blocks].length == 0
|
@@ -112,8 +91,7 @@ module SlackMessage::Api
|
|
112
91
|
body = JSON.parse(response.body)
|
113
92
|
error = body.fetch("error", "")
|
114
93
|
|
115
|
-
|
116
|
-
|
94
|
+
SlackMessage::ErrorHandling.raise_post_response_errors(response, message, profile)
|
117
95
|
SlackMessage::Response.new(response, profile[:handle])
|
118
96
|
end
|
119
97
|
|
@@ -130,10 +108,13 @@ module SlackMessage::Api
|
|
130
108
|
}
|
131
109
|
end
|
132
110
|
|
133
|
-
|
111
|
+
if SlackMessage::Configuration.debugging?
|
112
|
+
warn params.inspect
|
113
|
+
end
|
134
114
|
|
135
|
-
|
115
|
+
response = delete_message(profile, params)
|
136
116
|
|
117
|
+
SlackMessage::ErrorHandling.raise_delete_response_errors(response, message, profile)
|
137
118
|
response
|
138
119
|
end
|
139
120
|
|
@@ -0,0 +1,124 @@
|
|
1
|
+
class SlackMessage::ErrorHandling
|
2
|
+
PERMISSIONS_ERRORS = ["token_revoked", "token_expired", "invalid_auth", "not_authed",
|
3
|
+
"team_access_not_granted", "no_permission", "missing_scope",
|
4
|
+
"not_allowed_token_type", "ekm_access_denied"]
|
5
|
+
|
6
|
+
def self.raise_post_response_errors(response, params, profile)
|
7
|
+
body = JSON.parse(response.body)
|
8
|
+
error = body.fetch("error", "")
|
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"
|
12
|
+
elsif error == "channel_not_found"
|
13
|
+
raise SlackMessage::ApiError, "Tried to send Slack message to non-existent channel or user '#{params[:channel]}'"
|
14
|
+
|
15
|
+
# scheduling messages
|
16
|
+
elsif error == "invalid_time"
|
17
|
+
raise SlackMessage::ApiError, "Couldn't schedule Slack message because you requested an invalid time '#{params[:post_at]}'"
|
18
|
+
elsif error == "time_in_past"
|
19
|
+
raise SlackMessage::ApiError, "Couldn't schedule Slack message because you requested a time in the past (or too close to now) '#{params[:post_at]}'"
|
20
|
+
elsif error == "time_too_far"
|
21
|
+
raise SlackMessage::ApiError, "Couldn't schedule Slack message because you requested a time more than 120 days in the future '#{params[:post_at]}'"
|
22
|
+
|
23
|
+
|
24
|
+
elsif PERMISSIONS_ERRORS.include?(error)
|
25
|
+
raise SlackMessage::ApiError, "Couldn't send Slack message because the API key for profile '#{profile[:handle]}' is wrong, or the app has insufficient permissions (#{error})"
|
26
|
+
elsif error == "message_too_long"
|
27
|
+
raise SlackMessage::ApiError, "Tried to send Slack message, but the message was too long"
|
28
|
+
elsif error == "invalid_arguments"
|
29
|
+
raise SlackMessage::ApiError, "Tried to send Slack message with invalid payload"
|
30
|
+
elsif ["rate_limited", "ratelimited"].include?(error)
|
31
|
+
raise SlackMessage::ApiError, "Couldn't send Slack message because you've reached your rate limit"
|
32
|
+
elsif response.code == "302"
|
33
|
+
raise SlackMessage::ApiError, "Got 302 response while posting to Slack. Check your API key for profile '#{profile[:handle]}'"
|
34
|
+
elsif response.code != "200"
|
35
|
+
raise SlackMessage::ApiError, "Got an error back from the Slack API (HTTP #{response.code}):\n#{response.body}"
|
36
|
+
elsif !(error.nil? || error == "")
|
37
|
+
raise SlackMessage::ApiError, "Received error response from Slack during message posting:\n#{response.body}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.raise_update_response_errors(response, message, profile)
|
42
|
+
body = JSON.parse(response.body)
|
43
|
+
error = body.fetch("error", "")
|
44
|
+
|
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
|
+
elsif error == "channel_not_found"
|
48
|
+
raise SlackMessage::ApiError, "Tried to update Slack message to non-existent channel or user '#{message.channel}'"
|
49
|
+
|
50
|
+
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}'"
|
52
|
+
elsif error == "cant_update_message"
|
53
|
+
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
|
+
elsif error == "edit_window_closed"
|
55
|
+
raise SlackMessage::ApiError, "Couldn't update message because it's too old"
|
56
|
+
|
57
|
+
|
58
|
+
elsif PERMISSIONS_ERRORS.include?(error)
|
59
|
+
raise SlackMessage::ApiError, "Couldn't update Slack message because the API key for profile '#{profile[:handle]}' is wrong, or the app has insufficient permissions (#{error})"
|
60
|
+
elsif error == "message_too_long"
|
61
|
+
raise SlackMessage::ApiError, "Tried to update Slack message, but the message was too long"
|
62
|
+
elsif error == "invalid_arguments"
|
63
|
+
raise SlackMessage::ApiError, "Tried to update Slack message with invalid payload"
|
64
|
+
elsif ["rate_limited", "ratelimited"].include?(error)
|
65
|
+
raise SlackMessage::ApiError, "Couldn't update Slack message because you've reached your rate limit"
|
66
|
+
elsif response.code == "302"
|
67
|
+
raise SlackMessage::ApiError, "Got 302 response while updating a message. Check your API key for profile '#{profile[:handle]}'"
|
68
|
+
elsif response.code != "200"
|
69
|
+
raise SlackMessage::ApiError, "Got an error back from the Slack API (HTTP #{response.code}):\n#{response.body}"
|
70
|
+
elsif !(error.nil? || error == "")
|
71
|
+
raise SlackMessage::ApiError, "Received error response from Slack during message update:\n#{response.body}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.raise_delete_response_errors(response, message, profile)
|
76
|
+
body = JSON.parse(response.body)
|
77
|
+
error = body.fetch("error", "")
|
78
|
+
|
79
|
+
if error == "channel_not_found"
|
80
|
+
raise SlackMessage::ApiError, "Tried to delete Slack message in non-existent channel '#{message.channel}'"
|
81
|
+
|
82
|
+
elsif error == "invalid_scheduled_message_id"
|
83
|
+
raise SlackMessage::ApiError, "Can't delete message because the ID was invalid, or the message has already posted (#{message.scheduled_message_id})"
|
84
|
+
elsif error == "message_not_found"
|
85
|
+
raise SlackMessage::ApiError, "Tried to delete Slack message, but the message wasn't found (timestamp '#{message.timestamp}' for channel '#{message.channel}')"
|
86
|
+
elsif error == "cant_delete_message"
|
87
|
+
raise SlackMessage::ApiError, "Can't delete message because '#{profile[:handle]}' doesn't have permission to"
|
88
|
+
elsif error == "compliance_exports_prevent_deletion"
|
89
|
+
raise SlackMessage::ApiError, "Can't delete message because team compliance settings prevent it"
|
90
|
+
|
91
|
+
|
92
|
+
elsif PERMISSIONS_ERRORS.include?(error)
|
93
|
+
raise SlackMessage::ApiError, "Couldn't delete Slack message because the API key for profile '#{profile[:handle]}' is wrong, or the app has insufficient permissions (#{error})"
|
94
|
+
elsif ["rate_limited", "ratelimited"].include?(error)
|
95
|
+
raise SlackMessage::ApiError, "Couldn't delete Slack message because you've reached your rate limit"
|
96
|
+
elsif response.code == "302"
|
97
|
+
raise SlackMessage::ApiError, "Got 302 response while deleting a message. Check your API key for profile '#{profile[:handle]}'"
|
98
|
+
elsif response.code != "200"
|
99
|
+
raise SlackMessage::ApiError, "Got an error back from the Slack API (HTTP #{response.code}):\n#{response.body}"
|
100
|
+
elsif !(error.nil? || error == "")
|
101
|
+
raise SlackMessage::ApiError, "Received error response from Slack during message delete:\n#{response.body}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.raise_user_lookup_response_errors(payload)
|
106
|
+
error = payload["error"]
|
107
|
+
|
108
|
+
if error == "users_not_found"
|
109
|
+
raise SlackMessage::ApiError, "Couldn't find a user with the email '#{email}'"
|
110
|
+
|
111
|
+
|
112
|
+
elsif PERMISSIONS_ERRORS.include?(error)
|
113
|
+
raise SlackMessage::ApiError, "Couldn't look up users because the API key for profile '#{profile[:handle]}' is wrong, or the app has insufficient permissions (#{error})"
|
114
|
+
elsif error
|
115
|
+
raise SlackMessage::ApiError, "Received error response from Slack during user lookup:\n#{response.body}"
|
116
|
+
elsif response.code == "302"
|
117
|
+
raise SlackMessage::ApiError, "Got 302 response during user lookup. Check your API key for profile '#{profile[:handle]}'"
|
118
|
+
elsif response.code != "200"
|
119
|
+
raise SlackMessage::ApiError, "Got an error back from the Slack API (HTTP #{response.code}):\n#{response.body}"
|
120
|
+
elsif !(error.nil? || error == "")
|
121
|
+
raise SlackMessage::ApiError, "Received error response from Slack during user lookup:\n#{response.body}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/slack_message/rspec.rb
CHANGED
@@ -14,12 +14,15 @@ require 'rspec/mocks'
|
|
14
14
|
# it can be cleaned up properly.
|
15
15
|
#
|
16
16
|
|
17
|
-
# TODO:
|
17
|
+
# TODO: test helpers for scheduled messages, editing and deleting, and
|
18
|
+
# notification text. And realistically, overhaul all this.
|
18
19
|
|
19
20
|
module SlackMessage::RSpec
|
20
21
|
extend RSpec::Matchers::DSL
|
21
22
|
|
22
23
|
@@listeners = []
|
24
|
+
@@custom_response = {}
|
25
|
+
@@response_code = '200'
|
23
26
|
|
24
27
|
def self.register_expectation_listener(expectation_instance)
|
25
28
|
@@listeners << expectation_instance
|
@@ -29,6 +32,11 @@ module SlackMessage::RSpec
|
|
29
32
|
@@listeners.delete(expectation_instance)
|
30
33
|
end
|
31
34
|
|
35
|
+
def self.reset_custom_responses
|
36
|
+
@@custom_response = {}
|
37
|
+
@@response_code = '200'
|
38
|
+
end
|
39
|
+
|
32
40
|
FauxResponse = Struct.new(:code, :body)
|
33
41
|
|
34
42
|
def self.included(_)
|
@@ -38,22 +46,24 @@ module SlackMessage::RSpec
|
|
38
46
|
listener.record_call(params.merge(profile: profile))
|
39
47
|
end
|
40
48
|
|
41
|
-
response = {
|
42
|
-
"
|
43
|
-
"
|
44
|
-
"
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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)
|
57
67
|
end
|
58
68
|
|
59
69
|
SlackMessage::Api.undef_method(:look_up_user_by_email)
|
@@ -63,6 +73,18 @@ module SlackMessage::RSpec
|
|
63
73
|
end
|
64
74
|
end
|
65
75
|
|
76
|
+
def self.respond_with(response = {}, code: '200')
|
77
|
+
raise ArgumentError, "custom response must be a hash" unless response.is_a? Hash
|
78
|
+
|
79
|
+
@@custom_response = response
|
80
|
+
@@response_code = code
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.reset_mock_response
|
84
|
+
@@custom_response = {}
|
85
|
+
@@response_code = '200'
|
86
|
+
end
|
87
|
+
|
66
88
|
# w/ channel
|
67
89
|
matcher :post_slack_message_to do |expected|
|
68
90
|
match do |actual|
|
data/lib/slack_message.rb
CHANGED
data/slack_message.gemspec
CHANGED
data/spec/slack_message_spec.rb
CHANGED
@@ -111,10 +111,6 @@ RSpec.describe SlackMessage do
|
|
111
111
|
}.to post_slack_message_with_icon_matching(/thisperson/)
|
112
112
|
end
|
113
113
|
|
114
|
-
it "lets you assert notification text" do
|
115
|
-
# TODO :|
|
116
|
-
end
|
117
|
-
|
118
114
|
it "can assert more generally too tbh" do
|
119
115
|
expect {
|
120
116
|
SlackMessage.post_to('#general') { text "foo" }
|
@@ -123,8 +119,6 @@ RSpec.describe SlackMessage do
|
|
123
119
|
end
|
124
120
|
|
125
121
|
describe "API convenience" do
|
126
|
-
let(:profile) { SlackMessage::Configuration.profile(:default) }
|
127
|
-
|
128
122
|
before do
|
129
123
|
SlackMessage.configure do |config|
|
130
124
|
config.clear_profiles!
|
@@ -158,5 +152,57 @@ RSpec.describe SlackMessage do
|
|
158
152
|
end
|
159
153
|
end
|
160
154
|
|
161
|
-
|
155
|
+
describe "error handling" do
|
156
|
+
before do
|
157
|
+
SlackMessage.configure do |config|
|
158
|
+
config.clear_profiles!
|
159
|
+
config.add_profile(name: 'default profile', api_token: 'abc123')
|
160
|
+
config.add_profile(:schmoebot, name: 'Schmoe', api_token: 'abc123', icon: ':schmoebot:', default_channel: '#schmoes')
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
after do
|
165
|
+
SlackMessage::RSpec.reset_mock_response
|
166
|
+
end
|
167
|
+
|
168
|
+
it "raises nice error messages when API methods return errors" do
|
169
|
+
SlackMessage::RSpec.respond_with('error' => 'nuffin')
|
170
|
+
|
171
|
+
expect {
|
172
|
+
SlackMessage.post_to('#general') { text 'nuh uh' }
|
173
|
+
}.to raise_error(SlackMessage::ApiError)
|
174
|
+
|
175
|
+
expect {
|
176
|
+
SlackMessage.post_as(:schmoebot) { text 'nuh uh' }
|
177
|
+
}.to raise_error(SlackMessage::ApiError)
|
178
|
+
end
|
179
|
+
|
180
|
+
it "raises for redirects" do
|
181
|
+
SlackMessage::RSpec.respond_with(code: '302')
|
182
|
+
|
183
|
+
expect {
|
184
|
+
SlackMessage.post_to('#general') { text 'nuh uh' }
|
185
|
+
}.to raise_error(SlackMessage::ApiError)
|
186
|
+
end
|
187
|
+
|
188
|
+
it "raises errors w/ updates too" do
|
189
|
+
message = SlackMessage.post_to('#general') { text 'nuh uh' }
|
190
|
+
|
191
|
+
SlackMessage::RSpec.respond_with('error' => 'bad choice')
|
192
|
+
|
193
|
+
expect {
|
194
|
+
SlackMessage.update(message) { text 'nuh uh' }
|
195
|
+
}.to raise_error(SlackMessage::ApiError)
|
196
|
+
end
|
197
|
+
|
198
|
+
it "even raises errors during deletes" do
|
199
|
+
message = SlackMessage.post_to('#general') { text 'nuh uh' }
|
200
|
+
|
201
|
+
SlackMessage::RSpec.respond_with('error' => 'bad choice')
|
202
|
+
|
203
|
+
expect {
|
204
|
+
SlackMessage.delete(message) { text 'nuh uh' }
|
205
|
+
}.to raise_error(SlackMessage::ApiError)
|
206
|
+
end
|
207
|
+
end
|
162
208
|
end
|
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.
|
4
|
+
version: 3.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe Mastey
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-12-
|
11
|
+
date: 2021-12-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -73,11 +73,13 @@ files:
|
|
73
73
|
- docs/05_deleting_messages.md
|
74
74
|
- docs/06_notifying_users.md
|
75
75
|
- docs/07_testing.md
|
76
|
+
- docs/_config.yml
|
76
77
|
- docs/index.md
|
77
78
|
- lib/slack_message.rb
|
78
79
|
- lib/slack_message/api.rb
|
79
80
|
- lib/slack_message/configuration.rb
|
80
81
|
- lib/slack_message/dsl.rb
|
82
|
+
- lib/slack_message/error_handling.rb
|
81
83
|
- lib/slack_message/response.rb
|
82
84
|
- lib/slack_message/rspec.rb
|
83
85
|
- slack_message.gemspec
|