Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
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.DataTableCell;
import com.slack.api.model.block.LayoutBlock;
import com.slack.api.model.block.composition.TextObject;
import com.slack.api.model.block.element.BlockElement;
Expand Down Expand Up @@ -79,6 +80,7 @@ public static void registerTypeAdapters(GsonBuilder builder, boolean failOnUnkno
.registerTypeAdapter(ContextActionsBlockElement.class, new GsonContextActionsBlockElementFactory(failOnUnknownProps))
.registerTypeAdapter(BlockElement.class, new GsonBlockElementFactory(failOnUnknownProps))
.registerTypeAdapter(RichTextElement.class, new GsonRichTextElementFactory(failOnUnknownProps))
.registerTypeAdapter(DataTableCell.class, new GsonDataTableCellFactory(failOnUnknownProps))
.registerTypeAdapter(FunctionExecutedEvent.InputValue.class, new GsonFunctionExecutedEventInputValueFactory())
.registerTypeAdapter(Attachment.VideoHtml.class, new GsonMessageAttachmentVideoHtmlFactory(failOnUnknownProps))
.registerTypeAdapter(MessageChangedEvent.PreviousMessage.class, new GsonMessageChangedEventPreviousMessageFactory(failOnUnknownProps))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ public static SectionBlock section(ModelConfigurator<SectionBlock.SectionBlockBu
return configurator.configure(SectionBlock.builder()).build();
}

// DataTableBlock

public static DataTableBlock dataTable(ModelConfigurator<DataTableBlock.DataTableBlockBuilder> configurator) {
return configurator.configure(DataTableBlock.builder()).build();
}

// VideoBlock
public static VideoBlock video(ModelConfigurator<VideoBlock.VideoBlockBuilder> configurator) {
return configurator.configure(VideoBlock.builder()).build();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
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 a sortable, paginated grid of tabular data.
*
* <p>Each row is a list of {@link DataTableCell cells}; a cell may be a
* {@link RawTextDataTableCell} ({@code raw_text}), {@link RawNumberDataTableCell}
* ({@code raw_number}), or a {@link RichTextBlock} ({@code rich_text}). The first
* row is the header row; header cells cannot use {@code rich_text}. A data table
* supports 1-20 columns and up to 100 data rows (101 rows including the header),
* with all rows sharing the same column count.</p>
*
* @see <a href="https://docs.slack.dev/reference/block-kit/blocks/data-table-block">Data table block</a>
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DataTableBlock implements LayoutBlock {
public static final String TYPE = "data_table";
private final String type = TYPE;

/**
* The rows of the table. Each row is a list of cells. The first row is the header row.
* Minimum 2 rows (header plus one data row); maximum 101 rows. Every row must contain
* the same number of cells (1-20).
*/
@Builder.Default
private List<List<DataTableCell>> rows = new ArrayList<>();

/**
* Required. The caption describing the table, used as the caption of the rendered HTML element.
*/
private String caption;

/**
* The number of rows shown per page. Valid range 1-100; defaults to 5 when omitted.
*/
private Integer pageSize;

/**
* Zero-based index of the column that identifies each row. Defaults to 0 when omitted.
*/
private Integer rowHeaderColumnIndex;

private String blockId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.slack.api.model.block;

/**
* A single cell within a {@link DataTableBlock} row. A cell can be one of:
*
* <ul>
* <li>{@link RawTextDataTableCell} ({@code raw_text})</li>
* <li>{@link RawNumberDataTableCell} ({@code raw_number})</li>
* <li>{@link com.slack.api.model.block.RichTextBlock RichTextBlock} ({@code rich_text})</li>
* </ul>
*
* <p>Header cells (those in the first row) cannot use the {@code rich_text} type.</p>
*
* @see <a href="https://docs.slack.dev/reference/block-kit/blocks/data-table-block">Data table block</a>
*/
public interface DataTableCell {

String getType();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.slack.api.model.block;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* A {@code raw_number} cell within a {@link DataTableBlock}, holding a numeric value.
* The {@code text} field carries the display representation of the value (for example a
* formatted string) and must be at least one character long. Columns made up entirely of
* {@code raw_number} cells are sorted numerically.
*
* @see <a href="https://docs.slack.dev/reference/block-kit/blocks/data-table-block">Data table block</a>
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RawNumberDataTableCell implements DataTableCell {
public static final String TYPE = "raw_number";
private final String type = TYPE;
private Double value;
private String text;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.slack.api.model.block;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* A {@code raw_text} cell within a {@link DataTableBlock}, holding unformatted plain text.
* The {@code text} must be at least one character long.
*
* @see <a href="https://docs.slack.dev/reference/block-kit/blocks/data-table-block">Data table block</a>
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RawTextDataTableCell implements DataTableCell {
public static final String TYPE = "raw_text";
private final String type = TYPE;
private String text;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RichTextBlock implements LayoutBlock {
public class RichTextBlock implements LayoutBlock, DataTableCell {
public static final String TYPE = "rich_text";
private final String type = TYPE;
@Builder.Default
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.slack.api.util.json;

import com.google.gson.*;
import com.slack.api.model.block.DataTableCell;
import com.slack.api.model.block.RawNumberDataTableCell;
import com.slack.api.model.block.RawTextDataTableCell;
import com.slack.api.model.block.RichTextBlock;

import java.lang.reflect.Type;

/**
* Factory for deserializing the cells of a
* {@link com.slack.api.model.block.DataTableBlock data table block}.
*
* @see <a href="https://docs.slack.dev/reference/block-kit/blocks/data-table-block">Data table block</a>
*/
public class GsonDataTableCellFactory implements JsonDeserializer<DataTableCell>, JsonSerializer<DataTableCell> {

private final boolean failOnUnknownProperties;

public GsonDataTableCellFactory() {
this(false);
}

public GsonDataTableCellFactory(boolean failOnUnknownProperties) {
this.failOnUnknownProperties = failOnUnknownProperties;
}

@Override
public DataTableCell 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<? extends DataTableCell> clazz = getDataTableCellClassInstance(typeName);
return context.deserialize(jsonObject, clazz);
}

@Override
public JsonElement serialize(DataTableCell src, Type typeOfSrc, JsonSerializationContext context) {
return context.serialize(src);
}

private Class<? extends DataTableCell> getDataTableCellClassInstance(String typeName) {
switch (typeName) {
case RawTextDataTableCell.TYPE:
return RawTextDataTableCell.class;
case RawNumberDataTableCell.TYPE:
return RawNumberDataTableCell.class;
case RichTextBlock.TYPE:
return RichTextBlock.class;
default:
throw new JsonParseException("Unknown data table cell type: " + typeName);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ private Class<? extends LayoutBlock> getLayoutClassInstance(String typeName) {
return VideoBlock.class;
case RichTextBlock.TYPE:
return RichTextBlock.class;
case DataTableBlock.TYPE:
return DataTableBlock.class;
case ShareShortcutBlock.TYPE:
return ShareShortcutBlock.class;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1742,6 +1742,94 @@ public void parseVideoBlocks() {
assertEquals("https://www.youtube.com/embed/RRxQQxiM7AA?feature=oembed&autoplay=1", block.getVideoUrl());
}

@Test
public void parseDataTableBlock() {
// https://docs.slack.dev/reference/block-kit/blocks/data-table-block
String json = "{\n" +
" \"blocks\": [\n" +
" {\n" +
" \"type\": \"data_table\",\n" +
" \"block_id\": \"bid\",\n" +
" \"caption\": \"Quarterly results\",\n" +
" \"page_size\": 10,\n" +
" \"row_header_column_index\": 0,\n" +
" \"rows\": [\n" +
" [\n" +
" { \"type\": \"raw_text\", \"text\": \"Team\" },\n" +
" { \"type\": \"raw_text\", \"text\": \"Revenue\" }\n" +
" ],\n" +
" [\n" +
" {\n" +
" \"type\": \"rich_text\",\n" +
" \"elements\": [\n" +
" {\n" +
" \"type\": \"rich_text_section\",\n" +
" \"elements\": [ { \"type\": \"text\", \"text\": \"Platform\" } ]\n" +
" }\n" +
" ]\n" +
" },\n" +
" { \"type\": \"raw_number\", \"value\": 1234.5, \"text\": \"$1,234.50\" }\n" +
" ]\n" +
" ]\n" +
" }\n" +
" ]\n" +
"}";
Message message = GsonFactory.createSnakeCase().fromJson(json, Message.class);
assertThat(message, is(notNullValue()));
assertThat(message.getBlocks().size(), is(1));

DataTableBlock block = (DataTableBlock) message.getBlocks().get(0);
assertThat(block.getType(), is("data_table"));
assertThat(block.getBlockId(), is("bid"));
assertThat(block.getCaption(), is("Quarterly results"));
assertThat(block.getPageSize(), is(10));
assertThat(block.getRowHeaderColumnIndex(), is(0));
assertThat(block.getRows().size(), is(2));

RawTextDataTableCell header = (RawTextDataTableCell) block.getRows().get(0).get(0);
assertThat(header.getType(), is("raw_text"));
assertThat(header.getText(), is("Team"));

DataTableCell richCell = block.getRows().get(1).get(0);
assertTrue(richCell instanceof RichTextBlock);
assertThat(richCell.getType(), is("rich_text"));

RawNumberDataTableCell numberCell = (RawNumberDataTableCell) block.getRows().get(1).get(1);
assertThat(numberCell.getType(), is("raw_number"));
assertThat(numberCell.getValue(), is(1234.5));
assertThat(numberCell.getText(), is("$1,234.50"));
}

@Test
public void buildDataTableBlock() {
DataTableBlock block = dataTable(t -> t
.blockId("bid")
.caption("Quarterly results")
.pageSize(10)
.rowHeaderColumnIndex(0)
.rows(Arrays.asList(
Arrays.asList(
RawTextDataTableCell.builder().text("Team").build(),
RawTextDataTableCell.builder().text("Revenue").build()
),
Arrays.asList(
RawTextDataTableCell.builder().text("Platform").build(),
RawNumberDataTableCell.builder().value(1234.5).text("$1,234.50").build()
)
)));
assertThat(block, is(notNullValue()));

Gson gson = GsonFactory.createSnakeCase();
String json = gson.toJson(block);
DataTableBlock restored = (DataTableBlock) gson.fromJson(json, LayoutBlock.class);
assertThat(restored.getType(), is("data_table"));
assertThat(restored.getCaption(), is("Quarterly results"));
assertThat(restored.getPageSize(), is(10));
assertThat(restored.getRows().size(), is(2));
RawNumberDataTableCell numberCell = (RawNumberDataTableCell) restored.getRows().get(1).get(1);
assertThat(numberCell.getValue(), is(1234.5));
}

@Test
public void parseLinkTriggerMessages() {
// https://tools.slack.dev/deno-slack-sdk/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
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.DataTableCell;
import com.slack.api.model.block.LayoutBlock;
import com.slack.api.model.block.composition.TextObject;
import com.slack.api.model.block.element.BlockElement;
Expand Down Expand Up @@ -39,6 +40,7 @@ public static Gson createSnakeCase(boolean failOnUnknownProperties, boolean unkn
.registerTypeAdapter(ContextActionsBlockElement.class, new GsonContextActionsBlockElementFactory(failOnUnknownProperties))
.registerTypeAdapter(TextObject.class, new GsonTextObjectFactory(failOnUnknownProperties))
.registerTypeAdapter(RichTextElement.class, new GsonRichTextElementFactory(failOnUnknownProperties))
.registerTypeAdapter(DataTableCell.class, new GsonDataTableCellFactory(failOnUnknownProperties))
.registerTypeAdapter(FunctionExecutedEvent.InputValue.class,
new GsonFunctionExecutedEventInputValueFactory(failOnUnknownProperties))
.registerTypeAdapter(Attachment.VideoHtml.class,
Expand Down