Skip to content

Use native Slack Table Block for test results sample rendering#2227

Open
quickcoffee wants to merge 4 commits intoelementary-data:masterfrom
quickcoffee:2225-add-slack-table-block
Open

Use native Slack Table Block for test results sample rendering#2227
quickcoffee wants to merge 4 commits intoelementary-data:masterfrom
quickcoffee:2225-add-slack-table-block

Conversation

@quickcoffee
Copy link
Copy Markdown
Contributor

@quickcoffee quickcoffee commented May 7, 2026

Summary

  • Replaces the tabulate-based ASCII table-in-code-block rendering with Slack's native Table Block ("type": "table") in BlockKitBuilder._add_table_block()
  • Header row uses bold rich_text cells; data rows use raw_text cells
  • Falls back to a JSON code block for tables with >20 columns (Slack limit) or if a second table would appear in the same message (Slack allows one table per message)
  • Removes the now-unused tabulate import and cell-truncation helpers from BlockKitBuilder
  • Regenerates all 19 affected block_kit test fixtures

Screenshot on how this looks like in Slack (manually rendered via a script):
image

Closes #2225

Test plan

  • pytest tests/unit/messages/formats/block_kit/ passes with regenerated fixtures
  • Sent a full realistic alert message to a Slack channel and visually verified the table renders correctly (screenshot above)
  • Verified fallback to JSON code block works for the second-table-in-message edge case

Summary by CodeRabbit

  • New Features

    • Tables now render using Slack's native table block format with bold headers, left-aligned columns, and non-wrapping cells for improved readability.
    • Empty or null cell values are shown as blank cells.
  • Improvements / Bug Fixes

    • Table size caps enforced; when limits or multiple tables would occur, content falls back to a JSON/code-block representation to preserve output.
  • Tests

    • Added a test ensuring None/empty cells produce non-empty table cell text.

Replaces the tabulate-based ASCII table in code blocks with Slack's
native Table Block (type: "table"), giving column-aligned, scannable
tables directly in alert messages.

- Bold rich_text header row, raw_text data cells
- Falls back to JSON code block for >20 columns (Slack limit) or if
  a second table would appear in the same message
- Removes unused tabulate import and cell-truncation helpers from BlockKitBuilder
- Regenerates all block_kit test fixtures to match the new format

Closes elementary-data#2225
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

👋 @quickcoffee
Thank you for raising your pull request.
Please make sure to add tests and document all user-facing changes.
You can do this by editing the docs files in this pull request.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4ce4b21c-8499-437b-8d97-3b79a28594d8

📥 Commits

Reviewing files that changed from the base of the PR and between 18402cf and 2ace13d.

📒 Files selected for processing (4)
  • 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
  • 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
  • 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
  • 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
 ______________________________________________________________________________________________________________________
< Design with contracts. Use contracts to document and verify that code does no more and no less than it claims to do. >
 ----------------------------------------------------------------------------------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
📝 Walkthrough

Walkthrough

The PR replaces markdown/code-fence table rendering with native Slack Table Blocks, adds constants to cap columns (20) and rows (100), introduces header/data cell helpers, rewrites table-emission logic with a JSON fallback when limits or prior-table constraints apply, resets table state per build, and updates fixtures plus a unit test to the new structured table format.

Changes

Slack Table Block Integration

Layer / File(s) Summary
Import Changes and Table Limits
elementary/messages/formats/block_kit.py
Remove tabulate import. Add _MAX_SLACK_TABLE_COLUMNS = 20, _MAX_SLACK_TABLE_ROWS = 100, and _has_table_block initialization in BlockKitBuilder.__init__() to track table emission.
Remove Old Truncation Helper
elementary/messages/formats/block_kit.py
Delete previous per-column truncation helper used for tabulate-based string table rendering.
Cell Generation Helpers
elementary/messages/formats/block_kit.py
Add _make_header_cell(text) producing rich_text bold header cells and _make_data_cell(value) producing raw_text data cells (converts None to a safe empty value).
Table Block Rendering Logic
elementary/messages/formats/block_kit.py
Rewrite _add_table_block() to enforce 20-column/100-row limits, fall back to a JSON code-block when caps are exceeded or a prior table was emitted, otherwise construct a Slack table block with header/data cells and column_settings. Set _has_table_block = True after successful table insertion.
Build State Initialization
elementary/messages/formats/block_kit.py
build() resets _has_table_block to False before processing message blocks to ensure per-build tracking.
Test Fixtures
tests/unit/messages/formats/block_kit/fixtures/all_blocks_*.json, table_block_*_*.json
Replace markdown section table payloads with structured table blocks: rows arrays using rich_text header cells (bold "Column N") and raw_text data cells, plus column_settings (align: left, is_wrapped: false).
Unit Test
tests/unit/messages/formats/block_kit/test_block_kit.py
Import TableBlock and add a test ensuring None and empty-string cells in a table produce non-empty data cell text in the formatted Slack table block.

🎯 3 (Moderate) | ⏱️ ~25 minutes

"I nibbled at code, then shaped every row,
Bold headers and cells now merrily show. 🐰
From fenced text to blocks neat and bright,
Twenty by hundred keep tables polite.
Fixtures sing, and tests clap in delight."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Use native Slack Table Block for test results sample rendering' clearly and concisely summarizes the main change: replacing code-block table rendering with Slack's native Table Block for test results.
Linked Issues check ✅ Passed The PR fully implements the core requirements from #2225: uses Slack Table Block for test results, maintains fallback to JSON code blocks for Slack limits (20 columns, single table per message), leverages rich_text for headers enabling future link support.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the Table Block feature: BlockKitBuilder enhancements for table rendering, fixture updates reflecting new table structure, and test coverage for edge cases like None and empty cells.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@quickcoffee quickcoffee requested a deployment to elementary_test_env May 7, 2026 10:01 — with GitHub Actions Waiting
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 55d2ac0fa2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread elementary/messages/formats/block_kit.py Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/unit/messages/formats/block_kit/fixtures/table_block_200_2.json (1)

93-102: ⚡ Quick win

"is_wrapped": false hardcoded on all columns may silently clip 200-char cell content

All fixtures set "is_wrapped": false on every column. For the 200-character cells in this fixture that is the entire cell content budget, meaning Slack will not wrap it — the row stays one-line and text is clipped or requires horizontal scroll. The column_settings property controls text alignment and wrapping behaviour for table columns, so enabling is_wrapped: true for columns that are expected to receive long free-text content would allow the full value to be visible.

If this is intentional (keeping rows compact, relying on upstream truncation), at minimum the fixture name table_block_200_2 implies cells up to 200 characters are expected — that's a scenario where wrapping is needed to avoid loss of information.

💡 Suggested `column_settings` for variable-length content columns
       "column_settings": [
         {
           "align": "left",
-          "is_wrapped": false
+          "is_wrapped": true
         },
         {
           "align": "left",
-          "is_wrapped": false
+          "is_wrapped": true
         }
       ]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/messages/formats/block_kit/fixtures/table_block_200_2.json` around
lines 93 - 102, The fixture table_block_200_2.json sets "is_wrapped": false on
every entry of the "column_settings" array which will force 200-character cell
content to be clipped or require horizontal scrolling; update the
"column_settings" entries for columns expected to hold long/free-form text to
use "is_wrapped": true (leave short/static columns as false if desired) so Slack
will wrap long cell content and preserve visibility — modify the
"column_settings" objects in this fixture accordingly.
elementary/messages/formats/block_kit.py (1)

283-285: ⚡ Quick win

Silent row truncation gives users no indication that data was dropped.

When block.rows exceeds _MAX_SLACK_TABLE_ROWS - 1 (99 rows), the slice on line 284 silently discards the remainder. Unlike the column-overflow and second-table cases — which fall back to a visible JSON code block — this case produces a structurally valid table that is simply incomplete, with no truncation notice.

Consider appending an informational row (e.g. "… N more rows") or logging a warning so operators know data was dropped.

♻️ Proposed fix
-        rows: List[List[dict]] = [[self._make_header_cell(h) for h in block.headers]]
-        for row in block.rows[: self._MAX_SLACK_TABLE_ROWS - 1]:
-            rows.append([self._make_data_cell(v) for v in row])
+        max_data_rows = self._MAX_SLACK_TABLE_ROWS - 1  # one slot taken by header
+        data_rows = block.rows[:max_data_rows]
+        omitted = len(block.rows) - len(data_rows)
+
+        rows: List[List[dict]] = [[self._make_header_cell(h) for h in block.headers]]
+        for row in data_rows:
+            rows.append([self._make_data_cell(v) for v in row])
+        if omitted:
+            notice = f"… {omitted} more row(s) not shown"
+            rows.append([self._make_data_cell(notice)] + [self._make_data_cell("") for _ in block.headers[1:]])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@elementary/messages/formats/block_kit.py` around lines 283 - 285, The slice
on block.rows silently drops rows when len(block.rows) >
self._MAX_SLACK_TABLE_ROWS - 1; update the rows-building logic (where rows,
_make_header_cell, _make_data_cell and block.rows are used) to detect omitted =
len(block.rows) - (self._MAX_SLACK_TABLE_ROWS - 1) and, if omitted > 0, append a
visible informational row indicating the number omitted (e.g. "… N more rows")
so users see truncation; implement the info-row using _make_data_cell for the
first column and matching empty cells for the rest (to keep column structure),
and also emit a debug/warning log mentioning the truncation for operators.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@elementary/messages/formats/block_kit.py`:
- Around line 270-281: The fallback JSON-dump path in _add_table_block silently
truncates rows because zip(block.headers, row) isn't strict; update the dict
construction in _add_table_block to either use zip(block.headers, row,
strict=True) so a ValueError is raised on length mismatch, or explicitly
validate that len(row) == len(block.headers) before building dicts and handle
mismatches (e.g., log the problem and either pad/truncate deterministically or
raise) so malformed rows cannot produce corrupted dict output; keep references
to TableBlock, _MAX_SLACK_TABLE_COLUMNS, _has_table_block, _add_block, and
_format_markdown_section when locating the change.

---

Nitpick comments:
In `@elementary/messages/formats/block_kit.py`:
- Around line 283-285: The slice on block.rows silently drops rows when
len(block.rows) > self._MAX_SLACK_TABLE_ROWS - 1; update the rows-building logic
(where rows, _make_header_cell, _make_data_cell and block.rows are used) to
detect omitted = len(block.rows) - (self._MAX_SLACK_TABLE_ROWS - 1) and, if
omitted > 0, append a visible informational row indicating the number omitted
(e.g. "… N more rows") so users see truncation; implement the info-row using
_make_data_cell for the first column and matching empty cells for the rest (to
keep column structure), and also emit a debug/warning log mentioning the
truncation for operators.

In `@tests/unit/messages/formats/block_kit/fixtures/table_block_200_2.json`:
- Around line 93-102: The fixture table_block_200_2.json sets "is_wrapped":
false on every entry of the "column_settings" array which will force
200-character cell content to be clipped or require horizontal scrolling; update
the "column_settings" entries for columns expected to hold long/free-form text
to use "is_wrapped": true (leave short/static columns as false if desired) so
Slack will wrap long cell content and preserve visibility — modify the
"column_settings" objects in this fixture accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a2e2b5c8-dc8b-430c-b7b4-bbb6cfb2c7f5

📥 Commits

Reviewing files that changed from the base of the PR and between 9b843d1 and 55d2ac0.

📒 Files selected for processing (20)
  • elementary/messages/formats/block_kit.py
  • tests/unit/messages/formats/block_kit/fixtures/all_blocks_green.json
  • tests/unit/messages/formats/block_kit/fixtures/all_blocks_no_color.json
  • tests/unit/messages/formats/block_kit/fixtures/all_blocks_red.json
  • tests/unit/messages/formats/block_kit/fixtures/all_blocks_yellow.json
  • tests/unit/messages/formats/block_kit/fixtures/table_block_200_1.json
  • tests/unit/messages/formats/block_kit/fixtures/table_block_200_2.json
  • tests/unit/messages/formats/block_kit/fixtures/table_block_200_3.json
  • tests/unit/messages/formats/block_kit/fixtures/table_block_200_4.json
  • tests/unit/messages/formats/block_kit/fixtures/table_block_200_5.json
  • tests/unit/messages/formats/block_kit/fixtures/table_block_30_1.json
  • tests/unit/messages/formats/block_kit/fixtures/table_block_30_2.json
  • tests/unit/messages/formats/block_kit/fixtures/table_block_30_3.json
  • tests/unit/messages/formats/block_kit/fixtures/table_block_30_4.json
  • tests/unit/messages/formats/block_kit/fixtures/table_block_30_5.json
  • tests/unit/messages/formats/block_kit/fixtures/table_block_8_1.json
  • tests/unit/messages/formats/block_kit/fixtures/table_block_8_2.json
  • tests/unit/messages/formats/block_kit/fixtures/table_block_8_3.json
  • tests/unit/messages/formats/block_kit/fixtures/table_block_8_4.json
  • tests/unit/messages/formats/block_kit/fixtures/table_block_8_5.json

Comment thread elementary/messages/formats/block_kit.py
@ofek1weiss
Copy link
Copy Markdown
Contributor

hey @quickcoffee, looks really awesome 🚀
just make sure to take a look at this comment
#2227 (comment)

Slack rejects raw_text cells with empty text. Convert None to "NULL"
(represents database NULL values) and guard against any other value
that stringifies to an empty string with a single-space fallback.

Adds a dedicated test to assert no raw_text cell ever has empty text.
@quickcoffee quickcoffee requested a deployment to elementary_test_env May 7, 2026 11:38 — with GitHub Actions Waiting
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unit/messages/formats/block_kit/test_block_kit.py`:
- Around line 42-45: Run Black to reformat the test file containing the
TableBlock.from_dicts call and commit the changes so CI pre-commit passes;
specifically, open the test that constructs TableBlock.from_dicts([{"col_a":
None, "col_b": "value"}, {"col_a": "", "col_b": "other"}]) and apply Black (or
your project's pre-commit hooks) to produce the normalized formatting, then add
and commit the formatted file.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 63836521-efa1-415f-bec1-96557216851c

📥 Commits

Reviewing files that changed from the base of the PR and between 55d2ac0 and 2abbdb3.

📒 Files selected for processing (2)
  • elementary/messages/formats/block_kit.py
  • tests/unit/messages/formats/block_kit/test_block_kit.py

Comment thread tests/unit/messages/formats/block_kit/test_block_kit.py Outdated
@quickcoffee quickcoffee deployed to elementary_test_env May 7, 2026 11:55 — with GitHub Actions Active
@quickcoffee quickcoffee requested a review from ofek1weiss May 7, 2026 12:00
@ofek1weiss
Copy link
Copy Markdown
Contributor

@quickcoffee some of the tests are not passing, seems like not all of the test fixture were updated

@quickcoffee quickcoffee requested a deployment to elementary_test_env May 7, 2026 14:06 — with GitHub Actions Waiting
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use Slack Table Block to display test results in Slack alerts

2 participants