Skip to content

Commit 142f748

Browse files
committed
separate block kit steps into slack_sdk/models
1 parent e6760c0 commit 142f748

2 files changed

Lines changed: 74 additions & 69 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -283,75 +283,7 @@ Defined in `.github/workflows/ci-build.yml`, runs on push to `main`, all PRs, an
283283

284284
### Adding a New Block Kit Type
285285

286-
Block Kit models live in `slack_sdk/models/blocks/` across three files:
287-
288-
| File | Contents |
289-
| --- | --- |
290-
| `blocks.py` | Layout blocks (`SectionBlock`, `ActionsBlock`, `HeaderBlock`, etc.) |
291-
| `block_elements.py` | Interactive elements (`ButtonElement`, `StaticSelectElement`, `DatePickerElement`, etc.) |
292-
| `basic_components.py` | Composition objects (`TextObject`, `Option`, `ConfirmObject`, etc.) |
293-
294-
All types are exported from `slack_sdk/models/blocks/__init__.py`.
295-
296-
#### Base class hierarchy
297-
298-
```
299-
JsonObject
300-
├── Block → layout blocks
301-
├── BlockElement → non-interactive elements
302-
│ └── InteractiveElement → elements with action_id
303-
│ └── InputInteractiveElement → elements usable inside InputBlock
304-
├── TextObject → PlainTextObject, MarkdownTextObject
305-
├── Option / OptionGroup
306-
└── ConfirmObject, etc.
307-
```
308-
309-
Choose the base class that matches the type you're adding.
310-
311-
#### Steps
312-
313-
1. **Define the class** in the appropriate file. Follow this pattern:
314-
315-
```python
316-
class MyNewBlock(Block):
317-
type = "my_new_block"
318-
319-
@property
320-
def attributes(self) -> Set[str]:
321-
return super().attributes.union({"text", "optional_field"})
322-
323-
def __init__(self, *, text: Union[str, dict, TextObject], optional_field: Optional[str] = None, block_id: Optional[str] = None, **others: dict):
324-
super().__init__(type=self.type, block_id=block_id)
325-
show_unknown_key_warning(self, others)
326-
self.text = TextObject.parse(text, default_type=PlainTextObject.type)
327-
self.optional_field = optional_field
328-
329-
@JsonValidator("text must be provided")
330-
def _validate_text(self):
331-
return self.text is not None
332-
```
333-
334-
Key conventions:
335-
- Set `type` class attribute to the Slack API type string
336-
- Override `attributes` to return the set of JSON field names for serialization
337-
- Call `super().__init__()` with `type=self.type`
338-
- Call `show_unknown_key_warning(self, others)` to log unexpected kwargs
339-
- Use `TextObject.parse()`, `ConfirmObject.parse()`, and `BlockElement.parse()` for nested composition objects
340-
- Add `@JsonValidator` decorators for constraints (required fields, max lengths, enum values, date formats)
341-
- Use `@EnumValidator` for fields restricted to a set of values
342-
343-
2. **Register for deserialization:**
344-
- **Elements:** Automatic — `BlockElement.parse()` discovers subclasses at runtime via `__subclasses__()`. No manual step needed.
345-
- **Blocks:** Manual — add an `elif` clause in `Block.parse()` (in `blocks.py`) mapping the type string to the new class.
346-
347-
3. **Export the class** — add it to the imports and `__all__` list in `slack_sdk/models/blocks/__init__.py`.
348-
349-
4. **Add tests** in `tests/slack_sdk/models/test_blocks.py`. Cover:
350-
- Round-trip: `input_dict == MyNewBlock(**input_dict).to_dict()`
351-
- Validation: required fields raise `SlackObjectFormationError` when missing
352-
- Constraints: max lengths, enum values, format patterns
353-
354-
5. **Validate:** `./scripts/run_validation.sh`
286+
See [`slack_sdk/models/AGENTS.md`](slack_sdk/models/AGENTS.md) for detailed steps on defining, registering, exporting, and testing new Block Kit blocks, elements, and composition objects.
355287

356288
### Fixing a Bug
357289

slack_sdk/models/AGENTS.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# AGENTS.md — Block Kit Models
2+
3+
## Adding a New Block Kit Type
4+
5+
Block Kit models live in `slack_sdk/models/blocks/` across three files:
6+
7+
| File | Contents |
8+
| --- | --- |
9+
| `blocks.py` | Layout blocks (`SectionBlock`, `ActionsBlock`, `HeaderBlock`, etc.) |
10+
| `block_elements.py` | Interactive elements (`ButtonElement`, `StaticSelectElement`, `DatePickerElement`, etc.) |
11+
| `basic_components.py` | Composition objects (`TextObject`, `Option`, `ConfirmObject`, etc.) |
12+
13+
All types are exported from `slack_sdk/models/blocks/__init__.py`.
14+
15+
### Base class hierarchy
16+
17+
```
18+
JsonObject
19+
├── Block → layout blocks
20+
├── BlockElement → non-interactive elements
21+
│ └── InteractiveElement → elements with action_id
22+
│ └── InputInteractiveElement → elements usable inside InputBlock
23+
├── TextObject → PlainTextObject, MarkdownTextObject
24+
├── Option / OptionGroup
25+
└── ConfirmObject, etc.
26+
```
27+
28+
Choose the base class that matches the type you're adding.
29+
30+
### Steps
31+
32+
1. **Define the class** in the appropriate file. Follow this pattern:
33+
34+
```python
35+
class MyNewBlock(Block):
36+
type = "my_new_block"
37+
38+
@property
39+
def attributes(self) -> Set[str]:
40+
return super().attributes.union({"text", "optional_field"})
41+
42+
def __init__(self, *, text: Union[str, dict, TextObject], optional_field: Optional[str] = None, block_id: Optional[str] = None, **others: dict):
43+
super().__init__(type=self.type, block_id=block_id)
44+
show_unknown_key_warning(self, others)
45+
self.text = TextObject.parse(text, default_type=PlainTextObject.type)
46+
self.optional_field = optional_field
47+
48+
@JsonValidator("text must be provided")
49+
def _validate_text(self):
50+
return self.text is not None
51+
```
52+
53+
Key conventions:
54+
- Set `type` class attribute to the Slack API type string
55+
- Override `attributes` to return the set of JSON field names for serialization
56+
- Call `super().__init__()` with `type=self.type`
57+
- Call `show_unknown_key_warning(self, others)` to log unexpected kwargs
58+
- Use `TextObject.parse()`, `ConfirmObject.parse()`, and `BlockElement.parse()` for nested composition objects
59+
- Add `@JsonValidator` decorators for constraints (required fields, max lengths, enum values, date formats)
60+
- Use `@EnumValidator` for fields restricted to a set of values
61+
62+
2. **Register for deserialization:**
63+
- **Elements:** Automatic — `BlockElement.parse()` discovers subclasses at runtime via `__subclasses__()`. No manual step needed.
64+
- **Blocks:** Manual — add an `elif` clause in `Block.parse()` (in `blocks.py`) mapping the type string to the new class.
65+
66+
3. **Export the class** — add it to the imports and `__all__` list in `slack_sdk/models/blocks/__init__.py`.
67+
68+
4. **Add tests** in `tests/slack_sdk/models/test_blocks.py`. Cover:
69+
- Round-trip: `input_dict == MyNewBlock(**input_dict).to_dict()`
70+
- Validation: required fields raise `SlackObjectFormationError` when missing
71+
- Constraints: max lengths, enum values, format patterns
72+
73+
5. **Validate:** `./scripts/run_validation.sh`

0 commit comments

Comments
 (0)