diff --git a/slack-api-client/src/main/java/com/slack/api/util/json/GsonFactory.java b/slack-api-client/src/main/java/com/slack/api/util/json/GsonFactory.java index f501907e9..4468f2df4 100644 --- a/slack-api-client/src/main/java/com/slack/api/util/json/GsonFactory.java +++ b/slack-api-client/src/main/java/com/slack/api/util/json/GsonFactory.java @@ -9,6 +9,7 @@ import com.slack.api.model.File; import com.slack.api.model.admin.AppWorkflow; import com.slack.api.model.block.ContextBlockElement; +import com.slack.api.model.block.ContextActionsBlockElement; import com.slack.api.model.block.LayoutBlock; import com.slack.api.model.block.composition.TextObject; import com.slack.api.model.block.element.BlockElement; @@ -75,6 +76,7 @@ public static void registerTypeAdapters(GsonBuilder builder, boolean failOnUnkno .registerTypeAdapter(LayoutBlock.class, new GsonLayoutBlockFactory(failOnUnknownProps)) .registerTypeAdapter(TextObject.class, new GsonTextObjectFactory(failOnUnknownProps)) .registerTypeAdapter(ContextBlockElement.class, new GsonContextBlockElementFactory(failOnUnknownProps)) + .registerTypeAdapter(ContextActionsBlockElement.class, new GsonContextActionsBlockElementFactory(failOnUnknownProps)) .registerTypeAdapter(BlockElement.class, new GsonBlockElementFactory(failOnUnknownProps)) .registerTypeAdapter(RichTextElement.class, new GsonRichTextElementFactory(failOnUnknownProps)) .registerTypeAdapter(FunctionExecutedEvent.InputValue.class, new GsonFunctionExecutedEventInputValueFactory()) diff --git a/slack-api-client/src/test/java/test_with_remote_apis/methods/chat_Test.java b/slack-api-client/src/test/java/test_with_remote_apis/methods/chat_Test.java index ef4359c83..4dd6a4c85 100644 --- a/slack-api-client/src/test/java/test_with_remote_apis/methods/chat_Test.java +++ b/slack-api-client/src/test/java/test_with_remote_apis/methods/chat_Test.java @@ -38,6 +38,8 @@ import static com.slack.api.model.Attachments.asAttachments; import static com.slack.api.model.Attachments.attachment; import static com.slack.api.model.block.Blocks.*; +import static com.slack.api.model.block.composition.BlockCompositions.confirmationDialog; +import static com.slack.api.model.block.composition.BlockCompositions.feedbackButton; import static com.slack.api.model.block.composition.BlockCompositions.markdownText; import static com.slack.api.model.block.composition.BlockCompositions.plainText; import static com.slack.api.model.block.element.BlockElements.*; @@ -1072,7 +1074,42 @@ public void streamMessages() throws IOException, SlackApiException { assertThat(appends.getError(), is(nullValue())); ChatStopStreamResponse stops = slack.methods(botToken).chatStopStream(r -> r .channel(randomChannelId) - .ts(streamer.getTs())); + .ts(streamer.getTs()) + .blocks( + asBlocks( + contextActions(a -> a. + elements( + asContextActionsElements( + feedbackButtons(b -> b + .positiveButton( + feedbackButton(c -> c + .text(plainText(":+1:")) + .value("+1") + ) + ) + .negativeButton( + feedbackButton(c -> c + .text(plainText(":-1:")) + .value("-1") + ) + ) + ), + iconButton(b -> b + .icon("trash") + .text(plainText("Remove")) + .confirm( + confirmationDialog(c -> c + .title(plainText("Oops")) + .text(plainText("This response might've been just alright...")) + .style("danger") + ) + ) + ) + ) + ) + ) + ) + )); assertThat(stops.isOk(), is(true)); assertThat(stops.getError(), is(nullValue())); } diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/Blocks.java b/slack-api-model/src/main/java/com/slack/api/model/block/Blocks.java index 1b890629a..fd451d8d2 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/Blocks.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/Blocks.java @@ -43,6 +43,20 @@ public static ContextBlock context(String blockId, List ele return ContextBlock.builder().blockId(blockId).elements(elements).build(); } + // ContextActionsBlock + + public static ContextActionsBlock contextActions(ModelConfigurator configurator) { + return configurator.configure(ContextActionsBlock.builder()).build(); + } + + public static ContextActionsBlock contextActions(List elements) { + return ContextActionsBlock.builder().elements(elements).build(); + } + + public static ContextActionsBlock contextActions(String blockId, List elements) { + return ContextActionsBlock.builder().blockId(blockId).elements(elements).build(); + } + // DividerBlock public static DividerBlock divider(ModelConfigurator configurator) { diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/ContextActionsBlock.java b/slack-api-model/src/main/java/com/slack/api/model/block/ContextActionsBlock.java new file mode 100644 index 000000000..4a85c2e1a --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/model/block/ContextActionsBlock.java @@ -0,0 +1,25 @@ +package com.slack.api.model.block; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Displays actions as contextual info, which can include both feedback buttons and icon buttons. + * https://docs.slack.dev/reference/block-kit/blocks/context-actions-block + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ContextActionsBlock implements LayoutBlock { + public static final String TYPE = "context_actions"; + private final String type = TYPE; + @Builder.Default + private List elements = new ArrayList<>(); + private String blockId; +} diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/ContextActionsBlockElement.java b/slack-api-model/src/main/java/com/slack/api/model/block/ContextActionsBlockElement.java new file mode 100644 index 000000000..b1459f94e --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/model/block/ContextActionsBlockElement.java @@ -0,0 +1,16 @@ +package com.slack.api.model.block; + +import com.slack.api.model.block.element.FeedbackButtonsElement; +import com.slack.api.model.block.element.IconButtonElement; + +/** + * Specific interface to make context actions layout blocks' {@link ContextActionsBlock} elements type-safe, + * because ContextActionsBlock can only contain {@link FeedbackButtonsElement} and {@link IconButtonElement} elements. + *

+ * Slack Block Kit Reference: Context Actions Block's elements + */ +public interface ContextActionsBlockElement { + + String getType(); + +} diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/UnknownContextActionsBlockElement.java b/slack-api-model/src/main/java/com/slack/api/model/block/UnknownContextActionsBlockElement.java new file mode 100644 index 000000000..44cfd3581 --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/model/block/UnknownContextActionsBlockElement.java @@ -0,0 +1,14 @@ +package com.slack.api.model.block; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UnknownContextActionsBlockElement implements ContextActionsBlockElement { + private String type; +} diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/composition/BlockCompositions.java b/slack-api-model/src/main/java/com/slack/api/model/block/composition/BlockCompositions.java index a6345a551..c8cb8c218 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/composition/BlockCompositions.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/composition/BlockCompositions.java @@ -76,11 +76,15 @@ public static DispatchActionConfig dispatchActionConfig(ModelConfigurator configurator) { + return configurator.configure(FeedbackButtonObject.builder()).build(); + } + // SlackFileObject public static SlackFileObject slackFile(ModelConfigurator configurator) { return configurator.configure(SlackFileObject.builder()).build(); } - } - diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/composition/ConfirmationDialogObject.java b/slack-api-model/src/main/java/com/slack/api/model/block/composition/ConfirmationDialogObject.java index ac3b33724..9be9a8fa2 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/composition/ConfirmationDialogObject.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/composition/ConfirmationDialogObject.java @@ -6,6 +6,9 @@ import lombok.NoArgsConstructor; /** + * Defines a dialog that adds a confirmation step to interactive elements. + * + * https://docs.slack.dev/reference/block-kit/composition-objects/confirmation-dialog-object/ * https://docs.slack.dev/messaging/migrating-outmoded-message-compositions-to-blocks */ @Data @@ -13,9 +16,24 @@ @NoArgsConstructor @AllArgsConstructor public class ConfirmationDialogObject { + /** + * A plain_text text object that defines the dialog's title. Maximum length for this field is 100 characters. + */ private PlainTextObject title; + /** + * A plain_text text object that defines the explanatory text that appears in the confirm dialog. Maximum length for the text in this field is 300 characters. + */ private TextObject text; + /** + * A plain_text text object to define the text of the button that confirms the action. Maximum length for the text in this field is 30 characters. + */ private PlainTextObject confirm; + /** + * A plain_text text object to define the text of the button that cancels the action. Maximum length for the text in this field is 30 characters. + */ private PlainTextObject deny; + /** + * Defines the color scheme applied to the confirm button. A value of danger will display the button with a red background on desktop, or red text on mobile. A value of primary will display the button with a green background on desktop, or blue text on mobile. If this field is not provided, the default value will be primary. + */ private String style; } diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/composition/FeedbackButtonObject.java b/slack-api-model/src/main/java/com/slack/api/model/block/composition/FeedbackButtonObject.java new file mode 100644 index 000000000..883910f98 --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/model/block/composition/FeedbackButtonObject.java @@ -0,0 +1,30 @@ +package com.slack.api.model.block.composition; + +import com.slack.api.model.block.composition.PlainTextObject; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Defines an object containing a feedback button to be used within the {@link FeedbackButtonsElement} block. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FeedbackButtonObject { + /** + * A text object that defines the button's text. Can only be of type: plain_text. Maximum length for the text in this field is 75 characters. + */ + private PlainTextObject text; + /** + * The value to send along with the interaction payload. Maximum length is 2000 characters. + */ + private String value; + /** + * A label for longer descriptive text about a button element. This label will be read out by screen readers instead of the button text object. Maximum length is 75 characters. + */ + private String accessibilityLabel; +} diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/element/BlockElements.java b/slack-api-model/src/main/java/com/slack/api/model/block/element/BlockElements.java index 8be0e34f0..a4f5450ab 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/element/BlockElements.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/element/BlockElements.java @@ -2,6 +2,7 @@ import com.slack.api.model.ModelConfigurator; import com.slack.api.model.block.ContextBlockElement; +import com.slack.api.model.block.ContextActionsBlockElement; import java.util.Arrays; import java.util.List; @@ -19,6 +20,10 @@ public static List asContextElements(ContextBlockElement... return Arrays.asList(elements); } + public static List asContextActionsElements(ContextActionsBlockElement... elements) { + return Arrays.asList(elements); + } + public static List asRichTextElements(RichTextElement... elements) { return Arrays.asList(elements); } @@ -93,6 +98,18 @@ public static DatetimePickerElement datetimePicker(ModelConfigurator configurator) { + return configurator.configure(FeedbackButtonsElement.builder()).build(); + } + + // IconButtonElement + + public static IconButtonElement iconButton(ModelConfigurator configurator) { + return configurator.configure(IconButtonElement.builder()).build(); + } + // ImageElement public static ImageElement image(ModelConfigurator configurator) { diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/element/FeedbackButtonsElement.java b/slack-api-model/src/main/java/com/slack/api/model/block/element/FeedbackButtonsElement.java new file mode 100644 index 000000000..04f8dcf96 --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/model/block/element/FeedbackButtonsElement.java @@ -0,0 +1,38 @@ +package com.slack.api.model.block.element; + +import com.slack.api.model.block.ContextActionsBlockElement; +import com.slack.api.model.block.composition.FeedbackButtonObject; + +import lombok.*; + +/** + * Buttons to indicate positive or negative feedback. + * https://docs.slack.dev/reference/block-kit/block-elements/feedback-buttons-element + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class FeedbackButtonsElement extends BlockElement implements ContextActionsBlockElement { + public static final String TYPE = "feedback_buttons"; + private final String type = TYPE; + + /** + * An identifier for the action triggered when a menu option is selected. + * You can use this when you receive an interaction payload to identify the source of the action. + * Should be unique among all other action_ids used elsewhere by your app. + * Maximum length for this field is 255 characters. + */ + private String actionId; + + /** + * A button to indicate positive feedback. + */ + private FeedbackButtonObject positiveButton; + + /** + * A button to indicate negative feedback. + */ + private FeedbackButtonObject negativeButton; +} diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/element/IconButtonElement.java b/slack-api-model/src/main/java/com/slack/api/model/block/element/IconButtonElement.java new file mode 100644 index 000000000..407d81222 --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/model/block/element/IconButtonElement.java @@ -0,0 +1,62 @@ +package com.slack.api.model.block.element; + +import com.slack.api.model.Confirmation; +import com.slack.api.model.block.ContextActionsBlockElement; +import com.slack.api.model.block.composition.ConfirmationDialogObject; +import com.slack.api.model.block.composition.PlainTextObject; + +import lombok.*; + +import java.util.List; + +/** + * An icon button to perform actions. + * https://docs.slack.dev/reference/block-kit/block-elements/icon-button-element + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class IconButtonElement extends BlockElement implements ContextActionsBlockElement { + public static final String TYPE = "icon_button"; + private final String type = TYPE; + + /** + * An identifier for the action triggered when a menu option is selected. + * You can use this when you receive an interaction payload to identify the source of the action. + * Should be unique among all other action_ids used elsewhere by your app. + * Maximum length for this field is 255 characters. + */ + private String actionId; + + /** + * The icon to show (e.g., "trash"). + */ + private String icon; + + /* + * A text object that defines the button's text. Can only be of type: plain_text. + */ + private PlainTextObject text; + + /** + * The value to send along with the interaction payload. Maximum length is 2000 characters. + */ + private String value; + + /** + * A confirm object that defines an optional confirmation dialog after the button is clicked. + */ + private ConfirmationDialogObject confirm; + + /** + * A label for longer descriptive text about a button element. This label will be read out by screen readers instead of the button text object. Maximum length is 75 characters. + */ + private String accessibilityLabel; + + /** + * An array of user IDs for which the icon button appears. If not provided, the button is visible to all users. + */ + private List visibleToUserIds; +} diff --git a/slack-api-model/src/main/java/com/slack/api/util/json/GsonBlockElementFactory.java b/slack-api-model/src/main/java/com/slack/api/util/json/GsonBlockElementFactory.java index 02db27275..145d52311 100644 --- a/slack-api-model/src/main/java/com/slack/api/util/json/GsonBlockElementFactory.java +++ b/slack-api-model/src/main/java/com/slack/api/util/json/GsonBlockElementFactory.java @@ -44,6 +44,10 @@ private Class getContextBlockElementClassInstance(String switch (typeName) { case ButtonElement.TYPE: return ButtonElement.class; + case FeedbackButtonsElement.TYPE: + return FeedbackButtonsElement.class; + case IconButtonElement.TYPE: + return IconButtonElement.class; case ImageElement.TYPE: return ImageElement.class; case ChannelsSelectElement.TYPE: diff --git a/slack-api-model/src/main/java/com/slack/api/util/json/GsonContextActionsBlockElementFactory.java b/slack-api-model/src/main/java/com/slack/api/util/json/GsonContextActionsBlockElementFactory.java new file mode 100644 index 000000000..e9af80d40 --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/util/json/GsonContextActionsBlockElementFactory.java @@ -0,0 +1,58 @@ +package com.slack.api.util.json; + +import com.google.gson.*; +import com.slack.api.model.block.ContextActionsBlockElement; +import com.slack.api.model.block.UnknownContextActionsBlockElement; +import com.slack.api.model.block.element.FeedbackButtonsElement; +import com.slack.api.model.block.element.IconButtonElement; + +import java.lang.reflect.Type; + +/** + * Factory for deserializing BlockKit 'context actions block' elements from a + * {@link com.slack.api.model.Message chat message response}. + * + * @see Context Actions Block + */ +public class GsonContextActionsBlockElementFactory implements JsonDeserializer, JsonSerializer { + + private boolean failOnUnknownProperties; + + public GsonContextActionsBlockElementFactory() { + this(false); + } + + public GsonContextActionsBlockElementFactory(boolean failOnUnknownProperties) { + this.failOnUnknownProperties = failOnUnknownProperties; + } + + @Override + public ContextActionsBlockElement deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + final JsonObject jsonObject = json.getAsJsonObject(); + final JsonPrimitive prim = (JsonPrimitive) jsonObject.get("type"); + final String typeName = prim.getAsString(); + final Class clazz = getContextActionsBlockElementClassInstance(typeName); + return context.deserialize(jsonObject, clazz); + } + + @Override + public JsonElement serialize(ContextActionsBlockElement src, Type typeOfSrc, JsonSerializationContext context) { + return context.serialize(src); + } + + private Class getContextActionsBlockElementClassInstance(String typeName) { + switch (typeName) { + case FeedbackButtonsElement.TYPE: + return FeedbackButtonsElement.class; + case IconButtonElement.TYPE: + return IconButtonElement.class; + default: + if (failOnUnknownProperties) { + throw new JsonParseException("Unknown context actions block element type: " + typeName); + } else { + return UnknownContextActionsBlockElement.class; + } + } + } +} diff --git a/slack-api-model/src/main/java/com/slack/api/util/json/GsonLayoutBlockFactory.java b/slack-api-model/src/main/java/com/slack/api/util/json/GsonLayoutBlockFactory.java index 9460e7223..9286e6b51 100644 --- a/slack-api-model/src/main/java/com/slack/api/util/json/GsonLayoutBlockFactory.java +++ b/slack-api-model/src/main/java/com/slack/api/util/json/GsonLayoutBlockFactory.java @@ -41,6 +41,8 @@ private Class getLayoutClassInstance(String typeName) { return ImageBlock.class; case ContextBlock.TYPE: return ContextBlock.class; + case ContextActionsBlock.TYPE: + return ContextActionsBlock.class; case CallBlock.TYPE: return CallBlock.class; case ActionsBlock.TYPE: diff --git a/slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java b/slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java index cbe50c595..1fc12d58d 100644 --- a/slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java +++ b/slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java @@ -1245,6 +1245,74 @@ public void parseCallBlock() { assertThat(block, is(notNullValue())); } + @Test + public void parseContextActionsBlock() { + String json = "{\n" + + " \"type\": \"context_actions\",\n" + + " \"elements\": [\n" + + " {\n" + + " \"type\": \"feedback_buttons\",\n" + + " \"action_id\": \"feedback\",\n" + + " \"positive_button\": {\n" + + " \"text\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"Good Response\",\n" + + " \"emoji\": true\n" + + " },\n" + + " \"accessibility_label\": \"Submit positive feedback on this response\",\n" + + " \"value\": \"good-feedback\"\n" + + " },\n" + + " \"negative_button\": {\n" + + " \"text\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"Bad Response\",\n" + + " \"emoji\": true\n" + + " },\n" + + " \"accessibility_label\": \"Submit negative feedback on this response\",\n" + + " \"value\": \"bad-feedback\"\n" + + " }\n" + + " },\n" + + " {\n" + + " \"type\": \"icon_button\",\n" + + " \"icon\": \"trash\",\n" + + " \"text\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"Remove\"\n" + + " },\n" + + " \"confirm\": {\n" + + " \"title\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"Oops\"\n" + + " },\n" + + " \"text\": {\n" + + " \"type\": \"plain_text\",\n" + + " \"text\": \"This response might've been just alright...\"\n" + + " },\n" + + " \"style\": \"danger\"\n" + + " },\n" + + " \"visible_to_user_ids\": [\"USLACKBOT\", \"U0123456789\"]\n" + + " }\n" + + " ]\n" + + "}"; + ContextActionsBlock block = GsonFactory.createSnakeCase().fromJson(json, ContextActionsBlock.class); + assertNotNull(block); + assertEquals("context_actions", block.getType()); + assertEquals(2, block.getElements().size()); + FeedbackButtonsElement buttons = (FeedbackButtonsElement) block.getElements().get(0); + assertEquals("feedback", buttons.getActionId()); + assertEquals("good-feedback", buttons.getPositiveButton().getValue()); + assertEquals("bad-feedback", buttons.getNegativeButton().getValue()); + assertEquals("Good Response", buttons.getPositiveButton().getText().getText()); + assertEquals("Bad Response", buttons.getNegativeButton().getText().getText()); + IconButtonElement iconButton = (IconButtonElement) block.getElements().get(1); + assertEquals("trash", iconButton.getIcon()); + assertEquals("Remove", iconButton.getText().getText()); + assertEquals("Oops", iconButton.getConfirm().getTitle().getText()); + assertEquals("This response might've been just alright...", iconButton.getConfirm().getText().getText()); + assertEquals("danger", iconButton.getConfirm().getStyle()); + assertEquals(Arrays.asList("USLACKBOT", "U0123456789"), iconButton.getVisibleToUserIds()); + } + String richTextSkinTone = "{ \"blocks\": [\n" + " {\n" + " \"type\": \"rich_text\",\n" + 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 79d4499b7..12073ab6d 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 @@ -13,6 +13,8 @@ import java.util.List; import static com.slack.api.model.block.Blocks.*; +import static com.slack.api.model.block.composition.BlockCompositions.confirmationDialog; +import static com.slack.api.model.block.composition.BlockCompositions.feedbackButton; import static com.slack.api.model.block.composition.BlockCompositions.plainText; import static com.slack.api.model.block.element.BlockElements.*; import static org.hamcrest.CoreMatchers.is; @@ -63,6 +65,41 @@ public void testCall() { assertThat(call(f -> f.blockId("block-id").callId("R111")), is(notNullValue())); } + @Test + public void testContextActions() { + assertThat(contextActions(b -> b + .elements(asContextActionsElements( + feedbackButtons(f -> f + .actionId("feedback") + .positiveButton( + feedbackButton(p -> p + .text(plainText("Good Response")) + .value("good-feedback") + ) + ) + .negativeButton( + feedbackButton(n -> n + .text(plainText("Bad Response")) + .value("bad-feedback") + ) + ) + ), + iconButton(i -> i + .icon("trash") + .text(plainText("Remove")) + .confirm( + confirmationDialog(c -> c + .title(plainText("Oops")) + .text(plainText("This response might've been just alright...")) + .style("danger") + ) + ) + .visibleToUserIds(Arrays.asList("USLACKBOT", "U0123456789")) + ) + )) + ), is(notNullValue())); + } + @Test public void testImage() { assertThat(Blocks.image(i -> i.blockId("block-id").imageUrl("https://www.example.com/")), is(notNullValue())); diff --git a/slack-api-model/src/test/java/test_locally/unit/GsonFactory.java b/slack-api-model/src/test/java/test_locally/unit/GsonFactory.java index 695ee2498..409056175 100644 --- a/slack-api-model/src/test/java/test_locally/unit/GsonFactory.java +++ b/slack-api-model/src/test/java/test_locally/unit/GsonFactory.java @@ -6,6 +6,7 @@ import com.slack.api.model.Attachment; import com.slack.api.model.File; import com.slack.api.model.block.ContextBlockElement; +import com.slack.api.model.block.ContextActionsBlockElement; import com.slack.api.model.block.LayoutBlock; import com.slack.api.model.block.composition.TextObject; import com.slack.api.model.block.element.BlockElement; @@ -33,6 +34,7 @@ public static Gson createSnakeCase(boolean failOnUnknownProperties, boolean unkn .registerTypeAdapter(LayoutBlock.class, new GsonLayoutBlockFactory(failOnUnknownProperties)) .registerTypeAdapter(BlockElement.class, new GsonBlockElementFactory(failOnUnknownProperties)) .registerTypeAdapter(ContextBlockElement.class, new GsonContextBlockElementFactory(failOnUnknownProperties)) + .registerTypeAdapter(ContextActionsBlockElement.class, new GsonContextActionsBlockElementFactory(failOnUnknownProperties)) .registerTypeAdapter(TextObject.class, new GsonTextObjectFactory(failOnUnknownProperties)) .registerTypeAdapter(RichTextElement.class, new GsonRichTextElementFactory(failOnUnknownProperties)) .registerTypeAdapter(FunctionExecutedEvent.InputValue.class,