From 11b5a6d8f023accaadbafe02544f49aa76361401 Mon Sep 17 00:00:00 2001 From: RamiNoodle733 Date: Thu, 5 Feb 2026 22:20:40 -0600 Subject: [PATCH] fix: Truncate large table samples in Teams alerts to prevent payload size issues (#2044) When sending alerts to Microsoft Teams with large test result samples (>25 rows with multiple columns), the payload exceeds Teams' limits and gets rejected. This fix adds: - New maximum_rows_in_alert_samples_table config parameter (default: 25) - Table truncation logic that limits rows and adds a message about omitted rows - CLI option --maximum-rows-in-alert-samples-table (-mr) to customize the limit Fixes #2044 --- elementary/config/config.py | 7 ++++++ .../monitor/alerts/alert_messages/builder.py | 24 ++++++++++++++++--- elementary/monitor/cli.py | 9 +++++++ .../alerts/data_monitoring_alerts.py | 3 ++- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/elementary/config/config.py b/elementary/config/config.py index b8e56143c..9eed42abf 100644 --- a/elementary/config/config.py +++ b/elementary/config/config.py @@ -72,6 +72,7 @@ def __init__( report_url: Optional[str] = None, teams_webhook: Optional[str] = None, maximum_columns_in_alert_samples: Optional[int] = None, + maximum_rows_in_alert_samples_table: Optional[int] = None, env: str = DEFAULT_ENV, run_dbt_deps_if_needed: Optional[bool] = None, project_name: Optional[str] = None, @@ -112,6 +113,12 @@ def __init__( 4, ) + self.maximum_rows_in_alert_samples_table = self._first_not_none( + maximum_rows_in_alert_samples_table, + config.get("maximum_rows_in_alert_samples_table"), + 25, + ) + self.timezone = self._first_not_none( timezone, config.get("timezone"), diff --git a/elementary/monitor/alerts/alert_messages/builder.py b/elementary/monitor/alerts/alert_messages/builder.py index 593b9ff38..29fc5ecb1 100644 --- a/elementary/monitor/alerts/alert_messages/builder.py +++ b/elementary/monitor/alerts/alert_messages/builder.py @@ -63,6 +63,7 @@ class MessageBuilderConfig(BaseModel): alert_groups_subscribers: bool = False maximum_columns_in_alert_samples: int = 4 + maximum_rows_in_alert_samples_table: int = 25 class AlertMessageBuilder: @@ -394,9 +395,26 @@ def _get_result_blocks( and len(result_sample[0].keys()) <= self.config.maximum_columns_in_alert_samples ): - result_blocks.append( - TableBlock.from_dicts(result_sample), - ) + # Truncate table if too many rows to prevent Teams payload size issues + if len(result_sample) > self.config.maximum_rows_in_alert_samples_table: + truncated_sample = result_sample[:self.config.maximum_rows_in_alert_samples_table] + omitted_rows_count = len(result_sample) - self.config.maximum_rows_in_alert_samples_table + result_blocks.append( + TableBlock.from_dicts(truncated_sample), + ) + result_blocks.append( + LinesBlock( + lines=[ + ItalicTextLineBlock( + text=f"... and {omitted_rows_count} more rows (truncated to prevent payload size issues)" + ), + ] + ) + ) + else: + result_blocks.append( + TableBlock.from_dicts(result_sample), + ) else: result_blocks.append( JsonCodeBlock(content=result_sample), diff --git a/elementary/monitor/cli.py b/elementary/monitor/cli.py index 9b47133ff..a4ee0280d 100644 --- a/elementary/monitor/cli.py +++ b/elementary/monitor/cli.py @@ -298,6 +298,13 @@ def get_cli_properties() -> dict: default=4, help="Maximum number of columns to display as a table in alert samples. Above this, the output is shown as raw JSON.", ) +@click.option( + "--maximum-rows-in-alert-samples-table", + "-mr", + type=int, + default=25, + help="Maximum number of rows to display in a table in alert samples. Above this, the table will be truncated to prevent Teams payload size issues.", +) @click.pass_context def monitor( ctx, @@ -330,6 +337,7 @@ def monitor( excludes, teams_webhook, maximum_columns_in_alert_samples, + maximum_rows_in_alert_samples_table, quiet_logs, ): """ @@ -364,6 +372,7 @@ def monitor( report_url=report_url, teams_webhook=teams_webhook, maximum_columns_in_alert_samples=maximum_columns_in_alert_samples, + maximum_rows_in_alert_samples_table=maximum_rows_in_alert_samples_table, quiet_logs=quiet_logs, ) anonymous_tracking = AnonymousCommandLineTracking(config) diff --git a/elementary/monitor/data_monitoring/alerts/data_monitoring_alerts.py b/elementary/monitor/data_monitoring/alerts/data_monitoring_alerts.py index 8b24e3be2..30d1d1a6a 100644 --- a/elementary/monitor/data_monitoring/alerts/data_monitoring_alerts.py +++ b/elementary/monitor/data_monitoring/alerts/data_monitoring_alerts.py @@ -129,7 +129,8 @@ def __init__( self.alerts_integration = self._get_integration_client() self.alert_message_builder = AlertMessageBuilder( MessageBuilderConfig( - maximum_columns_in_alert_samples=self.config.maximum_columns_in_alert_samples + maximum_columns_in_alert_samples=self.config.maximum_columns_in_alert_samples, + maximum_rows_in_alert_samples_table=self.config.maximum_rows_in_alert_samples_table, ) )