fix: socket mode should warn when ack is invoked multiple times#2519
fix: socket mode should warn when ack is invoked multiple times#2519WilliamBergamin merged 14 commits intomainfrom
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #2519 +/- ##
==========================================
+ Coverage 93.24% 93.28% +0.04%
==========================================
Files 36 37 +1
Lines 7443 7495 +52
Branches 652 656 +4
==========================================
+ Hits 6940 6992 +52
Misses 498 498
Partials 5 5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| stop(...args: any[]): Promise<unknown>; | ||
| } | ||
|
|
||
| export interface ResponseAck { |
There was a problem hiding this comment.
I created this interface so that HTTPResponseAck and SocketModeResponseAck can implement it, but I think its publicly facing because of where it is 🤔 Not sure if there is a better place to put this or maybe we should not do this at all
There was a problem hiding this comment.
Keeping just bind within this interface makes a lot of sense to me since it returns the actual AckFn 🤖
zimeg
left a comment
There was a problem hiding this comment.
@WilliamBergamin This is an impressive PR! 🎉
I'm a fan of these outputs matching implementations of the HTTP ack and the ideas around @slack/socket-mode being a more direct interface to Socket Mode and leaving acknowledgements as an exercise for the calling code 📚
This PR is working great for me in IRL testing, but I'm hoping we can merge the tests of #2520 before and with these changes to make sure nothing strange is happening with the updates around bind?
A few other comments are small suggestions or ideas for future changes, but nothing that blocks these changes! Great stuff all around 🚢 💨
src/types/receiver.ts
Outdated
| bind(): AckFn<any>; | ||
| // The function to acknowledge incoming requests | ||
| // biome-ignore lint/suspicious/noExplicitAny: TODO: Make the argument type more specific | ||
| ack(response?: any): void | Promise<void>; |
There was a problem hiding this comment.
📝 The asynchronous ack implementation - where the actual response is sent - for the Socket Mode receiver makes a lot of sense to me!
I'm wondering if most of the acknowledgement logic should be moved here for the HTTP receiver too?
This might not be causing a problem in calling code, but it's a little confusing to me that the response isn't sent via ack here:
bolt-js/src/receivers/HTTPResponseAck.ts
Lines 65 to 93 in ee8df73
There was a problem hiding this comment.
🤔 I'm now wondering if bind should be a simple wrapper that calls ack where all of the actual logic would be for both of these receivers?
This might help keep logs together if ack is called without bind 🌳
There was a problem hiding this comment.
🗣️ Rambling now, but this might be a change or fix better left for a follow up if it does make sense...
There was a problem hiding this comment.
From my understanding ack in HTTPResponseAck does not actually build the content response because this actually fires off the response to Slack.
In the case where the handler is responsible to call ack but it throws an error then their can be a race condition where ack is called twice
See this comment
But this does make me think that this implementation should be tweaked slightly 🤔
Co-authored-by: Eden Zimbelman <[email protected]>
….com/slackapi/bolt-js into socket-mode-should-warn-on-multi-ack
zimeg
left a comment
There was a problem hiding this comment.
@WilliamBergamin All looks good to me!
I noticed these logs are output as errors which might cause confusion when the log level is set to ERROR but the changes are working well for me overall 👾
| it('does not re-acknowledge events that handle acknowledge and then throw unknown errors', async () => { | ||
| const processStub = sinon.stub<[ReceiverEvent]>().callsFake(async (event: ReceiverEvent) => { | ||
| await event.ack(); | ||
| throw new Error('internal error'); | ||
| }); |
There was a problem hiding this comment.
🤩 Wonderful testcase to have! Thanks for making these improvements and tests read better overall!
| stop(...args: any[]): Promise<unknown>; | ||
| } | ||
|
|
||
| export interface ResponseAck { |
There was a problem hiding this comment.
Keeping just bind within this interface makes a lot of sense to me since it returns the actual AckFn 🤖
Co-authored-by: Eden Zimbelman <[email protected]>
Summary
The
HTTPReceiverandExpressReceiverthrow a ReceiverMultipleAckError if theackutility function is invoked more then once, but theSocketModeReceiverallows for multipleackinvocations. We recommend usingSocketModeReceiverfor development and theHTTPReceiver, a developer could accidentally invokeackmore then once in development and then see an error raised in production.These changes improve the behavior of
SocketModeReceiverto warn the developer wheneverackis invoked more then once. These changes also aim to align theSocketModeReceivercloser with the design patterns found in theHTTPReceiver.NOTE: opted to warn the developer instead of throwing a
ReceiverMultipleAckErrorsince it would has cause a breaking change. We could also inform the developer through a debug log.Testing
You can use the following app to test this behavior
{ "display_information": { "name": "hello-cmd" }, "features": { "app_home": { "home_tab_enabled": false, "messages_tab_enabled": true, "messages_tab_read_only_enabled": false }, "bot_user": { "display_name": "hello-cmd", "always_online": false }, "slash_commands": [ { "command": "/hello", "description": "Say hello" } ] }, "oauth_config": { "scopes": { "bot": [ "commands", "chat:write" ] } }, "settings": { "event_subscriptions": { "bot_events": [] }, "interactivity": { "is_enabled": true }, "org_deploy_enabled": true, "socket_mode_enabled": true, "token_rotation_enabled": false, } }Results
With log level
INFOWith log level
DEBUGRequirements