Skip to content

Commit 76675f3

Browse files
authored
Fix #1235 Add file input block element support (#1236)
1 parent 98b6a77 commit 76675f3

File tree

10 files changed

+135
-0
lines changed

10 files changed

+135
-0
lines changed

bolt-socket-mode/src/test/java/samples/SimpleApp.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ public static void main(String[] args) throws Exception {
7272
.element(plainTextInput(pti -> pti.actionId("agenda-action").multiline(true)))
7373
.label(plainText(pt -> pt.text("Detailed Agenda").emoji(true)))
7474
),
75+
// Note that this block element requires files:read scope
76+
input(input -> input
77+
.blockId("files-block")
78+
.element(fileInput(fi -> fi.actionId("files-action")))
79+
.label(plainText(pt -> pt.text("Attached files").emoji(true)))
80+
),
7581
input(input -> input
7682
.blockId("email-block")
7783
.element(emailTextInput(pti -> pti.actionId("email-action")))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.slack.api.model.kotlin_extension.block.element
2+
3+
import com.slack.api.model.block.element.FileInputElement
4+
import com.slack.api.model.kotlin_extension.block.BlockLayoutBuilder
5+
import com.slack.api.model.kotlin_extension.block.Builder
6+
7+
@BlockLayoutBuilder
8+
class FileInputElementBuilder : Builder<FileInputElement> {
9+
private var actionId: String? = null
10+
private var filetypes: List<String>? = null
11+
private var maxFiles: Int? = null
12+
13+
fun actionId(id: String) {
14+
actionId = id
15+
}
16+
17+
fun filetypes(filetypes: List<String>) {
18+
this.filetypes = filetypes
19+
}
20+
fun filetypes(vararg filetypes: String) {
21+
this.filetypes = filetypes.toList()
22+
}
23+
24+
fun maxFiles(maxFiles: Int) {
25+
this.maxFiles = maxFiles
26+
}
27+
28+
override fun build(): FileInputElement {
29+
return FileInputElement.builder()
30+
.actionId(actionId)
31+
.filetypes(filetypes)
32+
.maxFiles(maxFiles)
33+
.build()
34+
}
35+
}

slack-api-model-kotlin-extension/src/main/kotlin/com/slack/api/model/kotlin_extension/block/element/container/MultiBlockElementContainer.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ class MultiBlockElementContainer : BlockElementDsl {
109109
underlying += NumberInputElementBuilder().apply(builder).build()
110110
}
111111

112+
override fun fileInput(builder: FileInputElementBuilder.() -> Unit) {
113+
underlying += FileInputElementBuilder().apply(builder).build()
114+
}
115+
112116
override fun radioButtons(builder: RadioButtonsElementBuilder.() -> Unit) {
113117
underlying += RadioButtonsElementBuilder().apply(builder).build()
114118
}

slack-api-model-kotlin-extension/src/main/kotlin/com/slack/api/model/kotlin_extension/block/element/container/SingleBlockElementContainer.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ class SingleBlockElementContainer() : BlockElementDsl {
109109
underlying = NumberInputElementBuilder().apply(builder).build()
110110
}
111111

112+
override fun fileInput(builder: FileInputElementBuilder.() -> Unit) {
113+
underlying = FileInputElementBuilder().apply(builder).build()
114+
}
115+
112116
override fun radioButtons(builder: RadioButtonsElementBuilder.() -> Unit) {
113117
underlying = RadioButtonsElementBuilder().apply(builder).build()
114118
}

slack-api-model-kotlin-extension/src/main/kotlin/com/slack/api/model/kotlin_extension/block/element/dsl/BlockElementInputDsl.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.slack.api.model.kotlin_extension.block.element.dsl
22

3+
import com.slack.api.model.block.element.FileInputElement
34
import com.slack.api.model.kotlin_extension.block.BlockLayoutBuilder
45
import com.slack.api.model.kotlin_extension.block.element.*
56

@@ -120,6 +121,12 @@ interface BlockElementInputDsl {
120121
*/
121122
fun numberInput(builder: NumberInputElementBuilder.() -> Unit)
122123

124+
/**
125+
* @see <a href="https://api.slack.com/surfaces/modals/using#preparing_for_modals">Preparing your app for modals guide</a>
126+
* @see <a href="https://api.slack.com/reference/block-kit/block-elements#file_input">Documentation</a>
127+
*/
128+
fun fileInput(builder: FileInputElementBuilder.() -> Unit)
129+
123130
/**
124131
* This is the simplest form of select menu, with a static list of options passed in when defining the element.
125132
*

slack-api-model-kotlin-extension/src/test/kotlin/test_locally/block/InputBlockTest.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,38 @@ import kotlin.test.assertEquals
88

99
class InputBlockTest {
1010

11+
@Test
12+
fun fileInput() {
13+
val gson = GsonFactory.createSnakeCase()
14+
val blocks = withBlocks {
15+
input {
16+
blockId("b-1")
17+
element {
18+
fileInput {
19+
actionId("a-1")
20+
maxFiles(2)
21+
filetypes("jpg", "png")
22+
}
23+
}
24+
label("Files 1")
25+
}
26+
input {
27+
blockId("b-2")
28+
element {
29+
fileInput {
30+
actionId("a-2")
31+
maxFiles(3)
32+
filetypes(listOf("jpg", "png"))
33+
}
34+
}
35+
label("Files 2")
36+
}
37+
}
38+
val result = gson.toJson(blocks)
39+
val expected = """[{"type":"input","block_id":"b-1","label":{"type":"plain_text","text":"Files 1"},"element":{"type":"file_input","action_id":"a-1","filetypes":["jpg","png"],"max_files":2},"optional":false},{"type":"input","block_id":"b-2","label":{"type":"plain_text","text":"Files 2"},"element":{"type":"file_input","action_id":"a-2","filetypes":["jpg","png"],"max_files":3},"optional":false}]"""
40+
assertEquals(expected, result)
41+
}
42+
1143
@Test
1244
fun dispatchActions() {
1345
val gson = GsonFactory.createSnakeCase()

slack-api-model/src/main/java/com/slack/api/model/block/element/BlockElements.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ public static NumberInputElement numberInput(ModelConfigurator<NumberInputElemen
7171
return configurator.configure(NumberInputElement.builder()).build();
7272
}
7373

74+
public static FileInputElement fileInput(ModelConfigurator<FileInputElement.FileInputElementBuilder> configurator) {
75+
return configurator.configure(FileInputElement.builder()).build();
76+
}
77+
7478
// DatePickerElement
7579

7680
public static DatePickerElement datePicker(ModelConfigurator<DatePickerElement.DatePickerElementBuilder> 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.element;
2+
3+
import lombok.*;
4+
5+
import java.util.List;
6+
7+
/**
8+
* https://api.slack.com/reference/block-kit/block-elements#file_input
9+
*/
10+
@Data
11+
@Builder
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
@EqualsAndHashCode(callSuper = false)
15+
public class FileInputElement extends BlockElement {
16+
public static final String TYPE = "file_input";
17+
private final String type = TYPE;
18+
19+
/**
20+
* An identifier for the input value when the parent modal is submitted.
21+
* You can use this when you receive a view_submission payload to identify the value of the input element.
22+
* Should be unique among all other action_ids in the containing block. Maximum length is 255 characters.
23+
*/
24+
private String actionId;
25+
26+
/**
27+
* An array of valid file extensions that will be accepted for this element.
28+
* All file extensions will be accepted if filetypes is not specified.
29+
* This validation is provided for convenience only,
30+
* and you should perform your own file type validation based on what you expect to receive.
31+
*/
32+
private List<String> filetypes;
33+
34+
/**
35+
* Maximum number of files that can be uploaded for this file_input element.
36+
* Minimum of 1, maximum of 10. Defaults to 10 if not specified.
37+
*/
38+
private Integer maxFiles;
39+
}

slack-api-model/src/main/java/com/slack/api/model/view/ViewState.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.slack.api.model.view;
22

3+
import com.slack.api.model.File;
34
import com.slack.api.model.block.RichTextBlock;
45
import com.slack.api.model.block.composition.PlainTextObject;
56
import lombok.AllArgsConstructor;
@@ -37,6 +38,7 @@ public static class Value {
3738
private List<SelectedOption> selectedOptions;
3839
private String timezone; // for timepicker
3940
private RichTextBlock richTextValue; // "rich_text_input" type
41+
private List<File> files; // "file_input" type
4042
}
4143

4244
@Data

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ private Class<? extends BlockElement> getContextBlockElementClassInstance(String
8484
return EmailTextInputElement.class;
8585
case NumberInputElement.TYPE:
8686
return NumberInputElement.class;
87+
case FileInputElement.TYPE:
88+
return FileInputElement.class;
8789
case RichTextSectionElement.TYPE:
8890
return RichTextSectionElement.class;
8991
case RichTextListElement.TYPE:

0 commit comments

Comments
 (0)