question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Feature Request] Ability to shortcircuit requests by setting a response in listener middlewares

See original GitHub issue

Problem

I’m currently developing an app and i have certain preconditions for each command. In particular, each user must belong to a team for most commands, so i want to group that logic in a listener middleware

What i’ve tried

So what i would like is the ability to ask for that precondition on a common listener middleware and respond with a generic message if the user doesn’t belong to a team yet.

At first, i tried with a listener middleware similar to this

from slack_bolt import BoltResponse

def check_user_has_team(context, next):
    slack_user_id = context['slack_user_id']
    user = db.get_user(slack_user_id)
    if not user.team:
        # Shortcircuit request and respond with
        # The reason we will not continue
        return BoltResponse(status=200, body=f'User {slack_user_id} has no team')
    else:
        # If the user has a team, handle command logic
        next()


@app.command('/command_that_requires_team', middleware=[check_user_has_team])
def myhandler():
    pass

But i discovered that since the listener middleware doesn’t call next(), it is interpreted as if the command shouldn’t handle the request and it ends with

BoltResponse(status=404, body={"error": "unhandled request"})

due of the logic on slack_bolt/app/app.py#351

resp, next_was_not_called = listener.run_middleware(req=req, resp=resp)
if next_was_not_called:
    # The last listener middleware didn't call next() method.
    # This means the listener is not for this incoming request.
    continue

So i tried moving it to a global middleware and it worked. Because they are currently able to edit the response object. (Source)

resp = middleware.process(req=req, resp=resp, next=middleware_next)
if not middleware_state["next_called"]:
    if resp is None:
        return BoltResponse(
            status=404, body={"error": "no next() calls in middleware"}
        )
    return resp

The problem with this approach is that this check is not global. Some commands can be used without a team, but most of them don’t.

Feature Request

So my feature request would be to add the same possibility for listener middlewares, so we can rely on them to group common logic. I slightly edited bolt’s source code and i managed to get the expected behaviour, but i don’t know the codebase enough to judge if it’s a safe change or if it’s backward compatible (i believe it wouldn’t be) but feel free to correct me

resp, next_was_not_called = listener.run_middleware(req=req, resp=resp)
if next_was_not_called:
+    if resp is not None:
+        # The listener middleware decided that this request
+        # Should end by setting the response object
+        return resp

    # The last listener middleware didn't call next() method.
+    # And didn't set the response object
    # This means the listener is not for this incoming request.
    continue

Plus (+) denotes added lines, there’s no other modified/deleted lines

Alternative #1: Listener middlewares that can set the response

One way to workaround this is by using function decorators and decorate each function to check if a team exists, but i think the functionality between this decorators and middlewares may overlap. Personally, i think the middleware api is a bit more intutive (specially for beginners) and composable, hence the feature request.

But perhaps i missed some edge case on why we can’t do this, feel free to correct me if that’s the case

Alternative #2: Move to a global middleware with a command filter

Add a conditional on the global middleware to ignore the check based on commands. I don’t like this approach because it is high-maintenance and the check is not close to the handler logic, as it would be with the middleware and decorator approach.

Category (place an x in each of the [ ])

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter
  • Others

Thanks in advance for reading the wall of text!

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
seratchcommented, Jan 5, 2021

Thanks for the feedback (as always 😃 ). I agree this needs to be improved. Your diff already looks good to me but I will check if the implementation works for any cases.

0reactions
seratchcommented, Aug 22, 2022

@gfang-work As long as your app responds with 200 OK HTTP response, the error never arises.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Middleware And Listeners — Sanic 20.12.3 documentation
Request middleware receives only the request as an argument and are executed in the order they were added. Response middleware receives both the...
Read more >
Can't get 'Access-Control-Allow-Origin' header from fetch ...
I want the Rails app to act as an API and store data from the extension, but I can't get it to accept...
Read more >
Communicating with backend services using HTTP - Angular
The HTTP client service offers the following major features. The ability to request typed response objects; Streamlined error handling · Testability features ......
Read more >
createListenerMiddleware - Redux Toolkit - JS.ORG
Listener effect callbacks have access to dispatch and getState , similar to thunks. The listener also receives a set of async workflow functions ......
Read more >
Building the next generation react/http PSR-15 adapter
Request passes through middleware and is sent to the main thread again for further processing; Main thread comes up with a response and...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found