From a84fd29e969fd3104acf909fa91347d47ef8cfe9 Mon Sep 17 00:00:00 2001 From: Kirill Meshkov Date: Tue, 24 Mar 2026 13:37:05 +0400 Subject: [PATCH 1/8] Add detail field to CanvasesCreateResponse feat: Add "title" parameter to CanvasesCreateResponse --- .../api/methods/response/canvases/CanvasesCreateResponse.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/slack-api-client/src/main/java/com/slack/api/methods/response/canvases/CanvasesCreateResponse.java b/slack-api-client/src/main/java/com/slack/api/methods/response/canvases/CanvasesCreateResponse.java index f7efe3d61..4976f4caf 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/response/canvases/CanvasesCreateResponse.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/response/canvases/CanvasesCreateResponse.java @@ -19,6 +19,7 @@ public class CanvasesCreateResponse implements SlackApiTextResponse { private String canvasId; + private String detail; private ResponseMetadata responseMetadata; -} \ No newline at end of file +} From 00f3bd7904047d6f19d9633294070b61533700e9 Mon Sep 17 00:00:00 2001 From: Kirill Meshkov Date: Tue, 24 Mar 2026 13:38:16 +0400 Subject: [PATCH 2/8] feat: Add detail field to CanvasesEditResponse --- .../api/methods/response/canvases/CanvasesEditResponse.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/slack-api-client/src/main/java/com/slack/api/methods/response/canvases/CanvasesEditResponse.java b/slack-api-client/src/main/java/com/slack/api/methods/response/canvases/CanvasesEditResponse.java index 21ea50a61..188c26b6c 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/response/canvases/CanvasesEditResponse.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/response/canvases/CanvasesEditResponse.java @@ -17,6 +17,7 @@ public class CanvasesEditResponse implements SlackApiTextResponse { private String provided; private transient Map> httpResponseHeaders; + private String detail; private ResponseMetadata responseMetadata; -} \ No newline at end of file +} From 427a68f71073f27ded8e14fc259fff7703c648f8 Mon Sep 17 00:00:00 2001 From: Kirill Meshkov Date: Tue, 24 Mar 2026 13:39:59 +0400 Subject: [PATCH 3/8] Add error detail tests for canvases methods Added tests for error handling in canvases.create and canvases.edit methods. --- .../methods/canvases_Test.java | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/slack-api-client/src/test/java/test_with_remote_apis/methods/canvases_Test.java b/slack-api-client/src/test/java/test_with_remote_apis/methods/canvases_Test.java index 5e6b28b0c..5d32dd66c 100644 --- a/slack-api-client/src/test/java/test_with_remote_apis/methods/canvases_Test.java +++ b/slack-api-client/src/test/java/test_with_remote_apis/methods/canvases_Test.java @@ -22,10 +22,10 @@ import org.junit.Test; import java.util.Arrays; +import java.util.Collections; import java.util.List; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.assertThat; @Slf4j @@ -160,4 +160,34 @@ public void standalone_canvases_error() throws Exception { ); assertThat(creation.getError(), is("invalid_arguments")); } + + @Test + public void error_detail() throws Exception { + MethodsClient client = slack.methods(botToken); + // canvases.create + { + CanvasesCreateResponse response = client.canvasesCreate(r -> r + .title("test") + .documentContent(CanvasDocumentContent.builder() + .markdown("test") + .type("invalid") + .build()) + ); + assertThat(response.isOk(), is(false)); + assertThat(response.getError(), is("invalid_arguments")); + assertThat(response.getDetail(), is(notNullValue())); + } + // canvases.edit + { + CanvasesEditResponse response = client.canvasesEdit(r -> r + .canvasId("F123") + .changes(Collections.singletonList(CanvasDocumentChange.builder() + .documentContent(CanvasDocumentContent.builder().markdown("foo").type("invalid").build()) + .build())) + ); + assertThat(response.isOk(), is(false)); + assertThat(response.getError(), is("invalid_arguments")); + assertThat(response.getDetail(), is(notNullValue())); + } + } } From eb80cff07d4432ef8cfda270a67f185a51f1c5a2 Mon Sep 17 00:00:00 2001 From: Kirill Meshkov Date: Tue, 24 Mar 2026 13:44:22 +0400 Subject: [PATCH 4/8] Fix missing newline at end of CanvasesCreateResponse.java From 2d42d912a676bcc17a22f74630952a4c2dfbfa2c Mon Sep 17 00:00:00 2001 From: Kirill Meshkov Date: Tue, 24 Mar 2026 09:56:07 +0000 Subject: [PATCH 5/8] chore: remove trailing empty line --- .../api/methods/response/canvases/CanvasesCreateResponse.java | 2 +- .../api/methods/response/canvases/CanvasesEditResponse.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/slack-api-client/src/main/java/com/slack/api/methods/response/canvases/CanvasesCreateResponse.java b/slack-api-client/src/main/java/com/slack/api/methods/response/canvases/CanvasesCreateResponse.java index 4976f4caf..e09583b1d 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/response/canvases/CanvasesCreateResponse.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/response/canvases/CanvasesCreateResponse.java @@ -22,4 +22,4 @@ public class CanvasesCreateResponse implements SlackApiTextResponse { private String detail; private ResponseMetadata responseMetadata; -} +} \ No newline at end of file diff --git a/slack-api-client/src/main/java/com/slack/api/methods/response/canvases/CanvasesEditResponse.java b/slack-api-client/src/main/java/com/slack/api/methods/response/canvases/CanvasesEditResponse.java index 188c26b6c..e8e9ec03e 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/response/canvases/CanvasesEditResponse.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/response/canvases/CanvasesEditResponse.java @@ -20,4 +20,4 @@ public class CanvasesEditResponse implements SlackApiTextResponse { private String detail; private ResponseMetadata responseMetadata; -} +} \ No newline at end of file From 43ea7047ebb1ed2b4501acc1f0c4e8625cc57a9d Mon Sep 17 00:00:00 2001 From: Kirill Meshkov Date: Wed, 25 Mar 2026 06:51:25 +0000 Subject: [PATCH 6/8] revert import changes --- .../test/java/test_with_remote_apis/methods/canvases_Test.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/slack-api-client/src/test/java/test_with_remote_apis/methods/canvases_Test.java b/slack-api-client/src/test/java/test_with_remote_apis/methods/canvases_Test.java index 5d32dd66c..315f21cec 100644 --- a/slack-api-client/src/test/java/test_with_remote_apis/methods/canvases_Test.java +++ b/slack-api-client/src/test/java/test_with_remote_apis/methods/canvases_Test.java @@ -25,7 +25,8 @@ import java.util.Collections; import java.util.List; -import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; @Slf4j From 8fa6a8be98b226e3b1673e0895cc14056527fb41 Mon Sep 17 00:00:00 2001 From: Kirill Meshkov Date: Thu, 26 Mar 2026 06:22:10 +0000 Subject: [PATCH 7/8] add missing import --- .../test/java/test_with_remote_apis/methods/canvases_Test.java | 1 + 1 file changed, 1 insertion(+) diff --git a/slack-api-client/src/test/java/test_with_remote_apis/methods/canvases_Test.java b/slack-api-client/src/test/java/test_with_remote_apis/methods/canvases_Test.java index 315f21cec..398af83bd 100644 --- a/slack-api-client/src/test/java/test_with_remote_apis/methods/canvases_Test.java +++ b/slack-api-client/src/test/java/test_with_remote_apis/methods/canvases_Test.java @@ -26,6 +26,7 @@ import java.util.List; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; From a67b46cd8a9bd29cbcb7a4a60747b86e397f3d1a Mon Sep 17 00:00:00 2001 From: Kirill Meshkov Date: Fri, 27 Mar 2026 07:26:59 +0000 Subject: [PATCH 8/8] update test --- .../methods/canvases_Test.java | 91 +++++++++++-------- 1 file changed, 51 insertions(+), 40 deletions(-) diff --git a/slack-api-client/src/test/java/test_with_remote_apis/methods/canvases_Test.java b/slack-api-client/src/test/java/test_with_remote_apis/methods/canvases_Test.java index 398af83bd..4cb5f9561 100644 --- a/slack-api-client/src/test/java/test_with_remote_apis/methods/canvases_Test.java +++ b/slack-api-client/src/test/java/test_with_remote_apis/methods/canvases_Test.java @@ -21,10 +21,10 @@ import org.junit.BeforeClass; import org.junit.Test; -import java.util.Arrays; -import java.util.Collections; import java.util.List; +import static java.util.Collections.singletonList; +import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; @@ -52,11 +52,7 @@ public static void tearDown() throws InterruptedException { public void channel_canvases() throws Exception { MethodsClient client = slack.methods(botToken); - ConversationsCreateResponse newChannel = client.conversationsCreate(r -> r.name("test-" + System.currentTimeMillis())); - assertThat(newChannel.getError(), is(nullValue())); - String channelId = newChannel.getChannel().getId(); - - Thread.sleep(500L); // To avoid occasional 500 errors + String channelId = createChannel(client); ConversationsCanvasesCreateResponse creation = client.conversationsCanvasesCreate(r -> r .channelId(channelId) @@ -72,9 +68,7 @@ public void channel_canvases() throws Exception { ); assertThat(creation.getError(), is(nullValue())); - String canvasId = creation.getCanvasId(); - List userIds = Arrays.asList(client.authTest(r -> r).getUserId()); - FilesInfoResponse details = verifyCanvasOps(client, canvasId, channelId, userIds); + FilesInfoResponse details = verifyCanvasOps(client, creation.getCanvasId()); ChatPostMessageResponse message = client.chatPostMessage(r -> r .channel(channelId) .text("Here you are: " + details.getFile().getPermalink()) @@ -101,8 +95,8 @@ public void standalone_canvases() throws Exception { String canvasId = creation.getCanvasId(); try { - List userIds = Arrays.asList(client.authTest(r -> r).getUserId()); - verifyCanvasOps(client, canvasId, null, userIds); + List userIds = singletonList(client.authTest(r -> r).getUserId()); + verifyCanvasOps(client, canvasId); CanvasesAccessSetResponse set = client.canvasesAccessSet(r -> r .canvasId(canvasId) @@ -122,11 +116,11 @@ public void standalone_canvases() throws Exception { } } - FilesInfoResponse verifyCanvasOps(MethodsClient client, String canvasId, String channelId, List userIds) throws Exception { + FilesInfoResponse verifyCanvasOps(MethodsClient client, String canvasId) throws Exception { CanvasesSectionsLookupResponse lookupResult = client.canvasesSectionsLookup(r -> r .canvasId(canvasId) .criteria(CanvasesSectionsLookupRequest.Criteria.builder() - .sectionTypes(Arrays.asList(CanvasDocumentSectionType.H2)) + .sectionTypes(singletonList(CanvasDocumentSectionType.H2)) .containsText("Before") .build() ) @@ -136,7 +130,7 @@ FilesInfoResponse verifyCanvasOps(MethodsClient client, String canvasId, String String sectionId = lookupResult.getSections().get(0).getId(); CanvasesEditResponse edit = client.canvasesEdit(r -> r .canvasId(canvasId) - .changes(Arrays.asList(CanvasDocumentChange.builder() + .changes(singletonList(CanvasDocumentChange.builder() .sectionId(sectionId) .operation(CanvasEditOperation.REPLACE) .documentContent(CanvasDocumentContent.builder().markdown("## After").build()) @@ -166,30 +160,47 @@ public void standalone_canvases_error() throws Exception { @Test public void error_detail() throws Exception { MethodsClient client = slack.methods(botToken); - // canvases.create - { - CanvasesCreateResponse response = client.canvasesCreate(r -> r - .title("test") - .documentContent(CanvasDocumentContent.builder() - .markdown("test") - .type("invalid") - .build()) - ); - assertThat(response.isOk(), is(false)); - assertThat(response.getError(), is("invalid_arguments")); - assertThat(response.getDetail(), is(notNullValue())); - } - // canvases.edit - { - CanvasesEditResponse response = client.canvasesEdit(r -> r - .canvasId("F123") - .changes(Collections.singletonList(CanvasDocumentChange.builder() - .documentContent(CanvasDocumentContent.builder().markdown("foo").type("invalid").build()) - .build())) - ); - assertThat(response.isOk(), is(false)); - assertThat(response.getError(), is("invalid_arguments")); - assertThat(response.getDetail(), is(notNullValue())); - } + + String channelId = createChannel(client); + + String invalidCanvasContent = "1. Text\n * Nested"; // mixing of ordered and unordered lists is not supported + ConversationsCanvasesCreateResponse failedCreation = client.conversationsCanvasesCreate(r -> r + .channelId(channelId) + .documentContent(CanvasDocumentContent.builder() + .markdown(invalidCanvasContent) + .build()) + ); + assertThat(failedCreation.isOk(), is(false)); + assertThat(failedCreation.getError(), is("canvas_creation_failed")); + assertThat(failedCreation.getDetail(), containsString("Unsupported list type")); + + ConversationsCanvasesCreateResponse successfulCreation = client.conversationsCanvasesCreate(r -> r + .channelId(channelId) + .documentContent(CanvasDocumentContent.builder() + .markdown("Correct MD") + .build() + ) + ); + assertThat(successfulCreation.getCanvasId(), is(notNullValue())); + + CanvasesEditResponse editFailingResponse = client.canvasesEdit(r -> r + .canvasId(successfulCreation.getCanvasId()) + .changes(singletonList(CanvasDocumentChange.builder() + .operation(CanvasEditOperation.REPLACE) + .documentContent(CanvasDocumentContent.builder().markdown(invalidCanvasContent).build()) + .build())) + ); + assertThat(editFailingResponse.isOk(), is(false)); + assertThat(editFailingResponse.getError(), is("canvas_editing_failed")); + assertThat(editFailingResponse.getDetail(), containsString("Unsupported list type")); + } + + private static String createChannel(MethodsClient client) throws Exception { + ConversationsCreateResponse newChannel = client.conversationsCreate(r -> r.name("test-" + System.currentTimeMillis())); + assertThat(newChannel.getError(), is(nullValue())); + String channelId = newChannel.getChannel().getId(); + + Thread.sleep(500L); // To avoid occasional 500 errors + return channelId; } }