diff --git a/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/DataTableIF.java b/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/DataTableIF.java index e91843e8..aab8a514 100644 --- a/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/DataTableIF.java +++ b/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/DataTableIF.java @@ -3,12 +3,14 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonNaming; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.hubspot.immutables.style.HubSpotStyle; import com.hubspot.slack.client.models.blocks.table.DataTableCell; import com.hubspot.slack.client.models.blocks.table.RawTextTableCell; +import com.hubspot.slack.client.models.blocks.table.deserializer.NullSafeDataTableCellRowDeserializer; import java.util.Optional; import org.immutables.value.Value; import org.immutables.value.Value.Check; @@ -31,6 +33,7 @@ default String getType() { Optional getCaption(); @Value.Parameter(order = 2) + @JsonDeserialize(contentUsing = NullSafeDataTableCellRowDeserializer.class) ImmutableList> getRows(); @JsonProperty("page_size") diff --git a/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/TableIF.java b/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/TableIF.java index 2e9df519..5f2dcaf5 100644 --- a/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/TableIF.java +++ b/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/TableIF.java @@ -1,12 +1,14 @@ package com.hubspot.slack.client.models.blocks; import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonNaming; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.hubspot.immutables.style.HubSpotStyle; import com.hubspot.slack.client.models.blocks.table.TableCell; import com.hubspot.slack.client.models.blocks.table.TableColumnSetting; +import com.hubspot.slack.client.models.blocks.table.deserializer.NullSafeTableCellRowDeserializer; import org.immutables.value.Value; import org.immutables.value.Value.Check; import org.immutables.value.Value.Immutable; @@ -24,6 +26,7 @@ default String getType() { } @Value.Parameter + @JsonDeserialize(contentUsing = NullSafeTableCellRowDeserializer.class) ImmutableList> getRows(); ImmutableList getColumnSettings(); diff --git a/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/table/deserializer/AbstractNullSafeTableCellRowDeserializer.java b/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/table/deserializer/AbstractNullSafeTableCellRowDeserializer.java new file mode 100644 index 00000000..6d0e5e0a --- /dev/null +++ b/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/table/deserializer/AbstractNullSafeTableCellRowDeserializer.java @@ -0,0 +1,36 @@ +package com.hubspot.slack.client.models.blocks.table.deserializer; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.google.common.collect.ImmutableList; +import com.hubspot.slack.client.models.blocks.table.TableCell; +import java.io.IOException; + +abstract class AbstractNullSafeTableCellRowDeserializer + extends StdDeserializer> { + + private final Class cellType; + + protected AbstractNullSafeTableCellRowDeserializer(Class cellType) { + super(Object.class); + this.cellType = cellType; + } + + protected abstract T emptyCell(); + + @Override + public ImmutableList deserialize(JsonParser parser, DeserializationContext context) + throws IOException { + ImmutableList.Builder builder = ImmutableList.builder(); + while (parser.nextToken() != JsonToken.END_ARRAY) { + if (parser.currentToken() == JsonToken.VALUE_NULL) { + builder.add(emptyCell()); + } else { + builder.add(context.readValue(parser, cellType)); + } + } + return builder.build(); + } +} diff --git a/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/table/deserializer/NullSafeDataTableCellRowDeserializer.java b/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/table/deserializer/NullSafeDataTableCellRowDeserializer.java new file mode 100644 index 00000000..b26bb2a5 --- /dev/null +++ b/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/table/deserializer/NullSafeDataTableCellRowDeserializer.java @@ -0,0 +1,17 @@ +package com.hubspot.slack.client.models.blocks.table.deserializer; + +import com.hubspot.slack.client.models.blocks.table.DataTableCell; +import com.hubspot.slack.client.models.blocks.table.RawTextTableCell; + +public class NullSafeDataTableCellRowDeserializer + extends AbstractNullSafeTableCellRowDeserializer { + + public NullSafeDataTableCellRowDeserializer() { + super(DataTableCell.class); + } + + @Override + protected DataTableCell emptyCell() { + return RawTextTableCell.of(""); + } +} diff --git a/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/table/deserializer/NullSafeTableCellRowDeserializer.java b/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/table/deserializer/NullSafeTableCellRowDeserializer.java new file mode 100644 index 00000000..3f3a29c4 --- /dev/null +++ b/slack-base/src/main/java/com/hubspot/slack/client/models/blocks/table/deserializer/NullSafeTableCellRowDeserializer.java @@ -0,0 +1,17 @@ +package com.hubspot.slack.client.models.blocks.table.deserializer; + +import com.hubspot.slack.client.models.blocks.table.RawTextTableCell; +import com.hubspot.slack.client.models.blocks.table.TableCell; + +public class NullSafeTableCellRowDeserializer + extends AbstractNullSafeTableCellRowDeserializer { + + public NullSafeTableCellRowDeserializer() { + super(TableCell.class); + } + + @Override + protected TableCell emptyCell() { + return RawTextTableCell.of(""); + } +} diff --git a/slack-base/src/test/java/com/hubspot/slack/client/models/blocks/DataTableBlockTest.java b/slack-base/src/test/java/com/hubspot/slack/client/models/blocks/DataTableBlockTest.java index 7dfa67d6..5e797131 100644 --- a/slack-base/src/test/java/com/hubspot/slack/client/models/blocks/DataTableBlockTest.java +++ b/slack-base/src/test/java/com/hubspot/slack/client/models/blocks/DataTableBlockTest.java @@ -27,6 +27,7 @@ public class DataTableBlockTest { private static final int WITH_OPTIONS_INDEX = 2; private static final int UNKNOWN_CELL_INDEX = 3; private static final int NO_CAPTION_INDEX = 4; + private static final int NULL_CELL_INDEX = 5; private static DataTable[] blocks; @BeforeClass @@ -121,6 +122,23 @@ public void itDeserializesUnknownCellAsUnknownTableCell() { .isInstanceOf(UnknownTableCell.class); } + @Test + public void itDeserializesNullCellsAsBlankRawTextCells() { + assertThat(blocks[NULL_CELL_INDEX].getRows()).hasSize(2); + assertThat(blocks[NULL_CELL_INDEX].getRows().get(0)) + .containsExactly( + RawTextTableCell.of("Name"), + RawTextTableCell.of(""), + RawTextTableCell.of("Department") + ); + assertThat(blocks[NULL_CELL_INDEX].getRows().get(1)) + .containsExactly( + RawTextTableCell.of("Alice"), + RawTextTableCell.of(""), + RawTextTableCell.of("Engineering") + ); + } + @Test public void itSerializesAndDeserializes() throws IOException { DataTable original = DataTable diff --git a/slack-base/src/test/java/com/hubspot/slack/client/models/blocks/TableBlockTest.java b/slack-base/src/test/java/com/hubspot/slack/client/models/blocks/TableBlockTest.java index 0b0c46d9..a3087b0b 100644 --- a/slack-base/src/test/java/com/hubspot/slack/client/models/blocks/TableBlockTest.java +++ b/slack-base/src/test/java/com/hubspot/slack/client/models/blocks/TableBlockTest.java @@ -28,6 +28,7 @@ public class TableBlockTest { private static final int COLUMN_SETTINGS_INDEX = 2; private static final int BLOCK_ID_INDEX = 3; private static final int UNKNOWN_CELL_INDEX = 4; + private static final int NULL_CELL_INDEX = 5; private static Table[] blocks; @BeforeClass @@ -174,6 +175,17 @@ public void itFailsToBuildWithTooManyColumnSettings() { fail("Expected IllegalStateException for too many column settings"); } + @Test + public void itDeserializesNullCellsAsBlankRawTextCells() { + assertThat(blocks[NULL_CELL_INDEX].getRows()).hasSize(1); + assertThat(blocks[NULL_CELL_INDEX].getRows().get(0)) + .containsExactly( + RawTextTableCell.of("Name"), + RawTextTableCell.of(""), + RawTextTableCell.of("Department") + ); + } + @Test public void itFailsToBuildWithBlockIdExceedingMaxLength() { try { diff --git a/slack-base/src/test/resources/data_table_block.json b/slack-base/src/test/resources/data_table_block.json index 562253d7..d30b8ab9 100644 --- a/slack-base/src/test/resources/data_table_block.json +++ b/slack-base/src/test/resources/data_table_block.json @@ -85,5 +85,20 @@ {"type": "raw_text", "text": "Body"} ] ] + }, + { + "type": "data_table", + "rows": [ + [ + {"type": "raw_text", "text": "Name"}, + null, + {"type": "raw_text", "text": "Department"} + ], + [ + {"type": "raw_text", "text": "Alice"}, + null, + {"type": "raw_text", "text": "Engineering"} + ] + ] } ] diff --git a/slack-base/src/test/resources/table_block.json b/slack-base/src/test/resources/table_block.json index 9586a79f..155d21ec 100644 --- a/slack-base/src/test/resources/table_block.json +++ b/slack-base/src/test/resources/table_block.json @@ -62,5 +62,15 @@ {"type": "future_slack_cell_type"} ] ] + }, + { + "type": "table", + "rows": [ + [ + {"type": "raw_text", "text": "Name"}, + null, + {"type": "raw_text", "text": "Department"} + ] + ] } ]