Skip to content
Merged
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 @@ -34,6 +34,13 @@
@JsonSubTypes.Type(value = Table.class, name = Table.TYPE),
@JsonSubTypes.Type(value = DataTable.class, name = DataTable.TYPE),
@JsonSubTypes.Type(value = ContainerBlock.class, name = ContainerBlock.TYPE),
@JsonSubTypes.Type(value = VideoBlock.class, name = VideoBlock.TYPE),
@JsonSubTypes.Type(value = Carousel.class, name = Carousel.TYPE),
@JsonSubTypes.Type(value = Plan.class, name = Plan.TYPE),
@JsonSubTypes.Type(
value = DataVisualizationBlock.class,
name = DataVisualizationBlock.TYPE
),
}
)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.hubspot.slack.client.models.blocks;

import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy;
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 org.immutables.value.Value;
import org.immutables.value.Value.Check;
import org.immutables.value.Value.Immutable;

/**
* Slack's carousel block — a horizontally-scrolling container for related cards.
* @see <a href="https://docs.slack.dev/reference/block-kit/blocks/carousel-block">Carousel block</a>
*/
@Immutable
@HubSpotStyle
@JsonNaming(SnakeCaseStrategy.class)
public interface CarouselIF extends Block {
String TYPE = "carousel";

@Override
@Value.Derived
default String getType() {
return TYPE;
}

ImmutableList<Card> getElements();

@Check
default void check() {
Preconditions.checkState(
!getElements().isEmpty() && getElements().size() <= 10,
"carousel must contain between 1 and 10 cards"
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
@JsonSubTypes.Type(value = RichTextBlock.class, name = RichTextBlock.TYPE),
@JsonSubTypes.Type(value = Section.class, name = Section.TYPE),
@JsonSubTypes.Type(value = Table.class, name = Table.TYPE),
@JsonSubTypes.Type(value = VideoBlock.class, name = VideoBlock.TYPE),
}
)
public interface ContainerChildBlock {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.hubspot.slack.client.models.blocks;

import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.google.common.base.Preconditions;
import com.hubspot.immutables.style.HubSpotStyle;
import com.hubspot.slack.client.models.blocks.charts.Chart;
import org.immutables.value.Value;
import org.immutables.value.Value.Check;
import org.immutables.value.Value.Immutable;

/**
* Slack's data visualization block renders a pie, bar, area, or line chart.
* Limit of 2 data visualization blocks per message.
* @see <a href="https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block">Data visualization block</a>
*/
@Immutable
@HubSpotStyle
@JsonNaming(SnakeCaseStrategy.class)
public interface DataVisualizationBlockIF extends Block {
String TYPE = "data_visualization";

@Override
@Value.Derived
default String getType() {
return TYPE;
}

String getTitle();

Chart getChart();

@Check
default void check() {
Preconditions.checkState(
getTitle().length() <= 50,
"data_visualization title cannot exceed 50 characters"
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.hubspot.slack.client.models.blocks;

import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy;
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 org.immutables.value.Value;
import org.immutables.value.Value.Check;
import org.immutables.value.Value.Immutable;

/**
* Slack's plan block — a collection of related task cards.
* @see <a href="https://docs.slack.dev/reference/block-kit/blocks/plan-block">Plan block</a>
*/
@Immutable
@HubSpotStyle
@JsonNaming(SnakeCaseStrategy.class)
public interface PlanIF extends Block {
String TYPE = "plan";

@Override
@Value.Derived
default String getType() {
return TYPE;
}

String getTitle();

ImmutableList<PlanTask> getTasks();

@Check
default void check() {
Preconditions.checkState(
!getTasks().isEmpty() && getTasks().size() <= 50,
"plan must contain between 1 and 50 tasks"
);
long distinctTaskIds = getTasks()
.stream()
.map(PlanTask::getTaskId)
.distinct()
.count();
Preconditions.checkState(
distinctTaskIds == getTasks().size(),
"each task_id in a plan must be unique"
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.hubspot.slack.client.models.blocks;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.google.common.collect.ImmutableList;
import com.hubspot.immutables.style.HubSpotStyle;
import com.hubspot.slack.client.models.blocks.elements.UrlSource;
import com.hubspot.slack.client.models.blocks.elements.richtextelements.RichTextBlock;
import java.util.Optional;
import org.immutables.value.Value;
import org.immutables.value.Value.Immutable;

/**
* A task within a {@link Plan}. Per Slack's plan block docs, a plan's task objects carry no
* {@code type} field, so this is modeled as a plain object rather than reusing the polymorphic
* {@code task_card} block ({@code TaskCardBlock}). That lets it both serialize without a {@code type}
* and deserialize inbound plan tasks that omit one (an incoming {@code type} is ignored). Fields
* mirror the task card.
* @see <a href="https://docs.slack.dev/reference/block-kit/blocks/plan-block">Plan block</a>
*/
@Immutable
@HubSpotStyle
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonNaming(SnakeCaseStrategy.class)
public interface PlanTaskIF {
@Value.Parameter(order = 1)
String getTaskId();

@Value.Parameter(order = 2)
String getTitle();

Optional<RichTextBlock> getDetails();

Optional<RichTextBlock> getOutput();

ImmutableList<UrlSource> getSources();

Optional<TaskCardBlockStatus> getStatus();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.hubspot.slack.client.models.blocks;

import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.google.common.base.Preconditions;
import com.hubspot.immutables.style.HubSpotStyle;
import com.hubspot.slack.client.models.blocks.objects.Text;
import com.hubspot.slack.client.models.blocks.objects.TextType;
import java.util.Optional;
import org.immutables.value.Value;
import org.immutables.value.Value.Check;
import org.immutables.value.Value.Immutable;

/**
* Slack's video block embeds a video player in a message, modal, or home tab.
* Sending one requires the {@code links.embed:write} scope and that {@code video_url} matches one of
* the app's configured unfurl domains and points to an embeddable HTTPS URL.
* @see <a href="https://docs.slack.dev/reference/block-kit/blocks/video-block">Video block</a>
*/
@Immutable
@HubSpotStyle
@JsonNaming(SnakeCaseStrategy.class)
public interface VideoBlockIF extends Block, ContainerChildBlock {
String TYPE = "video";
String HTTPS_PREFIX = "https://";

@Override
@Value.Derived
default String getType() {
return TYPE;
}

String getAltText();

Text getTitle();

String getThumbnailUrl();

String getVideoUrl();

Optional<String> getTitleUrl();

Optional<Text> getDescription();

Optional<String> getProviderName();

Optional<String> getProviderIconUrl();

Optional<String> getAuthorName();

@Check
default void check() {
Preconditions.checkState(
getTitle().getType() == TextType.PLAIN_TEXT,
"video title must be a plain_text text object"
);
Preconditions.checkState(
getTitle().getText().length() < 200,
"video title must be less than 200 characters"
);
getDescription()
.ifPresent(description -> {
Preconditions.checkState(
description.getType() == TextType.PLAIN_TEXT,
"video description must be a plain_text text object"
);
Preconditions.checkState(
description.getText().length() < 200,
"video description must be less than 200 characters"
);
});
getAuthorName()
.ifPresent(authorName ->
Preconditions.checkState(
authorName.length() < 50,
"video author_name must be less than 50 characters"
)
);
Preconditions.checkState(
isHttpsUrl(getVideoUrl()),
"video video_url must be an HTTPS URL"
);
getTitleUrl()
.ifPresent(titleUrl ->
Preconditions.checkState(
isHttpsUrl(titleUrl),
"video title_url must be an HTTPS URL"
)
);
}

/** Case-insensitive {@code https://} prefix check (URL schemes are case-insensitive). */
static boolean isHttpsUrl(String url) {
return url.regionMatches(true, 0, HTTPS_PREFIX, 0, HTTPS_PREFIX.length());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.hubspot.slack.client.models.blocks.charts;

import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.google.common.collect.ImmutableList;
import com.hubspot.immutables.style.HubSpotStyle;
import org.immutables.value.Value;
import org.immutables.value.Value.Check;
import org.immutables.value.Value.Immutable;

/**
* An {@code area} chart for the data visualization block.
* @see <a href="https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block/#area">Data visualization block — area</a>
*/
@Immutable
@HubSpotStyle
@JsonNaming(SnakeCaseStrategy.class)
public interface AreaChartIF extends Chart {
String TYPE = "area";

@Override
@Value.Derived
default String getType() {
return TYPE;
}

ImmutableList<ChartDataSeries> getSeries();

ChartAxisConfig getAxisConfig();

@Check
default void check() {
Charts.checkSeries(getSeries());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.hubspot.slack.client.models.blocks.charts;

import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy;
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 org.immutables.value.Value;
import org.immutables.value.Value.Check;
import org.immutables.value.Value.Immutable;

/**
* A {@code bar} chart for the data visualization block.
* @see <a href="https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block/#bar">Data visualization block — bar</a>
*/
@Immutable
@HubSpotStyle
@JsonNaming(SnakeCaseStrategy.class)
public interface BarChartIF extends Chart {
String TYPE = "bar";

@Override
@Value.Derived
default String getType() {
return TYPE;
}

ImmutableList<ChartDataSeries> getSeries();

ChartAxisConfig getAxisConfig();

@Check
default void check() {
Charts.checkSeries(getSeries());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.hubspot.slack.client.models.blocks.charts;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

/**
* The polymorphic {@code chart} payload of a data visualization block. The concrete type is
* discriminated by {@code type}: {@code pie}, {@code bar}, {@code area}, or {@code line}.
* @see <a href="https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block">Data visualization block</a>
*/
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "type",
defaultImpl = UnknownChart.class
)
@JsonSubTypes(
{
@JsonSubTypes.Type(value = PieChart.class, name = PieChart.TYPE),
@JsonSubTypes.Type(value = BarChart.class, name = BarChart.TYPE),
@JsonSubTypes.Type(value = AreaChart.class, name = AreaChart.TYPE),
@JsonSubTypes.Type(value = LineChart.class, name = LineChart.TYPE),
}
)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public interface Chart {
String getType();
}
Loading
Loading