Skip to content

Commit fe0c436

Browse files
zimegClaude
andauthored
feat(model): add plan block (#1680)
* feat(model): add plan block Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com> * chore(model): match sibling blocks without a block_id comment Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com> --------- Co-authored-by: Claude <svc-devxp-claude@slack-corp.com>
1 parent 849d3f7 commit fe0c436

4 files changed

Lines changed: 192 additions & 0 deletions

File tree

‎slack-api-model/src/main/java/com/slack/api/model/block/Blocks.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ public static ContainerBlock container(ModelConfigurator<ContainerBlock.Containe
159159
return configurator.configure(ContainerBlock.builder()).build();
160160
}
161161

162+
// PlanBlock
163+
164+
public static PlanBlock plan(ModelConfigurator<PlanBlock.PlanBlockBuilder> configurator) {
165+
return configurator.configure(PlanBlock.builder()).build();
166+
}
167+
162168
// TaskCardBlock
163169

164170
public static TaskCardBlock taskCard(ModelConfigurator<TaskCardBlock.TaskCardBlockBuilder> configurator) {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.slack.api.model.block;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
import java.util.List;
9+
10+
/**
11+
* Displays a collection of related tasks.
12+
*
13+
* https://docs.slack.dev/reference/block-kit/blocks/plan-block
14+
*/
15+
@Data
16+
@Builder
17+
@NoArgsConstructor
18+
@AllArgsConstructor
19+
public class PlanBlock implements LayoutBlock {
20+
public static final String TYPE = "plan";
21+
22+
/**
23+
* The type of block. For this block, type will always be {@code plan}.
24+
*/
25+
private final String type = TYPE;
26+
27+
/**
28+
* Title of the plan in plain text.
29+
*/
30+
private String title;
31+
32+
/**
33+
* A sequence of task card blocks, maximum of 50 tasks. Each one contains task-like objects without a type,
34+
* and each {@code task_id} in a plan must be unique.
35+
*/
36+
private List<TaskCardBlock> tasks;
37+
38+
private String blockId;
39+
}

‎slack-api-model/src/main/java/com/slack/api/util/json/GsonLayoutBlockFactory.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ private Class<? extends LayoutBlock> getLayoutClassInstance(String typeName) {
6363
return InputBlock.class;
6464
case MarkdownBlock.TYPE:
6565
return MarkdownBlock.class;
66+
case PlanBlock.TYPE:
67+
return PlanBlock.class;
6668
case RichTextBlock.TYPE:
6769
return RichTextBlock.class;
6870
case SectionBlock.TYPE:

‎slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java‎

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2456,6 +2456,151 @@ public void buildTaskCardBlock() {
24562456
assertThat(taskCard.getSources().size(), is(1));
24572457
}
24582458

2459+
@Test
2460+
public void parsePlanBlock() {
2461+
String json = "{\n" +
2462+
" \"blocks\": [\n" +
2463+
" {\n" +
2464+
" \"type\": \"plan\",\n" +
2465+
" \"title\": \"Thinking completed\",\n" +
2466+
" \"tasks\": [\n" +
2467+
" {\n" +
2468+
" \"task_id\": \"call_001\",\n" +
2469+
" \"title\": \"Fetched user profile information\",\n" +
2470+
" \"status\": \"in_progress\",\n" +
2471+
" \"details\": {\n" +
2472+
" \"type\": \"rich_text\",\n" +
2473+
" \"block_id\": \"viMWO\",\n" +
2474+
" \"elements\": [\n" +
2475+
" {\n" +
2476+
" \"type\": \"rich_text_section\",\n" +
2477+
" \"elements\": [\n" +
2478+
" {\n" +
2479+
" \"type\": \"text\",\n" +
2480+
" \"text\": \"Searched database...\"\n" +
2481+
" }\n" +
2482+
" ]\n" +
2483+
" }\n" +
2484+
" ]\n" +
2485+
" },\n" +
2486+
" \"output\": {\n" +
2487+
" \"type\": \"rich_text\",\n" +
2488+
" \"block_id\": \"viMWO\",\n" +
2489+
" \"elements\": [\n" +
2490+
" {\n" +
2491+
" \"type\": \"rich_text_section\",\n" +
2492+
" \"elements\": [\n" +
2493+
" {\n" +
2494+
" \"type\": \"text\",\n" +
2495+
" \"text\": \"Profile data loaded\"\n" +
2496+
" }\n" +
2497+
" ]\n" +
2498+
" }\n" +
2499+
" ]\n" +
2500+
" }\n" +
2501+
" },\n" +
2502+
" {\n" +
2503+
" \"task_id\": \"call_002\",\n" +
2504+
" \"title\": \"Checked user permissions\",\n" +
2505+
" \"status\": \"in_progress\"\n" +
2506+
" },\n" +
2507+
" {\n" +
2508+
" \"task_id\": \"call_003\",\n" +
2509+
" \"title\": \"Generated comprehensive user report\",\n" +
2510+
" \"status\": \"complete\",\n" +
2511+
" \"output\": {\n" +
2512+
" \"type\": \"rich_text\",\n" +
2513+
" \"block_id\": \"crsk\",\n" +
2514+
" \"elements\": [\n" +
2515+
" {\n" +
2516+
" \"type\": \"rich_text_section\",\n" +
2517+
" \"elements\": [\n" +
2518+
" {\n" +
2519+
" \"type\": \"text\",\n" +
2520+
" \"text\": \"15 data points compiled\"\n" +
2521+
" }\n" +
2522+
" ]\n" +
2523+
" }\n" +
2524+
" ]\n" +
2525+
" }\n" +
2526+
" }\n" +
2527+
" ]\n" +
2528+
" }\n" +
2529+
" ]\n" +
2530+
"}";
2531+
Gson gson = GsonFactory.createSnakeCase();
2532+
Message message = gson.fromJson(json, Message.class);
2533+
assertThat(message, is(notNullValue()));
2534+
assertThat(message.getBlocks().size(), is(1));
2535+
2536+
PlanBlock plan = (PlanBlock) message.getBlocks().get(0);
2537+
assertThat(plan.getType(), is("plan"));
2538+
assertThat(plan.getTitle(), is("Thinking completed"));
2539+
assertThat(plan.getTasks().size(), is(3));
2540+
2541+
assertThat(plan.getTasks().get(0).getTaskId(), is("call_001"));
2542+
assertThat(plan.getTasks().get(0).getTitle(), is("Fetched user profile information"));
2543+
assertThat(plan.getTasks().get(0).getStatus(), is("in_progress"));
2544+
assertThat(plan.getTasks().get(0).getDetails().getBlockId(), is("viMWO"));
2545+
assertThat(plan.getTasks().get(0).getOutput().getBlockId(), is("viMWO"));
2546+
2547+
assertThat(plan.getTasks().get(1).getTaskId(), is("call_002"));
2548+
assertThat(plan.getTasks().get(1).getTitle(), is("Checked user permissions"));
2549+
assertThat(plan.getTasks().get(1).getStatus(), is("in_progress"));
2550+
2551+
assertThat(plan.getTasks().get(2).getTaskId(), is("call_003"));
2552+
assertThat(plan.getTasks().get(2).getTitle(), is("Generated comprehensive user report"));
2553+
assertThat(plan.getTasks().get(2).getStatus(), is("complete"));
2554+
assertThat(plan.getTasks().get(2).getOutput().getBlockId(), is("crsk"));
2555+
}
2556+
2557+
@Test
2558+
public void buildPlanBlock() {
2559+
PlanBlock plan = Blocks.plan(p -> p
2560+
.title("Thinking completed")
2561+
.tasks(Arrays.asList(
2562+
Blocks.taskCard(t -> t
2563+
.taskId("call_001")
2564+
.title("Fetched user profile information")
2565+
.status("in_progress")
2566+
.details(Blocks.richText(r -> r
2567+
.blockId("viMWO")
2568+
.elements(Arrays.asList(RichTextSectionElement.builder()
2569+
.elements(Arrays.asList(RichTextSectionElement.Text.builder()
2570+
.text("Searched database...")
2571+
.build()))
2572+
.build()))))
2573+
.output(Blocks.richText(r -> r
2574+
.blockId("viMWO")
2575+
.elements(Arrays.asList(RichTextSectionElement.builder()
2576+
.elements(Arrays.asList(RichTextSectionElement.Text.builder()
2577+
.text("Profile data loaded")
2578+
.build()))
2579+
.build()))))),
2580+
Blocks.taskCard(t -> t
2581+
.taskId("call_002")
2582+
.title("Checked user permissions")
2583+
.status("in_progress")),
2584+
Blocks.taskCard(t -> t
2585+
.taskId("call_003")
2586+
.title("Generated comprehensive user report")
2587+
.status("complete")
2588+
.output(Blocks.richText(r -> r
2589+
.blockId("crsk")
2590+
.elements(Arrays.asList(RichTextSectionElement.builder()
2591+
.elements(Arrays.asList(RichTextSectionElement.Text.builder()
2592+
.text("15 data points compiled")
2593+
.build()))
2594+
.build()))))))));
2595+
assertThat(plan, is(notNullValue()));
2596+
assertThat(plan.getType(), is("plan"));
2597+
assertThat(plan.getTitle(), is("Thinking completed"));
2598+
assertThat(plan.getTasks().size(), is(3));
2599+
assertThat(plan.getTasks().get(0).getTaskId(), is("call_001"));
2600+
assertThat(plan.getTasks().get(1).getTaskId(), is("call_002"));
2601+
assertThat(plan.getTasks().get(2).getTaskId(), is("call_003"));
2602+
}
2603+
24592604
@Test
24602605
public void parseAuthProviderBlock() {
24612606
String json = "{\"type\":\"home\",\"blocks\":[\n" +

0 commit comments

Comments
 (0)