Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Need help to run lazy listeners in response to view_submission requests on AWS Lambda #521

Closed
ashalobodin opened this issue Nov 11, 2021 · 11 comments · Fixed by #524
Closed
Labels
question Further information is requested

Comments

@ashalobodin
Copy link

ashalobodin commented Nov 11, 2021

I can't receive a response from a lazy function (in AWS lambda).
Although, the same AWS lambda was working a few days ago.

Reproducible in:

from slack_bolt import App
from slack_bolt.adapter.aws_lambda import SlackRequestHandler

app = App(process_before_response=True)


def respond_to_slack_within_3_seconds(ack):
    ack()


def handle_freeform_submission(body, client, view):
    slack_user_id = body['user']['id']
    time.sleep(5)
    client.chat_postMessage(channel=slack_user_id, text='some msg')


app.view('create_doc')(ack=respond_to_slack_within_3_seconds, lazy=[handle_freeform_submission])


def lambda_handler(event, context):
    logger.info(f'Received event: {event}')
    slack_handler = SlackRequestHandler(app=app)
    return slack_handler.handle(event, context)

The slack_bolt version

slack-bolt 1.9.2 # and any above
slack-sdk 3.11.2

Python runtime version

python3.7

Steps to reproduce:

are the same as in Issue #490

Expected result:

The function handle_freeform_submission should be called and send its result to a user.

Actual result:

Modal (code base not presented) opens and could be submitted with data
But the lazy function doesn't seem to be even called.

Here is a piece of lambda logs:
Screenshot 2021-11-11 at 21 03 27

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

@ashalobodin
Copy link
Author

ashalobodin commented Nov 11, 2021

An example from docs is not working too:

def testing_whatever(respond, body):
    time.sleep(5)  # longer than 3 seconds
    respond(f"Completed! (test task: {body or 'empty'})")


app.command("/test")(
    ack=respond_to_slack_within_3_seconds,
    lazy=[testing_whatever]
)

@srajiang
Copy link
Member

Hi @ashalobodin - Thanks for writing in, and for providing your sample code. Your code does appear to correctly follow the documentation (and odd that you report it previously was working, then stopped worked), so I will see whether I can replicate this behavior and get back to you.

@srajiang srajiang added the question Further information is requested label Nov 12, 2021
@srajiang
Copy link
Member

HI @ashalobodin - Unfortunately I'm not able to recreate the behavior you're experiencing.

With the following code deployed to AWS Lambda, I am able to successfully call handle_freeform_submission and see the message posted.

import logging
import time

from slack_bolt import App
from slack_bolt.adapter.aws_lambda import SlackRequestHandler

# process_before_response must be True when running on FaaS
app = App(process_before_response=True)


@app.middleware  # or app.use(log_request)
def log_request(logger, body, next):
    logger.debug(body)
    return next()


command = "/hello-bolt-python-lambda"
view = "create_doc"


def respond_to_slack_within_3_seconds(body, ack):
    if body.get("text") is None:
        ack(f":x: Usage: {command} (description here)")
    else:
        title = body["text"]
        ack(f"Accepted! (task: {title})")


def process_request_with_view(respond, body):
    time.sleep(5)
    title = body["text"]
    respond(f"Completed! (task: {title})")

def handle_freeform_submission(body, client, view):
    slack_user_id = body['user']['id']
    time.sleep(5)
    client.chat_postMessage(channel=slack_user_id, text='some msg')

@app.command(command)
def publish_view(client, body, ack, respond):
    ack()
    client.views_open(trigger_id=body['trigger_id'], view={
            "type": "modal",
            # View identifier
            "callback_id": view,
            "title": {"type": "plain_text", "text": "Updated modal"},
            "submit": {"type": "plain_text", "text": "Submit"},
            "blocks": [
                {
                    "type": "section",
                    "text": {"type": "plain_text", "text": "You updated the modal!"}
                },
                {
                    "type": "image",
                    "image_url": "https://media.giphy.com/media/SVZGEcYt7brkFUyU90/giphy.gif",
                    "alt_text": "Yay! The modal was updated"
                }
            ]
        })

app.view(view)(ack=respond_to_slack_within_3_seconds, lazy=[handle_freeform_submission])

SlackRequestHandler.clear_all_log_handlers()
logging.basicConfig(format="%(asctime)s %(message)s", level=logging.DEBUG)


def handler(event, context):
    slack_handler = SlackRequestHandler(app=app)
    return slack_handler.handle(event, context)

This example also includes some additional logging steps which may be illustrative in figuring out what's happening. Are you able to provide the full logs for the failures you're seeing?

@seratch
Copy link
Member

seratch commented Nov 12, 2021

FWIW, I noticed the example code in this repo was a bit broken (import time was missing). I've just fixed it: 1b5f4a1

If your error is caused by this, sorry about the confusing example.

>>> time.sleep(3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'time' is not defined
>>>
>>> import time
>>> time.sleep(3)
>>>

@ashalobodin
Copy link
Author

Hey there,

Thank you very much for your responses.

I just copied the code @srajiang provided and failed again - Submitting modal results in
"We had some trouble connecting. Try again?"

Screenshot 2021-11-12 at 10 16 00

The only difference is how we get (from AWS SecretsManager) and use secrets:

import boto3                 # boto3==1.18.62

secret_id = os.environ['some_secret_id']

session = boto3.session.Session()
secrets = json.loads(
    session.client('secretsmanager', region_name='us-east-1').get_secret_value(SecretId=secret_id)['SecretString']
)

app = App(
    token=secrets['SLACK_OAUTH_TOKEN'],
    signing_secret=secrets['SLACK_SIGNING_SECRET'],
    process_before_response=True
)

A piece of logging from lambda
Screenshot 2021-11-12 at 10 20 24

Might it be related to improper using of secrets?

@seratch
Copy link
Member

seratch commented Nov 12, 2021

@ashalobodin
The logs you've shared show that the HTT response status code for view_submission requests seem to be 202 Accepted, not 200 OK. The Bolt framework itself never returns the code.

Perhaps, some other parts of your AWS components may handle the requests instead. Can you make sure if the Request URL in the Interactivity & Shortcuts section is a valid URL? It may not the one of the deployed Lambda (+ API Gateway).

@srajiang
Copy link
Member

srajiang commented Nov 12, 2021

@seratch @ashalobodin
I took a look again (with less tired eyes this morning 😫 ) and noticed a few things that escaped me yesterday. tldr I can recreate the error in the client that says "We had some trouble connecting.." and I don't think it's related to the url or improper use of secrets, but some issue with sending 200 OK acknowledgement.

Ask: @ashalobodin Can take a look below and let me know whether you can also recreate the following behavior using my updated code sample? I want to figure out if your issue is the same or different.

I've revised my code to make the logic which handles the view publishing also lazy for clarity (and included that below)

Steps:

  1. I trigger the app.view lazy listener handler through the slash command. We see the modal correctly opens (and the response message is printed to the channel where the the command was executed. All fine there, as expected as @ashalobodin was seeing this work correctly as well. But interestingly, the logs ResponseMetadata show a 202 and not a 200 response code. I would have expected that this be a 200 OK response.

12-11-2021_lambda-publish-view

Screen Shot 2021-11-12 at 12 40 05 PM

  1. Then with that modal still open I trigger the second lazy listener app.view by hitting Submit on the modal. The lambda properly fires again as you will see from the logs below. It wasn't clear to me from your original post @ashalobodin whether you ever saw this behavior when using my original code sample, but I believe there is no problem with the lazy function actually executing.

12-11-2021_lambda-handle-freeform

and I even get the message "some msg" sent to me.

12-11-2021_lambda-message-sends

From looking at these, my guess is there might be a bug related to ack, as we see the client UI complains with the error, and of course, the modal doesn't close even after hitting submit.

Revised Code Sample

import logging
import time

from slack_bolt import App
from slack_bolt.adapter.aws_lambda import SlackRequestHandler

# process_before_response must be True when running on FaaS
app = App(process_before_response=True)


@app.middleware  # or app.use(log_request)
def log_request(logger, body, next):
    logger.debug(body)
    return next()


command = "/hello-bolt-python-lambda"
view = "create_doc"


def respond_to_slack_within_3_seconds(body, ack):
    if body.get("text") is None:
        ack(f":x: Usage: {command} (description here)")
    else:
        title = body["text"]
        ack(f"Accepted! (task: {title})")

def handle_freeform_submission(body, client, view):
    slack_user_id = body['user']['id']
    time.sleep(5)
    client.chat_postMessage(channel=slack_user_id, text='some msg')

def publish_view(client, body, ack, respond):
    ack()
    client.views_open(trigger_id=body['trigger_id'], view={
            "type": "modal",
            # View identifier
            "callback_id": view,
            "title": {"type": "plain_text", "text": "Updated modal"},
            "submit": {"type": "plain_text", "text": "Submit"},
            "blocks": [
                {
                    "type": "section",
                    "text": {"type": "plain_text", "text": "You updated the modal!"}
                },
                {
                    "type": "image",
                    "image_url": "https://media.giphy.com/media/SVZGEcYt7brkFUyU90/giphy.gif",
                    "alt_text": "Yay! The modal was updated"
                }
            ]
        })

app.command(command)(ack=respond_to_slack_within_3_seconds, lazy=[publish_view])
app.view(view)(ack=respond_to_slack_within_3_seconds, lazy=[handle_freeform_submission])

SlackRequestHandler.clear_all_log_handlers()
logging.basicConfig(format="%(asctime)s %(message)s", level=logging.DEBUG)


def handler(event, context):
    slack_handler = SlackRequestHandler(app=app)
    return slack_handler.handle(event, context)

# export SLACK_SIGNING_SECRET=***
# export SLACK_BOT_TOKEN=xoxb-***

# rm -rf vendor && cp -pr ../../src/* vendor/
# pip install python-lambda
# lambda deploy --config-file aws_lambda_config.yaml --requirements requirements.txt

@ashalobodin
Copy link
Author

Hey @srajiang,
I can confirm that I have completely the same issue with the updated code.

Additionally, a new message appears
Screenshot 2021-11-13 at 08 46 28

@seratch
Copy link
Member

seratch commented Nov 13, 2021

Thank you both! It seems that we don't need to worry about 202 HTTP status here (sorry about confusing you!).

@srajiang

From looking at these, my guess is there might be a bug related to ack, as we see the client UI complains with the error, and of course, the modal doesn't close even after hitting submit.

This is your code's issue. The ack() for view submission handling must return either "" (empty string) or {"response_action": ... } data. Thus, you can reproduce your acknowledgement error without AWS deployment. If you change the code this way, it works as you expect.

# the existing `respond_to_slack_within_3_seconds` is compatible with slash commands
# but it's not with view submission 
def ack_view_submission_within_3_seconds(ack):
    ack()

app.view(view)(
    ack=ack_view_submission_within_3_seconds,
    lazy=[handle_freeform_submission]
)

@ashalobodin
Congrats! The lazy listeners worked for you, anyway! Now, you can check the difference between the example code here and your original code to identify the issue.

FWIW, I am guessing that one possible cause of your issue would be unmatching callback_id. If your listener has the annotation @app.view("create_doc"), your modal view should have "callback_id": "create_doc" in it. Your code might be running other listener or might not be running listener at all.

Additionally, a new message appears

This is because the above code does so. If you modify the respond_to_slack_within_3_seconds as below (the way you initially did), the ephemeral message won't be posted.

def respond_to_slack_within_3_seconds(ack):
    ack()

It seems that everything is clear now. Let us know if you have follow-ups. I hope you'll figure out the issue in your code and make your app functioning as you expect!

@ashalobodin
Copy link
Author

Thank you very much for you help @seratch @srajiang .
Everything is clear now and works as expected.

@seratch seratch changed the title No response from a lazy function Need help to run lazy listeners in response to view_submission requests on AWS Lambda Nov 15, 2021
@srajiang srajiang linked a pull request Nov 15, 2021 that will close this issue
8 tasks
@srajiang
Copy link
Member

This is your code's issue. The ack() for view submission handling must return either "" (empty string) or {"response_action": ... } data. Thus, you can reproduce your acknowledgement error without AWS deployment. If you change the code this way, it works as you expect.

Yes, I see. Thank you for catching that - I think there could be some improvements to our docs on ack() nuance depending on what the request is. I've linked a PR to fix that up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants