diff --git a/json-logs/samples/app-backend/interactive-components/BlockActionPayload.json b/json-logs/samples/app-backend/interactive-components/BlockActionPayload.json index 54a9010f8..16e4148ee 100644 --- a/json-logs/samples/app-backend/interactive-components/BlockActionPayload.json +++ b/json-logs/samples/app-backend/interactive-components/BlockActionPayload.json @@ -104,7 +104,40 @@ "text": "", "emoji": false } - } + }, + "response_url_enabled": false + }, + { + "type": "multi_channels_select", + "placeholder": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "action_id": "", + "confirm": { + "title": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "text": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "confirm": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "deny": { + "type": "plain_text", + "text": "", + "emoji": false + } + }, + "max_selected_items": 123 }, { "type": "conversations_select", @@ -136,6 +169,53 @@ "text": "", "emoji": false } + }, + "response_url_enabled": false, + "filter": { + "include": [ + "" + ], + "exclude_external_shared_channels": false, + "exclude_bot_users": false + } + }, + { + "type": "multi_conversations_select", + "placeholder": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "action_id": "", + "confirm": { + "title": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "text": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "confirm": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "deny": { + "type": "plain_text", + "text": "", + "emoji": false + } + }, + "max_selected_items": 123, + "filter": { + "include": [ + "" + ], + "exclude_external_shared_channels": false, + "exclude_bot_users": false } }, { @@ -216,6 +296,39 @@ } } }, + { + "type": "multi_external_select", + "placeholder": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "action_id": "", + "min_query_length": 123, + "confirm": { + "title": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "text": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "confirm": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "deny": { + "type": "plain_text", + "text": "", + "emoji": false + } + }, + "max_selected_items": 123 + }, { "type": "image", "image_url": "", @@ -296,6 +409,38 @@ } } }, + { + "type": "multi_static_select", + "placeholder": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "action_id": "", + "confirm": { + "title": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "text": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "confirm": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "deny": { + "type": "plain_text", + "text": "", + "emoji": false + } + }, + "max_selected_items": 123 + }, { "type": "users_select", "placeholder": { @@ -327,6 +472,38 @@ "emoji": false } } + }, + { + "type": "multi_users_select", + "placeholder": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "action_id": "", + "confirm": { + "title": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "text": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "confirm": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "deny": { + "type": "plain_text", + "text": "", + "emoji": false + } + }, + "max_selected_items": 123 } ], "block_id": "" diff --git a/json-logs/samples/app-backend/interactive-components/MessageShortcutPayload.json b/json-logs/samples/app-backend/interactive-components/MessageShortcutPayload.json index 53f8e8410..5bbc717bc 100644 --- a/json-logs/samples/app-backend/interactive-components/MessageShortcutPayload.json +++ b/json-logs/samples/app-backend/interactive-components/MessageShortcutPayload.json @@ -80,7 +80,40 @@ "text": "", "emoji": false } - } + }, + "response_url_enabled": false + }, + { + "type": "multi_channels_select", + "placeholder": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "action_id": "", + "confirm": { + "title": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "text": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "confirm": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "deny": { + "type": "plain_text", + "text": "", + "emoji": false + } + }, + "max_selected_items": 123 }, { "type": "conversations_select", @@ -112,6 +145,53 @@ "text": "", "emoji": false } + }, + "response_url_enabled": false, + "filter": { + "include": [ + "" + ], + "exclude_external_shared_channels": false, + "exclude_bot_users": false + } + }, + { + "type": "multi_conversations_select", + "placeholder": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "action_id": "", + "confirm": { + "title": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "text": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "confirm": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "deny": { + "type": "plain_text", + "text": "", + "emoji": false + } + }, + "max_selected_items": 123, + "filter": { + "include": [ + "" + ], + "exclude_external_shared_channels": false, + "exclude_bot_users": false } }, { @@ -192,6 +272,39 @@ } } }, + { + "type": "multi_external_select", + "placeholder": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "action_id": "", + "min_query_length": 123, + "confirm": { + "title": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "text": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "confirm": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "deny": { + "type": "plain_text", + "text": "", + "emoji": false + } + }, + "max_selected_items": 123 + }, { "type": "image", "image_url": "", @@ -272,6 +385,38 @@ } } }, + { + "type": "multi_static_select", + "placeholder": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "action_id": "", + "confirm": { + "title": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "text": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "confirm": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "deny": { + "type": "plain_text", + "text": "", + "emoji": false + } + }, + "max_selected_items": 123 + }, { "type": "users_select", "placeholder": { @@ -303,6 +448,38 @@ "emoji": false } } + }, + { + "type": "multi_users_select", + "placeholder": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "action_id": "", + "confirm": { + "title": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "text": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "confirm": { + "type": "plain_text", + "text": "", + "emoji": false + }, + "deny": { + "type": "plain_text", + "text": "", + "emoji": false + } + }, + "max_selected_items": 123 } ], "block_id": "" diff --git a/json-logs/samples/rtm/MessageEvent.json b/json-logs/samples/rtm/MessageEvent.json index 861a63918..54f533084 100644 --- a/json-logs/samples/rtm/MessageEvent.json +++ b/json-logs/samples/rtm/MessageEvent.json @@ -87,7 +87,8 @@ "text": "", "emoji": false } - } + }, + "response_url_enabled": false }, { "type": "conversations_select", @@ -119,6 +120,11 @@ "text": "", "emoji": false } + }, + "response_url_enabled": false, + "filter": { + "exclude_external_shared_channels": false, + "exclude_bot_users": false } }, { diff --git a/json-logs/samples/rtm/PinAddedEvent.json b/json-logs/samples/rtm/PinAddedEvent.json index 782e78f8e..a578a6090 100644 --- a/json-logs/samples/rtm/PinAddedEvent.json +++ b/json-logs/samples/rtm/PinAddedEvent.json @@ -95,7 +95,8 @@ "text": "", "emoji": false } - } + }, + "response_url_enabled": false }, { "type": "conversations_select", @@ -127,6 +128,11 @@ "text": "", "emoji": false } + }, + "response_url_enabled": false, + "filter": { + "exclude_external_shared_channels": false, + "exclude_bot_users": false } }, { diff --git a/json-logs/samples/rtm/PinRemovedEvent.json b/json-logs/samples/rtm/PinRemovedEvent.json index 82e620dcc..1c68d7b11 100644 --- a/json-logs/samples/rtm/PinRemovedEvent.json +++ b/json-logs/samples/rtm/PinRemovedEvent.json @@ -95,7 +95,8 @@ "text": "", "emoji": false } - } + }, + "response_url_enabled": false }, { "type": "conversations_select", @@ -127,6 +128,11 @@ "text": "", "emoji": false } + }, + "response_url_enabled": false, + "filter": { + "exclude_external_shared_channels": false, + "exclude_bot_users": false } }, { diff --git a/json-logs/samples/rtm/StarAddedEvent.json b/json-logs/samples/rtm/StarAddedEvent.json index 92dae8bd1..4c08a7bde 100644 --- a/json-logs/samples/rtm/StarAddedEvent.json +++ b/json-logs/samples/rtm/StarAddedEvent.json @@ -95,7 +95,8 @@ "text": "", "emoji": false } - } + }, + "response_url_enabled": false }, { "type": "conversations_select", @@ -127,6 +128,11 @@ "text": "", "emoji": false } + }, + "response_url_enabled": false, + "filter": { + "exclude_external_shared_channels": false, + "exclude_bot_users": false } }, { diff --git a/json-logs/samples/rtm/StarRemovedEvent.json b/json-logs/samples/rtm/StarRemovedEvent.json index 8912e90ce..f212a7696 100644 --- a/json-logs/samples/rtm/StarRemovedEvent.json +++ b/json-logs/samples/rtm/StarRemovedEvent.json @@ -94,7 +94,8 @@ "text": "", "emoji": false } - } + }, + "response_url_enabled": false }, { "type": "conversations_select", @@ -126,6 +127,11 @@ "text": "", "emoji": false } + }, + "response_url_enabled": false, + "filter": { + "exclude_external_shared_channels": false, + "exclude_bot_users": false } }, { diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/element/ChannelsSelectElement.java b/slack-api-model/src/main/java/com/slack/api/model/block/element/ChannelsSelectElement.java index cbf90d45d..469440323 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/element/ChannelsSelectElement.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/element/ChannelsSelectElement.java @@ -39,4 +39,12 @@ public class ChannelsSelectElement extends BlockElement { * A confirm object that defines an optional confirmation dialog that appears after a menu item is selected. */ private ConfirmationDialogObject confirm; + + /** + * This field only works with menus in input blocks in modals. + * When set to true, the view_submission payload from the menu's parent view will contain a response_url. + * This response_url can be used for message responses. + * The target conversation for the message will be determined by the value of this select menu. + */ + private Boolean responseUrlEnabled; } diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/element/ConversationsFilter.java b/slack-api-model/src/main/java/com/slack/api/model/block/element/ConversationsFilter.java new file mode 100644 index 000000000..4faa2c75e --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/model/block/element/ConversationsFilter.java @@ -0,0 +1,38 @@ +package com.slack.api.model.block.element; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * Filter object for conversation lists + * Provides a way to filter the list of options in a conversations select menu or conversations multi-select menu. + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ConversationsFilter { + + /** + * Indicates which type of conversations should be included in the list. + * When this field is provided, any conversations that do not match will be excluded. + * You should provide an array of strings from the following options: im, mpim, private, and public. + * The array cannot be empty. + */ + private List include; + + /** + * Indicates whether to exclude external shared channels from conversation lists. Defaults to false. + */ + private Boolean excludeExternalSharedChannels; + + /** + * Indicates whether to exclude bot users from conversation lists. Defaults to false. + */ + private Boolean excludeBotUsers; + +} diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/element/ConversationsSelectElement.java b/slack-api-model/src/main/java/com/slack/api/model/block/element/ConversationsSelectElement.java index a537bf875..33a465239 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/element/ConversationsSelectElement.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/element/ConversationsSelectElement.java @@ -40,4 +40,17 @@ public class ConversationsSelectElement extends BlockElement { */ private ConfirmationDialogObject confirm; + /** + * This field only works with menus in input blocks in modals. + * When set to true, the view_submission payload from the menu's parent view will contain a response_url. + * This response_url can be used for message responses. + * The target conversation for the message will be determined by the value of this select menu. + */ + private Boolean responseUrlEnabled; + + /** + * A filter object that reduces the list of available conversations using the specified criteria. + */ + private ConversationsFilter filter; + } diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/element/MultiConversationsSelectElement.java b/slack-api-model/src/main/java/com/slack/api/model/block/element/MultiConversationsSelectElement.java index e95c5a3c2..85741f2ec 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/element/MultiConversationsSelectElement.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/element/MultiConversationsSelectElement.java @@ -49,4 +49,9 @@ public class MultiConversationsSelectElement extends BlockElement { */ private Integer maxSelectedItems; + /** + * A filter object that reduces the list of available conversations using the specified criteria. + */ + private ConversationsFilter filter; + } diff --git a/slack-api-model/src/test/java/test_locally/api/model/block/BlocksTest.java b/slack-api-model/src/test/java/test_locally/api/model/block/BlocksTest.java index 92d0011e7..e0a802c19 100644 --- a/slack-api-model/src/test/java/test_locally/api/model/block/BlocksTest.java +++ b/slack-api-model/src/test/java/test_locally/api/model/block/BlocksTest.java @@ -3,9 +3,13 @@ import com.slack.api.model.Message; import com.slack.api.model.block.*; import com.slack.api.model.block.element.BlockElement; +import com.slack.api.model.block.element.ChannelsSelectElement; +import com.slack.api.model.block.element.ConversationsSelectElement; +import com.slack.api.model.block.element.MultiConversationsSelectElement; import org.junit.Test; import test_locally.unit.GsonFactory; +import java.util.Arrays; import java.util.List; import static com.slack.api.model.block.Blocks.*; @@ -15,7 +19,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.*; public class BlocksTest { @@ -118,4 +122,194 @@ public void testCheckboxes() { assertThat(input(i -> i.element(checkboxes(c -> c.actionId("foo")))), is(notNullValue())); } + @Test + public void testConversationsFilter() { + String json = "{\"blocks\":[\n" + + " {\n" + + " \"type\": \"input\",\n" + + " \"element\": {\n" + + " \"type\": \"conversations_select\",\n" + + " \"placeholder\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"Select a conversation\",\n" + + " \"emoji\": true\n" + + " },\n" + + " \"filter\": {\n" + + " \"include\": [\n" + + " \"public\",\n" + + " \"mpim\"\n" + + " ],\n" + + " \"exclude_bot_users\": true\n" + + " }\n" + + " },\n" + + " \"label\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"Choose the conversation to publish your result to:\",\n" + + " \"emoji\": true\n" + + " }\n" + + " }\n" + + "]}"; + Message message = GsonFactory.createSnakeCase().fromJson(json, Message.class); + assertNotNull(message); + assertEquals(1, message.getBlocks().size()); + InputBlock block = (InputBlock) message.getBlocks().get(0); + ConversationsSelectElement element = (ConversationsSelectElement) block.getElement(); + assertEquals(Arrays.asList("public", "mpim"), element.getFilter().getInclude()); + assertTrue(element.getFilter().getExcludeBotUsers()); + assertNull(element.getFilter().getExcludeExternalSharedChannels()); + } + + @Test + public void testConversationsFilter_multi() { + String json = "{\"blocks\":[\n" + + " {\n" + + " \"type\": \"input\",\n" + + " \"element\": {\n" + + " \"type\": \"multi_conversations_select\",\n" + + " \"placeholder\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"Select a conversation\",\n" + + " \"emoji\": true\n" + + " },\n" + + " \"filter\": {\n" + + " \"include\": [\n" + + " \"public\",\n" + + " \"mpim\"\n" + + " ],\n" + + " \"exclude_bot_users\": true\n" + + " }\n" + + " },\n" + + " \"label\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"Choose the conversation to publish your result to:\",\n" + + " \"emoji\": true\n" + + " }\n" + + " }\n" + + "]}"; + Message message = GsonFactory.createSnakeCase().fromJson(json, Message.class); + assertNotNull(message); + assertEquals(1, message.getBlocks().size()); + InputBlock block = (InputBlock) message.getBlocks().get(0); + MultiConversationsSelectElement element = (MultiConversationsSelectElement) block.getElement(); + assertEquals(Arrays.asList("public", "mpim"), element.getFilter().getInclude()); + assertTrue(element.getFilter().getExcludeBotUsers()); + assertNull(element.getFilter().getExcludeExternalSharedChannels()); + } + + @Test + public void testResponseUrlEnabled_conversations() { + { + String json = "{\"blocks\":[\n" + + " {\n" + + " \"type\": \"input\",\n" + + " \"element\": {\n" + + " \"type\": \"conversations_select\",\n" + + " \"placeholder\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"Select a conversation\",\n" + + " \"emoji\": true\n" + + " },\n" + + " \"response_url_enabled\": true\n" + + " },\n" + + " \"label\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"Choose the conversation to publish your result to:\",\n" + + " \"emoji\": true\n" + + " }\n" + + " }\n" + + "]}"; + Message message = GsonFactory.createSnakeCase().fromJson(json, Message.class); + assertNotNull(message); + assertEquals(1, message.getBlocks().size()); + InputBlock block = (InputBlock) message.getBlocks().get(0); + ConversationsSelectElement element = (ConversationsSelectElement) block.getElement(); + assertTrue(element.getResponseUrlEnabled()); + } + + { + String json = "{\"blocks\":[\n" + + " {\n" + + " \"type\": \"input\",\n" + + " \"element\": {\n" + + " \"type\": \"conversations_select\",\n" + + " \"placeholder\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"Select a conversation\",\n" + + " \"emoji\": true\n" + + " }\n" + + " },\n" + + " \"label\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"Choose the conversation to publish your result to:\",\n" + + " \"emoji\": true\n" + + " }\n" + + " }\n" + + "]}"; + Message message = GsonFactory.createSnakeCase().fromJson(json, Message.class); + assertNotNull(message); + assertEquals(1, message.getBlocks().size()); + InputBlock block = (InputBlock) message.getBlocks().get(0); + ConversationsSelectElement element = (ConversationsSelectElement) block.getElement(); + assertNull(element.getResponseUrlEnabled()); + } + } + + @Test + public void testResponseUrlEnabled_channels() { + { + String json = "{\"blocks\":[\n" + + " {\n" + + " \"type\": \"input\",\n" + + " \"element\": {\n" + + " \"type\": \"channels_select\",\n" + + " \"placeholder\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"Select a conversation\",\n" + + " \"emoji\": true\n" + + " },\n" + + " \"response_url_enabled\": true\n" + + " },\n" + + " \"label\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"Choose the conversation to publish your result to:\",\n" + + " \"emoji\": true\n" + + " }\n" + + " }\n" + + "]}"; + Message message = GsonFactory.createSnakeCase().fromJson(json, Message.class); + assertNotNull(message); + assertEquals(1, message.getBlocks().size()); + InputBlock block = (InputBlock) message.getBlocks().get(0); + ChannelsSelectElement element = (ChannelsSelectElement) block.getElement(); + assertTrue(element.getResponseUrlEnabled()); + } + + { + String json = "{\"blocks\":[\n" + + " {\n" + + " \"type\": \"input\",\n" + + " \"element\": {\n" + + " \"type\": \"channels_select\",\n" + + " \"placeholder\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"Select a conversation\",\n" + + " \"emoji\": true\n" + + " }\n" + + " },\n" + + " \"label\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"Choose the conversation to publish your result to:\",\n" + + " \"emoji\": true\n" + + " }\n" + + " }\n" + + "]}"; + Message message = GsonFactory.createSnakeCase().fromJson(json, Message.class); + assertNotNull(message); + assertEquals(1, message.getBlocks().size()); + InputBlock block = (InputBlock) message.getBlocks().get(0); + ChannelsSelectElement element = (ChannelsSelectElement) block.getElement(); + assertNull(element.getResponseUrlEnabled()); + } + } + } \ No newline at end of file diff --git a/slack-app-backend/src/test/java/util/sample_json_generation/SampleObjects.java b/slack-app-backend/src/test/java/util/sample_json_generation/SampleObjects.java index 6a896b189..beb6f250c 100644 --- a/slack-app-backend/src/test/java/util/sample_json_generation/SampleObjects.java +++ b/slack-app-backend/src/test/java/util/sample_json_generation/SampleObjects.java @@ -60,16 +60,22 @@ private Json() { public static ConfirmationDialogObject Confirm = ConfirmationDialogObject.builder().text(TextObject).build(); + public static ConversationsFilter conversationsFilter = ConversationsFilter.builder().include(Arrays.asList("")).build(); public static List BlockElements = Arrays.asList( initProperties(ButtonElement.builder().confirm(Confirm).build()), initProperties(ChannelsSelectElement.builder().confirm(Confirm).build()), - initProperties(ConversationsSelectElement.builder().confirm(Confirm).build()), + initProperties(MultiChannelsSelectElement.builder().confirm(Confirm).build()), + initProperties(ConversationsSelectElement.builder().confirm(Confirm).filter(conversationsFilter).build()), + initProperties(MultiConversationsSelectElement.builder().confirm(Confirm).filter(conversationsFilter).build()), initProperties(DatePickerElement.builder().confirm(Confirm).build()), initProperties(ExternalSelectElement.builder().confirm(Confirm).build()), + initProperties(MultiExternalSelectElement.builder().confirm(Confirm).build()), initProperties(ImageElement.builder().build()), initProperties(OverflowMenuElement.builder().confirm(Confirm).build()), initProperties(StaticSelectElement.builder().confirm(Confirm).build()), - initProperties(UsersSelectElement.builder().confirm(Confirm).build()) + initProperties(MultiStaticSelectElement.builder().confirm(Confirm).build()), + initProperties(UsersSelectElement.builder().confirm(Confirm).build()), + initProperties(MultiUsersSelectElement.builder().confirm(Confirm).build()) ); public static List ContextBlockElements = Arrays.asList( (ContextBlockElement) initProperties(ImageElement.builder().build())