Add container block support - #462
Conversation
The Slack Block Kit API added a container block type (https://docs.slack.dev/reference/block-kit/blocks/container-block). This adds ContainerBlockIF/ContainerBlock following the same Immutables + @JsonSubTypes pattern used by all other block types, including validation for title/subtitle length (≤150 chars) and child_blocks count (≤10). Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
yyakhnytsia
left a comment
There was a problem hiding this comment.
reviewed, left few comments.
| return TYPE; | ||
| } | ||
|
|
||
| Text getTitle(); |
There was a problem hiding this comment.
According to the documentation title is optional:
Optional (one of title or rich_text_title is required)
Also, it would be nice to have the other optional fields from the Slack documentation to have the full functionality of this component.
There was a problem hiding this comment.
Good point! title is now Optional<Text>, and I've added rich_text_title (Optional<RichTextBlock>) with a @Check validation that at least one of the two is present.
I also went through the full spec and added the remaining optional fields: subtitle, icon, width, is_collapsible, default_collapse and has_header_divider.
|
|
||
| Text getTitle(); | ||
|
|
||
| ImmutableList<Block> getChildBlocks(); |
There was a problem hiding this comment.
Could you please check the supported type blocks: https://docs.slack.dev/reference/block-kit/blocks/container-block/#child-blocks
The used block has more types than are supported by the container type.
Also if we use the Block component it is possible to use recursive blocks: ContainerBlock inside ContainerBlock as a child component; not sure if such a structure is allowed.
There was a problem hiding this comment.
Thanks for flagging this. I've looked at a few approaches for constraining child_blocks:
Option A — dedicated union interface (ContainerChildBlock) following the BlockElement/RichTextObject pattern. This enforces the constraint at compile time, but requires every supported block type’s IF interface to extend ContainerChildBlock, and both Block.java and ContainerChildBlock.java would need updating whenever a new block type is added. My concern is that this would become a maintenance burden over time.
Option B — allowlist in @Check using a Set<String> of the allowed TYPE constants (e.g. Actions.TYPE, Divider.TYPE, etc.), defined in ContainerBlockIF. The constraint lives in one place, unknown types (future Slack block types that deserialize as UnknownBlock) pass through for forward compatibility, and disallowed known types (e.g. card) are caught at build time with a clear error.
I'm inclined to go with Option B, but wanted to check your thoughts.
There was a problem hiding this comment.
If you have concerns about option A, then option B is also the case. For me, Option A is better for development, since we have a clear restriction.
|
|
||
| @Check | ||
| default void check() { | ||
| Preconditions.checkState( |
There was a problem hiding this comment.
Text type validation is missing:
Title of the container, using plain_text formatting
We have two types of Text: plain_text and Markdown
There was a problem hiding this comment.
Fixed. We now validate in @Check that when title is present, its TextType is PLAIN_TEXT — building with a mrkdwn title throws IllegalStateException: title must use plain_text formatting.
- title is now Optional (one of title or rich_text_title is required) - Add rich_text_title field (RichTextBlock) - Add has_header_divider field - Validate title type must be plain_text - Validate at least one of title/rich_text_title is present - Prevent recursive container blocks in child_blocks Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- default_collapsed requires is_collapsible to be true - has_header_divider cannot be set on collapsible blocks Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Introduces ContainerChildBlock, a @JsonSubTypes-registered union interface following the BlockElement/RichTextObject pattern, so that child_blocks is typed as ImmutableList<ContainerChildBlock> rather than the unconstrained ImmutableList<Block>. Supported types (actions, context, divider, file, header, image, input, rich_text, section, table) implement the interface; UnknownBlock is the defaultImpl for forward compatibility with future Slack block types. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Why
The Slack Block Kit API introduced an official
containerblock type. This adds support for deserializing and building container blocks in this library, following the same patterns as other recently-added block types (card, task_card, data_table, table).What changed
ContainerBlockIF/ContainerBlock— Immutables implementation of thecontainerblock with all Slack API fields: requiredtitle(Text) andchild_blocks(ImmutableList); optionalsubtitle,icon(Image element),width,is_collapsible,default_collapsed, andblock_idContainerBlockWidth— enum for thewidthfield values (narrow,standard,wide,full)BlockElementLengthLimits— addedMAX_CONTAINER_CHILD_BLOCKS(10)Block.java— registeredContainerBlockin@JsonSubTypes@JsonInclude(NON_ABSENT)(notNON_EMPTY) on optional fields, per pattern established in PR Add card block support #456 to avoid Slack returninginvalid_argumentsValidation
@Checkenforces:titletext ≤ 150 characterssubtitletext ≤ 150 characters (when present)child_blockscount ≤ 10Test plan
ContainerBlockTestcovers deserialization of all fields, minimal block, round-trip serialization, and all three validation failure casesmvn testpasses locally for theslack-basemoduleBRAVE
@JsonSubTypes.Typeentry is additive; existing code deserializing blocks is unaffected