From c2ef2b624f9fb9038dd9d97dc5f09776297aea2d Mon Sep 17 00:00:00 2001
From: "codegen-sh[bot]" <131295404+codegen-sh[bot]@users.noreply.github.com>
Date: Thu, 24 Apr 2025 15:23:58 +0000
Subject: [PATCH 1/2] CG-17442: Add safe_add_reaction utility to handle
 already_reacted Slack API errors

---
 src/codegen/extensions/slack/utils/README.md  | 37 +++++++++
 .../extensions/slack/utils/__init__.py        | 49 ++++++++++++
 .../extensions/slack/utils/__init__.py        |  0
 .../extensions/slack/utils/test_utils.py      | 78 +++++++++++++++++++
 4 files changed, 164 insertions(+)
 create mode 100644 src/codegen/extensions/slack/utils/README.md
 create mode 100644 src/codegen/extensions/slack/utils/__init__.py
 create mode 100644 tests/unit/codegen/extensions/slack/utils/__init__.py
 create mode 100644 tests/unit/codegen/extensions/slack/utils/test_utils.py

diff --git a/src/codegen/extensions/slack/utils/README.md b/src/codegen/extensions/slack/utils/README.md
new file mode 100644
index 000000000..9a8399812
--- /dev/null
+++ b/src/codegen/extensions/slack/utils/README.md
@@ -0,0 +1,37 @@
+# Slack Utilities
+
+This module provides utility functions for working with the Slack API in a safe and reliable way.
+
+## Features
+
+### `safe_add_reaction`
+
+A utility function that safely adds a reaction to a Slack message, handling the case where the reaction already exists.
+
+This function is particularly useful for preventing the `SlackApiError` with the error message `already_reacted` that can occur when trying to add a reaction that already exists on a message.
+
+### Usage
+
+```python
+from slack_sdk import WebClient
+from codegen.extensions.slack.utils import safe_add_reaction
+
+client = WebClient(token="your-token")
+
+# Safely add a reaction
+response = safe_add_reaction(
+    client=client,
+    channel="C12345",
+    timestamp="1234567890.123456",
+    name="thumbsup"
+)
+
+# The function will not raise an exception if the reaction already exists
+```
+
+## Error Handling
+
+The `safe_add_reaction` function handles the following error cases:
+
+- If the reaction already exists (`already_reacted` error), it logs the information and returns a success response.
+- For all other errors, it logs the error and re-raises the exception for proper handling upstream.
diff --git a/src/codegen/extensions/slack/utils/__init__.py b/src/codegen/extensions/slack/utils/__init__.py
new file mode 100644
index 000000000..76ab8fd15
--- /dev/null
+++ b/src/codegen/extensions/slack/utils/__init__.py
@@ -0,0 +1,49 @@
+"""Utility functions for Slack integration."""
+
+import logging
+from typing import Any, Dict, Optional
+
+from slack_sdk import WebClient
+from slack_sdk.errors import SlackApiError
+
+from codegen.shared.logging.get_logger import get_logger
+
+logger = get_logger(__name__)
+logger.setLevel(logging.INFO)
+
+
+def safe_add_reaction(
+    client: WebClient,
+    channel: str,
+    timestamp: str,
+    name: str,
+) -> Dict[str, Any]:
+    """
+    Safely add a reaction to a Slack message, handling the case where the reaction already exists.
+    
+    Args:
+        client: The Slack WebClient instance
+        channel: The channel ID where the message is located
+        timestamp: The timestamp of the message
+        name: The name of the reaction emoji to add
+        
+    Returns:
+        The response from the Slack API or an error response
+    """
+    try:
+        return client.reactions_add(
+            channel=channel,
+            timestamp=timestamp,
+            name=name,
+        )
+    except SlackApiError as e:
+        # If the error is "already_reacted", just log it and continue
+        if e.response["error"] == "already_reacted":
+            logger.info(
+                f"Reaction '{name}' already exists on message in channel {channel} at {timestamp}. Ignoring."
+            )
+            # Return a success response to prevent further error handling
+            return {"ok": True, "already_exists": True}
+        # For other errors, re-raise
+        logger.error(f"Error adding reaction: {e}")
+        raise
diff --git a/tests/unit/codegen/extensions/slack/utils/__init__.py b/tests/unit/codegen/extensions/slack/utils/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/unit/codegen/extensions/slack/utils/test_utils.py b/tests/unit/codegen/extensions/slack/utils/test_utils.py
new file mode 100644
index 000000000..ac6bcebdc
--- /dev/null
+++ b/tests/unit/codegen/extensions/slack/utils/test_utils.py
@@ -0,0 +1,78 @@
+"""Tests for Slack utility functions."""
+
+import pytest
+from unittest.mock import MagicMock, patch
+
+from slack_sdk import WebClient
+from slack_sdk.errors import SlackApiError
+
+from codegen.extensions.slack.utils import safe_add_reaction
+
+
+def test_safe_add_reaction_success():
+    """Test that safe_add_reaction works correctly when the API call succeeds."""
+    mock_client = MagicMock(spec=WebClient)
+    mock_client.reactions_add.return_value = {"ok": True}
+    
+    result = safe_add_reaction(
+        client=mock_client,
+        channel="C12345",
+        timestamp="1234567890.123456",
+        name="thumbsup"
+    )
+    
+    assert result == {"ok": True}
+    mock_client.reactions_add.assert_called_once_with(
+        channel="C12345",
+        timestamp="1234567890.123456",
+        name="thumbsup"
+    )
+
+
+def test_safe_add_reaction_already_reacted():
+    """Test that safe_add_reaction handles the 'already_reacted' error correctly."""
+    mock_client = MagicMock(spec=WebClient)
+    
+    # Create a mock SlackApiError with the 'already_reacted' error
+    mock_response = {"ok": False, "error": "already_reacted"}
+    mock_error = SlackApiError(message="already_reacted", response=mock_response)
+    mock_client.reactions_add.side_effect = mock_error
+    
+    result = safe_add_reaction(
+        client=mock_client,
+        channel="C12345",
+        timestamp="1234567890.123456",
+        name="thumbsup"
+    )
+    
+    # Should return a success response with already_exists=True
+    assert result == {"ok": True, "already_exists": True}
+    mock_client.reactions_add.assert_called_once_with(
+        channel="C12345",
+        timestamp="1234567890.123456",
+        name="thumbsup"
+    )
+
+
+def test_safe_add_reaction_other_error():
+    """Test that safe_add_reaction re-raises other errors."""
+    mock_client = MagicMock(spec=WebClient)
+    
+    # Create a mock SlackApiError with a different error
+    mock_response = {"ok": False, "error": "invalid_auth"}
+    mock_error = SlackApiError(message="invalid_auth", response=mock_response)
+    mock_client.reactions_add.side_effect = mock_error
+    
+    with pytest.raises(SlackApiError):
+        safe_add_reaction(
+            client=mock_client,
+            channel="C12345",
+            timestamp="1234567890.123456",
+            name="thumbsup"
+        )
+    
+    mock_client.reactions_add.assert_called_once_with(
+        channel="C12345",
+        timestamp="1234567890.123456",
+        name="thumbsup"
+    )

From 59662be9e2110fd03e82aed7c55f63a9e814caf6 Mon Sep 17 00:00:00 2001
From: "codegen-sh[bot]" <131295404+codegen-sh[bot]@users.noreply.github.com>
Date: Thu, 24 Apr 2025 15:26:19 +0000
Subject: [PATCH 2/2] Automated pre-commit update

---
 src/codegen/extensions/slack/utils/README.md  |  7 +--
 .../extensions/slack/utils/__init__.py        | 15 ++---
 .../extensions/slack/utils/test_utils.py      | 59 +++++--------------
 3 files changed, 23 insertions(+), 58 deletions(-)

diff --git a/src/codegen/extensions/slack/utils/README.md b/src/codegen/extensions/slack/utils/README.md
index 9a8399812..f404809cf 100644
--- a/src/codegen/extensions/slack/utils/README.md
+++ b/src/codegen/extensions/slack/utils/README.md
@@ -19,12 +19,7 @@ from codegen.extensions.slack.utils import safe_add_reaction
 client = WebClient(token="your-token")
 
 # Safely add a reaction
-response = safe_add_reaction(
-    client=client,
-    channel="C12345",
-    timestamp="1234567890.123456",
-    name="thumbsup"
-)
+response = safe_add_reaction(client=client, channel="C12345", timestamp="1234567890.123456", name="thumbsup")
 
 # The function will not raise an exception if the reaction already exists
 ```
diff --git a/src/codegen/extensions/slack/utils/__init__.py b/src/codegen/extensions/slack/utils/__init__.py
index 76ab8fd15..f6d3db86c 100644
--- a/src/codegen/extensions/slack/utils/__init__.py
+++ b/src/codegen/extensions/slack/utils/__init__.py
@@ -17,16 +17,15 @@ def safe_add_reaction(
     channel: str,
     timestamp: str,
     name: str,
-) -> Dict[str, Any]:
-    """
-    Safely add a reaction to a Slack message, handling the case where the reaction already exists.
-    
+) -> dict[str, Any]:
+    """Safely add a reaction to a Slack message, handling the case where the reaction already exists.
+
     Args:
         client: The Slack WebClient instance
         channel: The channel ID where the message is located
         timestamp: The timestamp of the message
         name: The name of the reaction emoji to add
-        
+
     Returns:
         The response from the Slack API or an error response
     """
@@ -39,11 +38,9 @@ def safe_add_reaction(
     except SlackApiError as e:
         # If the error is "already_reacted", just log it and continue
         if e.response["error"] == "already_reacted":
-            logger.info(
-                f"Reaction '{name}' already exists on message in channel {channel} at {timestamp}. Ignoring."
-            )
+            logger.info(f"Reaction '{name}' already exists on message in channel {channel} at {timestamp}. Ignoring.")
             # Return a success response to prevent further error handling
             return {"ok": True, "already_exists": True}
         # For other errors, re-raise
-        logger.error(f"Error adding reaction: {e}")
+        logger.exception(f"Error adding reaction: {e}")
         raise
diff --git a/tests/unit/codegen/extensions/slack/utils/test_utils.py b/tests/unit/codegen/extensions/slack/utils/test_utils.py
index ac6bcebdc..8aa877904 100644
--- a/tests/unit/codegen/extensions/slack/utils/test_utils.py
+++ b/tests/unit/codegen/extensions/slack/utils/test_utils.py
@@ -1,8 +1,8 @@
 """Tests for Slack utility functions."""
 
-import pytest
-from unittest.mock import MagicMock, patch
+from unittest.mock import MagicMock
 
+import pytest
 from slack_sdk import WebClient
 from slack_sdk.errors import SlackApiError
 
@@ -13,66 +13,39 @@ def test_safe_add_reaction_success():
     """Test that safe_add_reaction works correctly when the API call succeeds."""
     mock_client = MagicMock(spec=WebClient)
     mock_client.reactions_add.return_value = {"ok": True}
-    
-    result = safe_add_reaction(
-        client=mock_client,
-        channel="C12345",
-        timestamp="1234567890.123456",
-        name="thumbsup"
-    )
-    
+
+    result = safe_add_reaction(client=mock_client, channel="C12345", timestamp="1234567890.123456", name="thumbsup")
+
     assert result == {"ok": True}
-    mock_client.reactions_add.assert_called_once_with(
-        channel="C12345",
-        timestamp="1234567890.123456",
-        name="thumbsup"
-    )
+    mock_client.reactions_add.assert_called_once_with(channel="C12345", timestamp="1234567890.123456", name="thumbsup")
 
 
 def test_safe_add_reaction_already_reacted():
     """Test that safe_add_reaction handles the 'already_reacted' error correctly."""
     mock_client = MagicMock(spec=WebClient)
-    
+
     # Create a mock SlackApiError with the 'already_reacted' error
     mock_response = {"ok": False, "error": "already_reacted"}
     mock_error = SlackApiError(message="already_reacted", response=mock_response)
     mock_client.reactions_add.side_effect = mock_error
-    
-    result = safe_add_reaction(
-        client=mock_client,
-        channel="C12345",
-        timestamp="1234567890.123456",
-        name="thumbsup"
-    )
-    
+
+    result = safe_add_reaction(client=mock_client, channel="C12345", timestamp="1234567890.123456", name="thumbsup")
+
     # Should return a success response with already_exists=True
     assert result == {"ok": True, "already_exists": True}
-    mock_client.reactions_add.assert_called_once_with(
-        channel="C12345",
-        timestamp="1234567890.123456",
-        name="thumbsup"
-    )
+    mock_client.reactions_add.assert_called_once_with(channel="C12345", timestamp="1234567890.123456", name="thumbsup")
 
 
 def test_safe_add_reaction_other_error():
     """Test that safe_add_reaction re-raises other errors."""
     mock_client = MagicMock(spec=WebClient)
-    
+
     # Create a mock SlackApiError with a different error
     mock_response = {"ok": False, "error": "invalid_auth"}
     mock_error = SlackApiError(message="invalid_auth", response=mock_response)
     mock_client.reactions_add.side_effect = mock_error
-    
+
     with pytest.raises(SlackApiError):
-        safe_add_reaction(
-            client=mock_client,
-            channel="C12345",
-            timestamp="1234567890.123456",
-            name="thumbsup"
-        )
-    
-    mock_client.reactions_add.assert_called_once_with(
-        channel="C12345",
-        timestamp="1234567890.123456",
-        name="thumbsup"
-    )
+        safe_add_reaction(client=mock_client, channel="C12345", timestamp="1234567890.123456", name="thumbsup")
+
+    mock_client.reactions_add.assert_called_once_with(channel="C12345", timestamp="1234567890.123456", name="thumbsup")