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

Modal responses for interactive-endpoint #909

Closed
6 of 9 tasks
hckhanh opened this issue Nov 16, 2019 · 24 comments
Closed
6 of 9 tasks

Modal responses for interactive-endpoint #909

hckhanh opened this issue Nov 16, 2019 · 24 comments
Labels
pkg:interactive-messages (deprecated) applies to `@slack/interactive-messages` question M-T: User needs support to use the project

Comments

@hckhanh
Copy link

hckhanh commented Nov 16, 2019

Description

Describe your issue here.

What type of issue is this? (place an x in one of the [ ])

  • bug
  • enhancement (feature request)
  • question
  • documentation related
  • testing related
  • discussion

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

  • I've read and understood the Contributing guidelines and have done my best effort to follow them.
  • I've read and agree to the Code of Conduct.
  • I've searched for any related issues and avoided creating a duplicate issue.

I cannot use Interactive URL to response the form error for view_submission

image

I write the api for interactive url in Lambda, then I set the interactive url and submit the form. I always receive this error on the view modal:

We had some trouble connecting. Try again?

The response for view_submission event that is requested to my server

{
  response_action: "errors",
    errors: {
      "restaurant-name": "You may not select a due date in the past"
    }
}
@hckhanh
Copy link
Author

hckhanh commented Nov 16, 2019

I don't know the error details, and I don't know how I can fix it. It's so hopeless :(

@seratch
Copy link
Member

seratch commented Nov 16, 2019

How about making sure if your Lambda function really received the requests by checking CloudWatch logs?

I'm not sure what your situation is but there are several possible patterns:

  • The requests from Slack servers may be sent to another endpoint (make sure if the request url is correct again)
  • Your endpoint may not be available or reachable from Slack servers
  • Your endpoint doesn't return 200 OK to requests from Slack servers

@seratch seratch added the question M-T: User needs support to use the project label Nov 16, 2019
@hckhanh
Copy link
Author

hckhanh commented Nov 16, 2019

@seratch, I mean when I make an action on Slack, Slack makes a request by using the interactive url I provided in the app's settings. My server receive the request from Slack (through interactive-endpoint) and send back to Slack the response like this:

{
  response_action: "errors",
    errors: {
      "restaurant-name": "You may not select a due date in the past"
    }
}

With status code: 200

But I always receive:

We had some trouble connecting. Try again?

But when I try to send the empty response with 200 OK, it close the modal view with no error

@hckhanh hckhanh changed the title Modal responses for interaction message api Modal responses for interactive-endpoint Nov 16, 2019
@seratch
Copy link
Member

seratch commented Nov 16, 2019

I see. Does your modal have the restaurant-name?

@aoberoi
Copy link
Contributor

aoberoi commented Nov 16, 2019

Just to be clear, your handler is returning the object you've shown above, right? And as @seratch mentioned, "restaurant-name" is the block_id of the block which contains the input element, right?

Let's also try to make sure it isn't timing out. Can you follow the instructions for debugging, and let us know what you see in the logs?

@aoberoi aoberoi added the pkg:interactive-messages (deprecated) applies to `@slack/interactive-messages` label Nov 16, 2019
@hckhanh
Copy link
Author

hckhanh commented Nov 16, 2019

@seratch My modal has a block with id restaurant-name. Is it right?
@aoberoi Here is the log

@slack/interactive-messages:adapter instantiated +0ms
@slack/interactive-messages:http-handler request received - method: POST, path: / +0ms
@slack/interactive-messages:http-handler Parsing raw request +1ms
@slack/interactive-messages:http-handler request signing verification success +3ms
@slack/interactive-messages:adapter dispatching to handler +2s

@hckhanh
Copy link
Author

hckhanh commented Nov 16, 2019

Here is my modal

{
  "type": "modal",
  "callback_id": "create-form",
  "title": {
    "type": "plain_text",
    "text": "Tạo thực đơn",
    "emoji": true
  },
  "submit": {
    "type": "plain_text",
    "text": "Tạo menu",
    "emoji": true
  },
  "close": {
    "type": "plain_text",
    "text": "Hủy",
    "emoji": true
  },
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "Vui lòng *điền đầy đủ* thông tin bên dưới để tạo thực đơn:"
      }
    },
    {
      "type": "input",
      "block_id": "restaurant-name",
      "element": {
        "type": "plain_text_input",
        "placeholder": {
          "type": "plain_text",
          "text": "Quán Cơm 321"
        }
      },
      "label": {
        "type": "plain_text",
        "text": "Tên nhà hàng/quán ăn",
        "emoji": true
      }
    },
    {
      "type": "input",
      "block_id": "restaurant-menu",
      "element": {
        "type": "plain_text_input",
        "multiline": true,
        "placeholder": {
          "type": "plain_text",
          "text": "Tên món ăn 1|Đơn giá\nTên món ăn 2|Đơn giá\nTên món ăn 3|Đơn giá"
        }
      },
      "label": {
        "type": "plain_text",
        "text": "Danh sách món ăn",
        "emoji": true
      }
    }
  ]
}

@hckhanh
Copy link
Author

hckhanh commented Nov 16, 2019

This is the viewSubmission handler. I am using Express

slackInteractions.viewSubmission("create-form", payload => {
 return Promise.resolve({
    response_action: "errors",
    errors: { "restaurant-name": "You may not select a due date in the past" }
  });
});

@hckhanh
Copy link
Author

hckhanh commented Nov 16, 2019

Now It works like a charm when I move from Lambda to native Nodejs express server.

image

@aoberoi
Copy link
Contributor

aoberoi commented Nov 16, 2019

Okay that’s great news. We’ve narrowed the problem down to usage with AWS Lambda. I haven’t used this package on Lambda before but you might find the information here helpful: #896

@dannyshaw
Copy link

I'm having random lambda issues also. This is a very vague untested theory:

I think the issue is thinking about the lambda as a server that can just keep running and processing async callbacks. I believe slack operates by both allowing responses to the initial request, but also posting of new messages to a specific response_url after returning a blank 200 to the initial request.

As Lambda kills the running instance as soon as you return from it. there's no continuing on and then making another call to the response_url as a traditional server would do. I think perhaps we need to trigger a new lambda somehow to do that. I'm not sure it's possible with interactive-messages

Again this is just a hunch.

@lemonbakeoff
Copy link

lemonbakeoff commented Apr 21, 2020

Has anyone ever confirmed this?

I think @dannyshaw is right.

Anyone come up with a workaround?

(FYI: I'm actually working in Python, but the behavior / error / cloud is the same... That's why I think it's Slack and and not a language or SDK per se.)

@an-dev
Copy link

an-dev commented May 3, 2020

+1 on the above, getting the following error

slack.errors.SlackApiError: The request to the Slack API failed.
The server responded with: {'ok': False, 'error': 'invalid_arguments', 'response_metadata': {'messages': ['[ERROR] must provide an object [json-pointer:/view]']}}

with a payload of

{
	"type": "modal",
        "response_action": "errors",
        "errors": {
                "block_test": "lol"
        },
	"title": {
		"type": "plain_text",
		"text": "Notification settings",
		"emoji": true
	},
	"submit": {
		"type": "plain_text",
		"text": "Submit",
		"emoji": true
	},
	"close": {
		"type": "plain_text",
		"text": "Cancel",
		"emoji": true
	},
	"blocks": [
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "*<fakelink.toUrl.com|PR Strategy 2019> posts into <fakelink.toChannel.com|#public-relations>*\n\nSelect which notifications to send:"
			}
		},
                {
			"type": "input",
                        "block_id": "block_test"
			"element": {
				"type": "plain_text_input"
			},
			"label": {
				"type": "plain_text",
				"text": "Label",
				"emoji": true
			}
		}
	]
}

@seratch
Copy link
Member

seratch commented May 4, 2020

@lemonbakeoff
Yes, as @dannyshaw mentioned, when running apps on AWS Lambda (or any other FaaS), developers should be aware of the possibility of termination of app containers once apps return an HTTP response to Slack. slackapi/bolt-js#361 and slackapi/bolt-js#395 are related to this topic and illustrate how we recognize this issue.

The available solutions are:

  1. have a queue to store messages from Slack and run another lambda function that takes and processes those messages asynchronously (the function can use response_url).
  2. use Bolt for JS's processBeforeResponse: true option and await say, respond, etc. (I know you're not using Bolt but Bolt for this use case is much easier)
const app = new App({
  signingSecret: process.env.SLACK_SIGNING_SECRET,
  token: process.env.SLACK_BOT_TOKEN,
  processBeforeResponse: true,
});

app.command("/hellol", async ({ respond, ack }) => {
  await respond("What's up?"); // posts a message using response_url
  await ack(); // returns 200 OK after response_url call completion
});

@an-dev
The JSON structure your app sends is invalid. You can use Block Kit Builder (Modal Preview) as a handy way to validate your payloads this way.

The valid one should be like this.

Regarding the errors response, it is not supposed to be merged into the view. Your app sends only the following JSON data as an HTTP response to a view_submissiontyped request from Slack. Refer to the official document for details: https://api.slack.com/surfaces/modals/using#displaying_errors

{
  "response_action": "errors",
  "errors": {
    "block_test": "lol"
  }
}

@an-dev
Copy link

an-dev commented May 4, 2020

@seratch Thanks for your insight. I assumed the error response was to be merged with the existing modal template as the documentation isn't explicit about it being separate.

@gterras
Copy link

gterras commented May 4, 2020

@seratch regarding your Bolt example :

app.command("/hellol", async ({ respond, ack }) => {
  await respond("What's up?"); // posts a message using response_url
  await ack(); // returns 200 OK after response_url call completion
});

Does it still mean the logic done before ack has to to be done under 3 seconds ?

@stevengill
Copy link
Member

@gterras yes it does. It is a limitation of the current design but most use cases should be able to complete within that timeframe. Are you running into issues with it?

@gterras
Copy link

gterras commented May 4, 2020

I get occasional ack fails on Cloudrun which is expected to be sometimes slow (cold start, instance creation etc). This or any random network error can delay the response. This lead to a very unpredictable and confusing UX unfortunately (can click two times the same button, grey triangle popping etc)

I fear that getting a response consistently under 3 seconds (actually less ?) is not possible for anyone who built an app following traditional Slack guidelines about events response, even if the processing logic is simple. There are too much factors and the risk is too high. The queue system seems the only reasonable things to do then, but what does processBeforeResponse actually do ?

@gterras
Copy link

gterras commented May 5, 2020

FYI after discussion with my cloud team we concluded that by nature a Slack app is incompatible with FaaS core concepts (can't guarantee a cold start + request processing in less than 3s). Moving the app to AppEngine seems much more reasonable (same range of prices, container always up) and allow repositionning the ack at the top of listeners like a standard Slack app.

@stevengill
Copy link
Member

@gterras processBeforeResponse allows your logic to run before ack is called. We found that calling ack, lambda would terminate because a response was sent and it wouldn't get to processing the logic. With processBeforeResponse, this was fixed.

IMO an always up solution is a better for slack apps, but we still want to support folks who want to use lambda (even though it has the limitations which you correctly mentioned).

@seratch
Copy link
Member

seratch commented Oct 16, 2020

I'm sure the team already provided answers (at least responses) to all the questions and comments (the original question about modals and general questions about cold-starts) in this issue.

Let me share some additional thoughts on the processBeforeResponse option. As already mentioned, the option is not a solution for 3 second timeout errors and/or general cold-start issues on FaaS. The option just guarantees all the code in a listener surely completes. For dealing with timeouts (due to long operations and cold-starts), you need to be a bit more creative.

Just for your information, we started offering lazy listener functions in Bolt for Python as an experimental feature. To know what it is, refer to the document and an example code. The feature is yet another way to realize the concept I proposed at slackapi/bolt-js#361. It runs an asynchronous operation in another invocation of the same Lambda function with the same args (body, context, say, respond, and so on). The only difference is the async function is unable to call ack() method. This feature should be greatly helpful for building meaningful Slack apps on FaaS (or on-demand container services like Cloud Run). Developers can use this feature in addition to process_before_response: True option.

We haven't decided to implement the same feature in Bolt for JS yet in the short term. The concept may have some conflict with Bolt for JS's fundamental design concept - a listener is a sequential chain of middleware. So, at this point, our general recommendation is to go with some solution that enables you to store messages in a queue (say, SQS) plus to run another function that subscribes the queue asynchronously.

Thanks for writing in and sharing your thoughts here! If you would like to discuss the FaaS topic further, please feel free to create a discussion issue in bolt-js project like slackapi/bolt-js#361.

@MrYoda
Copy link

MrYoda commented May 5, 2021

Had the same issue with We had some trouble connecting. Try again?. I has sent valid (as I thought) json response with HTTP 200.
I'm using Python 3, flask & flask-restplus. After few hours I discovered that restplus extends response object with message field by default - really bad idea for Slack responses.

@dsuresh-ap
Copy link

Not sure if people are still having this issue but we had it with Azure functions. Turns out we needed to pass the content-type as application/json in the response. Hopefully that helps someone else.

@diegoandresdiazespinoza
Copy link

diegoandresdiazespinoza commented Aug 4, 2023

I had similar issues, the issue in my case was sending a 200 http but with some text. So for example I was sending
Response("OK", status_code=200) and should be Response("", status_code=200), reading carefully docs it state it
here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pkg:interactive-messages (deprecated) applies to `@slack/interactive-messages` question M-T: User needs support to use the project
Projects
None yet
Development

No branches or pull requests