SIENTIAPDE-1184

fix: enhance error reporting in Formatters class

- Updated the parse_report_schedule method to return a dictionary for error keys, including both message and attachment.
- Improved error report formatting in send_error_report method to include attachments for better context in failure notifications.
This commit is contained in:
vitor-aignosi
2025-08-14 08:50:21 -03:00
parent 4d75d581f8
commit e8ce3e75d5
4 changed files with 109 additions and 40 deletions

View File

@@ -336,12 +336,15 @@ class Formatters(BaseActivity):
attachment_content=json.dumps(attachment, indent=4, sort_keys=True) attachment_content=json.dumps(attachment, indent=4, sort_keys=True)
) )
def parse_report_schedule(self, input_data: dict[str, Any]) -> tuple[list[str], list[str]]: def parse_report_schedule(self, input_data: dict[str, Any]) -> tuple[list[str], dict[str, Any]]:
success_keys = [f"{value['namespace']}/{value['schedule_name']}" success_keys = [f"{value['namespace']}/{value['schedule_name']}"
for value in input_data if value['success']] for value in input_data if value['success']]
error_keys = [f"{value['namespace']}/{value['schedule_name']}: {value['message']}" error_keys = {f"{value['namespace']}/{value['schedule_name']}": {
for value in input_data if not value['success']] 'message': value['message'],
'attachment': value.get('attachment', None)
}
for value in input_data if not value['success']}
return success_keys, error_keys return success_keys, error_keys
@@ -384,15 +387,24 @@ class Formatters(BaseActivity):
self.send_success_report( self.send_success_report(
metadata=metadata, metadata=metadata,
message=f"Created schedules: \n {', '.join(success_keys)}", message=f"Created schedules: \n {', '.join(success_keys)}",
notification_id="REPORT_ORCHESTRATION_CREATED_SCHEDULES" notification_id="REPORT_ORCHESTRATION_CREATED_SCHEDULES",
attachment=created_schedules
) )
if len(error_keys) > 0: if len(error_keys) > 0:
attachment = []
for value in error_keys.values():
if value['attachment'] is not None:
attachment.append(
f"{value['message']}\n{value['attachment']}")
else:
attachment.append(value['message'])
self.send_error_report( self.send_error_report(
metadata=metadata, metadata=metadata,
message=f"Failed to create schedules: \n {', '.join(error_keys)}", message=f"Failed to create schedules: \n {', '.join(error_keys)}",
notification_id="REPORT_ORCHESTRATION_CREATED_SCHEDULES_ERROR", notification_id="REPORT_ORCHESTRATION_CREATED_SCHEDULES_ERROR",
attachment=created_schedules attachment="\n ========== \n".join(attachment)
) )
# Send report for updated schedules # Send report for updated schedules
@@ -404,15 +416,24 @@ class Formatters(BaseActivity):
self.send_success_report( self.send_success_report(
metadata=metadata, metadata=metadata,
message=f"Updated schedules: \n {', '.join(success_keys)}", message=f"Updated schedules: \n {', '.join(success_keys)}",
notification_id="REPORT_ORCHESTRATION_UPDATED_SCHEDULES" notification_id="REPORT_ORCHESTRATION_UPDATED_SCHEDULES",
attachment=updated_schedules
) )
if len(error_keys) > 0: if len(error_keys) > 0:
attachment = []
for value in error_keys.values():
if value['attachment'] is not None:
attachment.append(
f"{value['message']}\n{value['attachment']}")
else:
attachment.append(value['message'])
self.send_error_report( self.send_error_report(
metadata=metadata, metadata=metadata,
message=f"Failed to update schedules: \n {', '.join(error_keys)}", message=f"Failed to update schedules: \n {', '.join(error_keys)}",
notification_id="REPORT_ORCHESTRATION_UPDATED_SCHEDULES_ERROR", notification_id="REPORT_ORCHESTRATION_UPDATED_SCHEDULES_ERROR",
attachment=updated_schedules attachment="\n ========== \n".join(attachment)
) )
if len(deleted_schedules) > 0: if len(deleted_schedules) > 0:
@@ -423,15 +444,24 @@ class Formatters(BaseActivity):
self.send_success_report( self.send_success_report(
metadata=metadata, metadata=metadata,
message=f"Deleted schedules: \n {', '.join(success_keys)}", message=f"Deleted schedules: \n {', '.join(success_keys)}",
notification_id="REPORT_ORCHESTRATION_DELETED_SCHEDULES" notification_id="REPORT_ORCHESTRATION_DELETED_SCHEDULES",
attachment=deleted_schedules
) )
if len(error_keys) > 0: if len(error_keys) > 0:
attachment = []
for value in error_keys.values():
if value['attachment'] is not None:
attachment.append(
f"{value['message']}\n{value['attachment']}")
else:
attachment.append(value['message'])
self.send_error_report( self.send_error_report(
metadata=metadata, metadata=metadata,
message=f"Failed to delete schedules: \n {', '.join(error_keys)}", message=f"Failed to delete schedules: \n {', '.join(error_keys)}",
notification_id="REPORT_ORCHESTRATION_DELETED_SCHEDULES_ERROR", notification_id="REPORT_ORCHESTRATION_DELETED_SCHEDULES_ERROR",
attachment=deleted_schedules attachment="\n ========== \n".join(attachment)
) )
@activity.defn(name="report_slot_orchestration") @activity.defn(name="report_slot_orchestration")

View File

@@ -527,14 +527,18 @@ def test_parse_report_schedule(formatters):
"namespace": "test_namespace", "namespace": "test_namespace",
"schedule_name": "test_schedule_name_to_create_error", "schedule_name": "test_schedule_name_to_create_error",
"success": False, "success": False,
"message": "test_error" "message": "test_error",
"attachment": "test_attachment"
} }
] ]
result = formatters.parse_report_schedule(input_data) result = formatters.parse_report_schedule(input_data)
assert result == ( assert result == (
["test_namespace/test_schedule_name_to_create"], ["test_namespace/test_schedule_name_to_create"],
["test_namespace/test_schedule_name_to_create_error: test_error"] {"test_namespace/test_schedule_name_to_create_error": {
'message': 'test_error',
'attachment': 'test_attachment'
}}
) )
@@ -558,7 +562,14 @@ async def test_report_schedule_orchestration(formatters):
"namespace": "test_namespace", "namespace": "test_namespace",
"schedule_name": "test_schedule_name_to_create_error", "schedule_name": "test_schedule_name_to_create_error",
"success": False, "success": False,
"message": "test_error" "message": "test_error1",
"attachment": "test_attachment1"
},
{
"namespace": "test_namespace",
"schedule_name": "test_schedule_name_to_create_error2",
"success": False,
"message": "test_error2"
} }
], ],
"updated_schedules": [ "updated_schedules": [
@@ -571,7 +582,21 @@ async def test_report_schedule_orchestration(formatters):
"namespace": "test_namespace", "namespace": "test_namespace",
"schedule_name": "test_schedule_name_to_update_error", "schedule_name": "test_schedule_name_to_update_error",
"success": False, "success": False,
"message": "test_error" "message": "test_error2",
"attachment": "test_attachment2"
},
{
"namespace": "test_namespace",
"schedule_name": "test_schedule_name_to_update_error2",
"success": False,
"message": "test_error3",
"attachment": "test_attachment3"
},
{
"namespace": "test_namespace",
"schedule_name": "test_schedule_name_to_update_error3",
"success": False,
"message": "test_error4"
} }
], ],
"deleted_schedules": [ "deleted_schedules": [
@@ -584,7 +609,14 @@ async def test_report_schedule_orchestration(formatters):
"namespace": "test_namespace", "namespace": "test_namespace",
"schedule_name": "test_schedule_name_to_delete_error", "schedule_name": "test_schedule_name_to_delete_error",
"success": False, "success": False,
"message": "test_error" "message": "test_error4",
"attachment": "test_attachment4"
},
{
"namespace": "test_namespace",
"schedule_name": "test_schedule_name_to_delete_error2",
"success": False,
"message": "test_error5"
} }
] ]
} }
@@ -600,39 +632,42 @@ async def test_report_schedule_orchestration(formatters):
call( call(
metadata=metadata['metadata'], metadata=metadata['metadata'],
message="Created schedules: \n test_namespace/test_schedule_name_to_create", message="Created schedules: \n test_namespace/test_schedule_name_to_create",
notification_id="REPORT_ORCHESTRATION_CREATED_SCHEDULES"
),
call(
metadata=metadata['metadata'],
message="Updated schedules: \n test_namespace/test_schedule_name_to_update",
notification_id="REPORT_ORCHESTRATION_UPDATED_SCHEDULES"
),
call(
metadata=metadata['metadata'],
message="Deleted schedules: \n test_namespace/test_schedule_name_to_delete",
notification_id="REPORT_ORCHESTRATION_DELETED_SCHEDULES"
)
])
formatters.send_error_report.assert_has_calls([
call(
metadata=metadata['metadata'],
message="Failed to create schedules: \n test_namespace/test_schedule_name_to_create_error: test_error",
notification_id="REPORT_ORCHESTRATION_CREATED_SCHEDULES", notification_id="REPORT_ORCHESTRATION_CREATED_SCHEDULES",
attachment=input_data['created_schedules'] attachment=input_data['created_schedules']
), ),
call( call(
metadata=metadata['metadata'], metadata=metadata['metadata'],
message="Failed to update schedules: \n test_namespace/test_schedule_name_to_update_error: test_error", message="Updated schedules: \n test_namespace/test_schedule_name_to_update",
notification_id="REPORT_ORCHESTRATION_UPDATED_SCHEDULES", notification_id="REPORT_ORCHESTRATION_UPDATED_SCHEDULES",
attachment=input_data['updated_schedules'] attachment=input_data['updated_schedules']
), ),
call( call(
metadata=metadata['metadata'], metadata=metadata['metadata'],
message="Failed to delete schedules: \n test_namespace/test_schedule_name_to_delete_error: test_error", message="Deleted schedules: \n test_namespace/test_schedule_name_to_delete",
notification_id="REPORT_ORCHESTRATION_DELETED_SCHEDULES", notification_id="REPORT_ORCHESTRATION_DELETED_SCHEDULES",
attachment=input_data['deleted_schedules'] attachment=input_data['deleted_schedules']
) )
]) ])
formatters.send_error_report.assert_has_calls([
call(
metadata=metadata['metadata'],
message="Failed to create schedules: \n test_namespace/test_schedule_name_to_create_error, test_namespace/test_schedule_name_to_create_error2",
notification_id="REPORT_ORCHESTRATION_CREATED_SCHEDULES_ERROR",
attachment="test_error1\ntest_attachment1\n ========== \ntest_error2"
),
call(
metadata=metadata['metadata'],
message="Failed to update schedules: \n test_namespace/test_schedule_name_to_update_error, test_namespace/test_schedule_name_to_update_error2, test_namespace/test_schedule_name_to_update_error3",
notification_id="REPORT_ORCHESTRATION_UPDATED_SCHEDULES_ERROR",
attachment="test_error2\ntest_attachment2\n ========== \ntest_error3\ntest_attachment3\n ========== \ntest_error4"
),
call(
metadata=metadata['metadata'],
message="Failed to delete schedules: \n test_namespace/test_schedule_name_to_delete_error, test_namespace/test_schedule_name_to_delete_error2",
notification_id="REPORT_ORCHESTRATION_DELETED_SCHEDULES_ERROR",
attachment="test_error4\ntest_attachment4\n ========== \ntest_error5"
)
])
@mark.asyncio @mark.asyncio

View File

@@ -280,7 +280,7 @@ async def test_update_pipelines_timestamps_success(datetime_mock, mongo_db):
{"schedule_name": "test2", "namespace": "test2"} {"schedule_name": "test2", "namespace": "test2"}
]}, ]},
{"$set": { {"$set": {
"updated_at": datetime_mock.now.return_value.strftime.return_value}} "updated_at": datetime_mock.now.return_value}}
) )
@@ -325,9 +325,9 @@ async def test_create_pipelines_timestamps_success(datetime_mock, mongo_db):
mongo_db.database["pipelines"].insert_many.assert_called_once_with( mongo_db.database["pipelines"].insert_many.assert_called_once_with(
[ [
{"schedule_name": "test1", "namespace": "test1", {"schedule_name": "test1", "namespace": "test1",
"updated_at": datetime_mock.now.return_value.strftime.return_value}, "updated_at": datetime_mock.now.return_value},
{"schedule_name": "test2", "namespace": "test2", {"schedule_name": "test2", "namespace": "test2",
"updated_at": datetime_mock.now.return_value.strftime.return_value} "updated_at": datetime_mock.now.return_value}
] ]
) )

View File

@@ -213,21 +213,24 @@ async def test_create_schedule(
input_data['schedules']['scouter']['test-schedule'], input_data['schedules']['scouter']['test-schedule'],
id="test-schedule", id="test-schedule",
task_queue="test-workflow-queue", task_queue="test-workflow-queue",
execution_timeout=ANY execution_timeout=ANY,
typed_search_attributes=mock_typed_search_attributes.return_value,
), ),
call( call(
"test-workflow", "test-workflow",
input_data['schedules']['scouter']['test-schedule-invalid-frequency'], input_data['schedules']['scouter']['test-schedule-invalid-frequency'],
id="test-schedule-invalid-frequency", id="test-schedule-invalid-frequency",
task_queue="test-workflow-queue", task_queue="test-workflow-queue",
execution_timeout=ANY execution_timeout=ANY,
typed_search_attributes=mock_typed_search_attributes.return_value,
), ),
call( call(
"test-workflow", "test-workflow",
input_data['schedules']['laborious']['test-schedule-laborious'], input_data['schedules']['laborious']['test-schedule-laborious'],
id="test-schedule-laborious", id="test-schedule-laborious",
task_queue="test-workflow-queue", task_queue="test-workflow-queue",
execution_timeout=ANY execution_timeout=ANY,
typed_search_attributes=mock_typed_search_attributes.return_value,
) )
]) ])
@@ -546,7 +549,8 @@ async def test_delete_schedules(temporal_manager):
"schedule_name": "test-schedule_no_handler", "schedule_name": "test-schedule_no_handler",
"namespace": "scouter", "namespace": "scouter",
"success": False, "success": False,
"message": "Schedule test-schedule_no_handler not found" "message": "Schedule test-schedule_no_handler not found",
"attachment": ANY
}, },
{ {
"schedule_name": "test-schedule-laborious", "schedule_name": "test-schedule-laborious",