Skip to content

Commit 55d4776

Browse files
authored
Merge pull request #2227 from quickcoffee/2225-add-slack-table-block
Use native Slack Table Block for test results sample rendering
2 parents 9b843d1 + 2ace13d commit 55d4776

25 files changed

Lines changed: 2785 additions & 136 deletions

elementary/messages/formats/block_kit.py

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from typing import Any, Callable, List, Optional, Tuple
33

44
from slack_sdk.models import blocks as slack_blocks
5-
from tabulate import tabulate
65

76
from elementary.messages.blocks import (
87
ActionBlock,
@@ -50,14 +49,16 @@ class FormattedBlockKitMessage(BaseModel):
5049
class BlockKitBuilder:
5150
_SECONDARY_FACT_CHUNK_SIZE = 2
5251
_LONGEST_MARKDOWN_SUFFIX_LEN = 3 # length of markdown's code suffix (```)
53-
_MAX_CELL_LENGTH_BY_COLUMN_COUNT = {4: 11, 3: 14, 2: 22, 1: 40, 0: 40}
52+
_MAX_SLACK_TABLE_COLUMNS = 20
53+
_MAX_SLACK_TABLE_ROWS = 100
5454

5555
def __init__(
5656
self, resolve_mention: Optional[ResolveMentionCallback] = None
5757
) -> None:
5858
self._blocks: List[dict] = []
5959
self._attachment_blocks: List[dict] = []
6060
self._is_divided = False
61+
self._has_table_block = False
6162
self._resolve_mention = resolve_mention or (lambda x: None)
6263

6364
def _format_icon(self, icon: Icon) -> str:
@@ -98,13 +99,6 @@ def _format_line_block_text(self, block: LineBlock) -> str:
9899
[self._format_inline_block(inline) for inline in block.inlines]
99100
)
100101

101-
def _format_table_cell(self, cell_value: Any, column_count: int) -> str:
102-
value = str(cell_value)
103-
max_cell_length = self._MAX_CELL_LENGTH_BY_COLUMN_COUNT[column_count]
104-
if len(value) > max_cell_length:
105-
return value[: max_cell_length - 2] + ".."
106-
return value
107-
108102
def _format_markdown_section_text(self, text: str) -> dict:
109103
if len(text) > slack_blocks.SectionBlock.text_max_length:
110104
text = (
@@ -257,24 +251,50 @@ def _add_divider_block(self, block: DividerBlock) -> None:
257251
self._add_block({"type": "divider"})
258252
self._is_divided = True
259253

254+
def _make_header_cell(self, text: str) -> dict:
255+
return {
256+
"type": "rich_text",
257+
"elements": [
258+
{
259+
"type": "rich_text_section",
260+
"elements": [
261+
{"type": "text", "text": str(text), "style": {"bold": True}}
262+
],
263+
}
264+
],
265+
}
266+
267+
def _make_data_cell(self, value: Any) -> dict:
268+
text = str(value) if value is not None else "NULL"
269+
return {"type": "raw_text", "text": text or " "}
270+
260271
def _add_table_block(self, block: TableBlock) -> None:
261272
column_count = len(block.headers)
262-
if column_count not in self._MAX_CELL_LENGTH_BY_COLUMN_COUNT:
273+
274+
if column_count > self._MAX_SLACK_TABLE_COLUMNS or self._has_table_block:
263275
dicts = [
264276
{header: cell for header, cell in zip(block.headers, row)}
265277
for row in block.rows
266278
]
267-
table_text = json.dumps(dicts, indent=2)
268-
else:
269-
new_rows = [
270-
[self._format_table_cell(cell, column_count) for cell in row]
271-
for row in block.rows
272-
]
273-
new_headers = [
274-
self._format_table_cell(cell, column_count) for cell in block.headers
275-
]
276-
table_text = tabulate(new_rows, headers=new_headers, tablefmt="simple")
277-
self._add_block(self._format_markdown_section(f"```{table_text}```"))
279+
self._add_block(
280+
self._format_markdown_section(f"```{json.dumps(dicts, indent=2)}```")
281+
)
282+
return
283+
284+
rows: List[List[dict]] = [[self._make_header_cell(h) for h in block.headers]]
285+
for row in block.rows[: self._MAX_SLACK_TABLE_ROWS - 1]:
286+
rows.append([self._make_data_cell(v) for v in row])
287+
288+
self._add_block(
289+
{
290+
"type": "table",
291+
"rows": rows,
292+
"column_settings": [
293+
{"align": "left", "is_wrapped": False} for _ in block.headers
294+
],
295+
}
296+
)
297+
self._has_table_block = True
278298

279299
def _add_actions_block(self, block: ActionsBlock) -> None:
280300
self._add_block(
@@ -337,6 +357,7 @@ def _get_final_blocks(
337357
def build(self, message: MessageBody) -> FormattedBlockKitMessage:
338358
self._blocks = []
339359
self._attachment_blocks = []
360+
self._has_table_block = False
340361
self._add_message_blocks(message.blocks)
341362
color_code = COLOR_MAP.get(message.color) if message.color else None
342363
blocks, attachment_blocks = self._get_final_blocks(message.color)

tests/unit/alerts/alert_messages/fixtures/block_kit/dbt_test_alert_status-error_link-True_description-True_tags-True_owners-True_table-True_error-True_sample-True_env-True.json

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,65 @@
8989
}
9090
},
9191
{
92-
"type": "section",
93-
"text": {
94-
"type": "mrkdwn",
95-
"text": "```column1 column2\n--------- ---------\nvalue1 value2```"
96-
}
92+
"type": "table",
93+
"rows": [
94+
[
95+
{
96+
"type": "rich_text",
97+
"elements": [
98+
{
99+
"type": "rich_text_section",
100+
"elements": [
101+
{
102+
"type": "text",
103+
"text": "column1",
104+
"style": {
105+
"bold": true
106+
}
107+
}
108+
]
109+
}
110+
]
111+
},
112+
{
113+
"type": "rich_text",
114+
"elements": [
115+
{
116+
"type": "rich_text_section",
117+
"elements": [
118+
{
119+
"type": "text",
120+
"text": "column2",
121+
"style": {
122+
"bold": true
123+
}
124+
}
125+
]
126+
}
127+
]
128+
}
129+
],
130+
[
131+
{
132+
"type": "raw_text",
133+
"text": "value1"
134+
},
135+
{
136+
"type": "raw_text",
137+
"text": "value2"
138+
}
139+
]
140+
],
141+
"column_settings": [
142+
{
143+
"align": "left",
144+
"is_wrapped": false
145+
},
146+
{
147+
"align": "left",
148+
"is_wrapped": false
149+
}
150+
]
97151
}
98152
],
99153
"color": "#ff0000"

tests/unit/alerts/alert_messages/fixtures/block_kit/dbt_test_alert_status-fail_link-True_description-True_tags-True_owners-True_table-True_error-True_sample-True_env-False.json

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,65 @@
8989
}
9090
},
9191
{
92-
"type": "section",
93-
"text": {
94-
"type": "mrkdwn",
95-
"text": "```column1 column2\n--------- ---------\nvalue1 value2```"
96-
}
92+
"type": "table",
93+
"rows": [
94+
[
95+
{
96+
"type": "rich_text",
97+
"elements": [
98+
{
99+
"type": "rich_text_section",
100+
"elements": [
101+
{
102+
"type": "text",
103+
"text": "column1",
104+
"style": {
105+
"bold": true
106+
}
107+
}
108+
]
109+
}
110+
]
111+
},
112+
{
113+
"type": "rich_text",
114+
"elements": [
115+
{
116+
"type": "rich_text_section",
117+
"elements": [
118+
{
119+
"type": "text",
120+
"text": "column2",
121+
"style": {
122+
"bold": true
123+
}
124+
}
125+
]
126+
}
127+
]
128+
}
129+
],
130+
[
131+
{
132+
"type": "raw_text",
133+
"text": "value1"
134+
},
135+
{
136+
"type": "raw_text",
137+
"text": "value2"
138+
}
139+
]
140+
],
141+
"column_settings": [
142+
{
143+
"align": "left",
144+
"is_wrapped": false
145+
},
146+
{
147+
"align": "left",
148+
"is_wrapped": false
149+
}
150+
]
97151
}
98152
],
99153
"color": "#ff0000"

tests/unit/alerts/alert_messages/fixtures/block_kit/dbt_test_alert_status-warn_link-True_description-False_tags-True_owners-False_table-True_error-False_sample-True_env-False.json

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,65 @@
6868
}
6969
},
7070
{
71-
"type": "section",
72-
"text": {
73-
"type": "mrkdwn",
74-
"text": "```column1 column2\n--------- ---------\nvalue1 value2```"
75-
}
71+
"type": "table",
72+
"rows": [
73+
[
74+
{
75+
"type": "rich_text",
76+
"elements": [
77+
{
78+
"type": "rich_text_section",
79+
"elements": [
80+
{
81+
"type": "text",
82+
"text": "column1",
83+
"style": {
84+
"bold": true
85+
}
86+
}
87+
]
88+
}
89+
]
90+
},
91+
{
92+
"type": "rich_text",
93+
"elements": [
94+
{
95+
"type": "rich_text_section",
96+
"elements": [
97+
{
98+
"type": "text",
99+
"text": "column2",
100+
"style": {
101+
"bold": true
102+
}
103+
}
104+
]
105+
}
106+
]
107+
}
108+
],
109+
[
110+
{
111+
"type": "raw_text",
112+
"text": "value1"
113+
},
114+
{
115+
"type": "raw_text",
116+
"text": "value2"
117+
}
118+
]
119+
],
120+
"column_settings": [
121+
{
122+
"align": "left",
123+
"is_wrapped": false
124+
},
125+
{
126+
"align": "left",
127+
"is_wrapped": false
128+
}
129+
]
76130
}
77131
],
78132
"color": "#ffcc00"

tests/unit/alerts/alert_messages/fixtures/block_kit/dbt_test_alert_status-warn_link-True_description-True_tags-True_owners-True_table-True_error-True_sample-True_env-True.json

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,65 @@
8989
}
9090
},
9191
{
92-
"type": "section",
93-
"text": {
94-
"type": "mrkdwn",
95-
"text": "```column1 column2\n--------- ---------\nvalue1 value2```"
96-
}
92+
"type": "table",
93+
"rows": [
94+
[
95+
{
96+
"type": "rich_text",
97+
"elements": [
98+
{
99+
"type": "rich_text_section",
100+
"elements": [
101+
{
102+
"type": "text",
103+
"text": "column1",
104+
"style": {
105+
"bold": true
106+
}
107+
}
108+
]
109+
}
110+
]
111+
},
112+
{
113+
"type": "rich_text",
114+
"elements": [
115+
{
116+
"type": "rich_text_section",
117+
"elements": [
118+
{
119+
"type": "text",
120+
"text": "column2",
121+
"style": {
122+
"bold": true
123+
}
124+
}
125+
]
126+
}
127+
]
128+
}
129+
],
130+
[
131+
{
132+
"type": "raw_text",
133+
"text": "value1"
134+
},
135+
{
136+
"type": "raw_text",
137+
"text": "value2"
138+
}
139+
]
140+
],
141+
"column_settings": [
142+
{
143+
"align": "left",
144+
"is_wrapped": false
145+
},
146+
{
147+
"align": "left",
148+
"is_wrapped": false
149+
}
150+
]
97151
}
98152
],
99153
"color": "#ffcc00"

0 commit comments

Comments
 (0)