Repository: slack-go/slack
Pull request #931 (2021-04-20 04:40:33)

Support response_urls field for view_submission payload
## Background

close https://github.com/slack-go/slack/issues/915

view_submission payload includes response_urls field when specific blocks have a special parameter(response_url_enabled parameter = true). However, the [InteractionCallback](https://github.com/takanabe/slack/blob/6213f1f1773414e89543b746c8091cb3c82d5e15/interactions.go#L33-L68) struct does not have the `ResponseURLs` field to capture the URLs injected in view submission callbacks.

So, this PR enable the InteractionCallback struct to capture response_urls while we decode request payloads.

## Technical Changes

* Add `ResponseURLs` field to the `ViewSubmissionCallback`struct

## References

Here are some useful Slack official documents to understand the specifications of the view submission callback payload.

* https://api.slack.com/surfaces/modals/using#modal_response_url
* https://api.slack.com/reference/interaction-payloads/views#view_submission

One caution in the document is the view submission payload shared as [an example](https://api.slack.com/reference/interaction-payloads/views#view_submission_example). It looks like the `hash` and `response_urls` fields exist in the `view` field. However, they are top level fields and not in the view field. (although view has `hash` field as well`)

```json
# the view submission example payload described in the document.
{
  "type": "view_submission",
  "team": { ... },
  "user": { ... },
  "view": {
    "id": "VNHU13V36",
    "type": "modal",
    "title": { ... },
    "submit": { ... },
    "blocks": [ ... ],
    "private_metadata": "shhh-its-secret",
    "callback_id": "modal-with-inputs",
    "state": {
      "values": {
        "multiline": {
          "mlvalue": {
            "type": "plain_text_input",
            "value": "This is my example inputted value"
          }
        },
        "target_channel": {
          "target_select": {
            "type": "conversations_select",
            "value": "C123B12DE"
          }
        }
      }
    },
    "hash": "156663117.cd33ad1f", // this is top level field
    "response_urls": [ // this is top level field
      {
        "block_id": "target_channel",
        "action_id": "target_select",
        "channel_id": "C123B12DE",
        "response_url": "https:\/\/hooks.slack.com\/app\/ABC12312\/1234567890\/A100B100C100d100"
      }
    ]
  }
}
```


Patch:
diff --git a/views.go b/views.go
index ee197ad..e3ee881 100644
--- a/views.go
+++ b/views.go
@@ -38,8 +38,16 @@ type View struct {
 	BotID           string           `json:"bot_id"`
 }
 
+type ViewSubmissionCallbackResponseURL struct {
+	BlockID     string `json:"block_id"`
+	ActionID    string `json:"action_id"`
+	ChannelID   string `json:"channel_id"`
+	ResponseURL string `json:"response_url"`
+}
+
 type ViewSubmissionCallback struct {
-	Hash string `json:"hash"`
+	Hash         string                              `json:"hash"`
+	ResponseURLs []ViewSubmissionCallbackResponseURL `json:"response_urls,omitempty"`
 }
 
 type ViewClosedCallback struct {


Test patch:
diff --git a/interactions_test.go b/interactions_test.go
index 8bd1f99..acac8f6 100644
--- a/interactions_test.go
+++ b/interactions_test.go
@@ -93,19 +93,35 @@ const (
 				"type": "plain_text",
 				"text": "meal choice"
 			},
-			"blocks": [{
-				"type": "input",
-				"block_id": "multi-line",
-				"label": {
-					"type": "plain_text",
-					"text": "dietary restrictions"
+			"blocks": [
+				{
+					"type": "input",
+					"block_id": "multi-line",
+					"label": {
+						"type": "plain_text",
+						"text": "dietary restrictions"
+					},
+					"element": {
+						"type": "plain_text_input",
+						"multiline": true,
+						"action_id": "ml-value"
+					}
 				},
-				"element": {
-					"type": "plain_text_input",
-					"multiline": true,
-					"action_id": "ml-value"
+				{
+					"type": "input",
+					"block_id": "target_channel",
+					"label": {
+						"type": "plain_text",
+						"text": "Select a channel to post the result on"
+					},
+					"element": {
+						"type": "conversations_select",
+						"action_id": "target_select",
+						"default_to_current_conversation": true,
+						"response_url_enabled": true
+					}
 				}
-			}],
+			],
 			"state": {
 				"values": {
 					"multi-line": {
@@ -113,10 +129,25 @@ const (
 							"type": "plain_text_input",
 							"value": "No onions"
 						}
+					},
+					"target_channel": {
+						"target_select": {
+							"type": "conversations_select",
+							"value": "C1AB2C3DE"
+						}
 					}
 				}
 			}
-		}
+		},
+		"hash": "156663117.cd33ad1f",
+		"response_urls": [
+			{
+				"block_id": "target_channel",
+				"action_id": "target_select",
+				"channel_id": "C1AB2C3DE",
+				"response_url": "https:\/\/hooks.slack.com\/app\/ABC12312\/1234567890\/A100B100C100d100"
+			}
+		]
 	}`
 )
 
@@ -231,6 +262,21 @@ func TestViewSubmissionCallback(t *testing.T) {
 							Multiline: true,
 						},
 					),
+					NewInputBlock(
+						"target_channel",
+						NewTextBlockObject(
+							"plain_text",
+							"Select a channel to post the result on",
+							false,
+							false,
+						),
+						&SelectBlockElement{
+							Type:                         "conversations_select",
+							ActionID:                     "target_select",
+							DefaultToCurrentConversation: true,
+							ResponseURLEnabled:           true,
+						},
+					),
 				},
 			},
 			State: &ViewState{
@@ -241,6 +287,23 @@ func TestViewSubmissionCallback(t *testing.T) {
 							Value: "No onions",
 						},
 					},
+					"target_channel": map[string]BlockAction{
+						"target_select": BlockAction{
+							Type:  "conversations_select",
+							Value: "C1AB2C3DE",
+						},
+					},
+				},
+			},
+		},
+		ViewSubmissionCallback: ViewSubmissionCallback{
+			Hash: "156663117.cd33ad1f",
+			ResponseURLs: []ViewSubmissionCallbackResponseURL{
+				{
+					BlockID:     "target_channel",
+					ActionID:    "target_select",
+					ChannelID:   "C1AB2C3DE",
+					ResponseURL: "https://hooks.slack.com/app/ABC12312/1234567890/A100B100C100d100",
 				},
 			},
 		},