From b235e64acff425a79ffbcb9206f6b9b28e70477b Mon Sep 17 00:00:00 2001
From: utkarsh sharma <utkarsharma2@gmail.com>
Date: Mon, 15 May 2023 17:18:29 +0530
Subject: [PATCH 1/2] Add send_event method in PagerdutyEventsHook

send_event() methods is suppose to replace PagerdutyEventsHook's create_event() method. Within send_event() we are using PagerdutySDK's send_event() instead of post() which is specific to our requirment. Also added deprecation warning in send_event() method
---
 .../pagerduty/hooks/pagerduty_events.py       | 83 ++++++++++++++++++-
 .../pagerduty/hooks/test_pagerduty_events.py  | 32 +++++++
 2 files changed, 114 insertions(+), 1 deletion(-)

diff --git a/airflow/providers/pagerduty/hooks/pagerduty_events.py b/airflow/providers/pagerduty/hooks/pagerduty_events.py
index c86790bbd75c0..3c351a4557e5e 100644
--- a/airflow/providers/pagerduty/hooks/pagerduty_events.py
+++ b/airflow/providers/pagerduty/hooks/pagerduty_events.py
@@ -19,11 +19,12 @@
 from __future__ import annotations
 
 from datetime import datetime
+import warnings
 from typing import Any
 
 import pdpyras
 
-from airflow.exceptions import AirflowException
+from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
 from airflow.hooks.base import BaseHook
 
 
@@ -115,6 +116,13 @@ def create_event(
             link's text.
         :return: PagerDuty Events API v2 response.
         """
+        warnings.warn(
+            "This method will be deprecated. Please use the "
+            "`PagerdutyEventsHook.send_event` to interact with the Events API",
+            AirflowProviderDeprecationWarning,
+            stacklevel=1,
+        )
+
         payload = {
             "summary": summary,
             "severity": severity,
@@ -153,6 +161,79 @@ def create_event(
         resp.raise_for_status()
         return resp.json()
 
+    def send_event(
+        self,
+        summary: str,
+        severity: str,
+        source: str = "airflow",
+        action: str = "trigger",
+        dedup_key: str | None = None,
+        custom_details: Any | None = None,
+        group: str | None = None,
+        component: str | None = None,
+        class_type: str | None = None,
+        images: list[Any] | None = None,
+        links: list[Any] | None = None,
+    ) -> dict:
+        """
+        Create event for service integration.
+
+        :param summary: Summary for the event
+        :param severity: Severity for the event, needs to be one of: info, warning, error, critical
+        :param source: Specific human-readable unique identifier, such as a
+            hostname, for the system having the problem.
+        :param action: Event action, needs to be one of: trigger, acknowledge,
+            resolve. Default to trigger if not specified.
+        :param dedup_key: A string which identifies the alert triggered for the given event.
+            Required for the actions acknowledge and resolve.
+        :param custom_details: Free-form details from the event. Can be a dictionary or a string.
+            If a dictionary is passed it will show up in PagerDuty as a table.
+        :param group: A cluster or grouping of sources. For example, sources
+            "prod-datapipe-02" and "prod-datapipe-03" might both be part of "prod-datapipe"
+        :param component: The part or component of the affected system that is broken.
+        :param class_type: The class/type of the event.
+        :param images: List of images to include. Each dictionary in the list accepts the following keys:
+            `src`: The source (URL) of the image being attached to the incident. This image must be served via
+            HTTPS.
+            `href`: [Optional] URL to make the image a clickable link.
+            `alt`: [Optional] Alternative text for the image.
+        :return: PagerDuty Events API v2 response.
+        """
+        payload = {
+            "summary": summary,
+            "severity": severity,
+            "source": source,
+        }
+        if custom_details is not None:
+            payload["custom_details"] = custom_details
+        if component:
+            payload["component"] = component
+        if group:
+            payload["group"] = group
+        if class_type:
+            payload["class"] = class_type
+
+        actions = ("trigger", "acknowledge", "resolve")
+        if action not in actions:
+            raise ValueError(f"Event action must be one of: {', '.join(actions)}")
+        data = {
+            "action": action,
+            "payload": payload,
+        }
+        if dedup_key:
+            data["dedup_key"] = dedup_key
+        elif action != "trigger":
+            raise ValueError(
+                f"The dedup_key property is required for action={action} events, and it must be a string."
+            )
+        if images is not None:
+            data["images"] = images
+        if links is not None:
+            data["links"] = links
+
+        session = pdpyras.EventsAPISession(self.integration_key)
+        return session.send_event(**data)
+
     def create_change_event(
         self,
         summary: str,
diff --git a/tests/providers/pagerduty/hooks/test_pagerduty_events.py b/tests/providers/pagerduty/hooks/test_pagerduty_events.py
index 7ba86d25dbc0d..41e1920813a5c 100644
--- a/tests/providers/pagerduty/hooks/test_pagerduty_events.py
+++ b/tests/providers/pagerduty/hooks/test_pagerduty_events.py
@@ -19,6 +19,7 @@
 
 import pytest
 
+from airflow.exceptions import AirflowProviderDeprecationWarning
 from airflow.models import Connection
 from airflow.providers.pagerduty.hooks.pagerduty import PagerdutyEventsHook
 from airflow.utils import db
@@ -62,3 +63,34 @@ def test_create_change_event(self, requests_mock, events_connections):
         requests_mock.post("https://events.pagerduty.com/v2/change/enqueue", json=mock_response_body)
         resp = hook.create_change_event(summary="test", source="airflow")
         assert resp == change_event_id
+
+    def test_send_event(self, requests_mock, events_connections):
+        hook = PagerdutyEventsHook(pagerduty_events_conn_id=DEFAULT_CONN_ID)
+        dedup_key = "samplekeyhere"
+        mock_response_body = {
+            "status": "success",
+            "message": "Event processed",
+            "dedup_key": dedup_key,
+        }
+        requests_mock.post("https://events.pagerduty.com/v2/enqueue", json=mock_response_body)
+        resp = hook.send_event(summary="test", source="airflow_test", severity="error", dedup_key=dedup_key)
+        assert resp == dedup_key
+
+    def test_create_event_deprecation_warning(self, requests_mock, events_connections):
+        hook = PagerdutyEventsHook(pagerduty_events_conn_id=DEFAULT_CONN_ID)
+        mock_response_body = {
+            "status": "success",
+            "message": "Event processed",
+            "dedup_key": "samplekeyhere",
+        }
+        requests_mock.post("https://events.pagerduty.com/v2/enqueue", json=mock_response_body)
+        warning = (
+            "This method will be deprecated. Please use the `PagerdutyEventsHook.send_event`"
+            " to interact with the Events API"
+        )
+        with pytest.warns(AirflowProviderDeprecationWarning, match=warning):
+            hook.create_event(
+                summary="test",
+                source="airflow_test",
+                severity="error",
+            )

From d6d1a73dc5351ab19f019b1abfc81aeff18babf1 Mon Sep 17 00:00:00 2001
From: utkarsh sharma <utkarsharma2@gmail.com>
Date: Sat, 20 May 2023 09:11:15 +0530
Subject: [PATCH 2/2] Code refactoring

---
 .../pagerduty/hooks/pagerduty_events.py       | 93 +++++++++++--------
 1 file changed, 56 insertions(+), 37 deletions(-)

diff --git a/airflow/providers/pagerduty/hooks/pagerduty_events.py b/airflow/providers/pagerduty/hooks/pagerduty_events.py
index 3c351a4557e5e..81607e4c937e0 100644
--- a/airflow/providers/pagerduty/hooks/pagerduty_events.py
+++ b/airflow/providers/pagerduty/hooks/pagerduty_events.py
@@ -18,8 +18,8 @@
 """Hook for sending or receiving data from PagerDuty as well as creating PagerDuty incidents."""
 from __future__ import annotations
 
-from datetime import datetime
 import warnings
+from datetime import datetime
 from typing import Any
 
 import pdpyras
@@ -123,38 +123,20 @@ def create_event(
             stacklevel=1,
         )
 
-        payload = {
-            "summary": summary,
-            "severity": severity,
-            "source": source,
-        }
-        if custom_details is not None:
-            payload["custom_details"] = custom_details
-        if component:
-            payload["component"] = component
-        if group:
-            payload["group"] = group
-        if class_type:
-            payload["class"] = class_type
-
-        actions = ("trigger", "acknowledge", "resolve")
-        if action not in actions:
-            raise ValueError(f"Event action must be one of: {', '.join(actions)}")
-        data = {
-            "event_action": action,
-            "payload": payload,
-        }
-        if dedup_key:
-            data["dedup_key"] = dedup_key
-        elif action != "trigger":
-            raise ValueError(
-                f"The dedup_key property is required for event_action={action} events, "
-                f"and it must be a string."
-            )
-        if images is not None:
-            data["images"] = images
-        if links is not None:
-            data["links"] = links
+        data = PagerdutyEventsHook.prepare_event_data(
+            summary=summary,
+            severity=severity,
+            source=source,
+            custom_details=custom_details,
+            component=component,
+            group=group,
+            class_type=class_type,
+            action=action,
+            dedup_key=dedup_key,
+            images=images,
+            links=links,
+            action_key_name="event_action",
+        )
 
         session = pdpyras.EventsAPISession(self.integration_key)
         resp = session.post("/v2/enqueue", json=data)
@@ -197,8 +179,45 @@ def send_event(
             HTTPS.
             `href`: [Optional] URL to make the image a clickable link.
             `alt`: [Optional] Alternative text for the image.
+        :param links: List of links to include. Each dictionary in the list accepts the following keys:
+            `href`: URL of the link to be attached.
+            `text`: [Optional] Plain text that describes the purpose of the link, and can be used as the
+            link's text.
         :return: PagerDuty Events API v2 response.
         """
+        data = PagerdutyEventsHook.prepare_event_data(
+            summary=summary,
+            severity=severity,
+            source=source,
+            custom_details=custom_details,
+            component=component,
+            group=group,
+            class_type=class_type,
+            action=action,
+            dedup_key=dedup_key,
+            images=images,
+            links=links,
+        )
+
+        session = pdpyras.EventsAPISession(self.integration_key)
+        return session.send_event(**data)
+
+    @staticmethod
+    def prepare_event_data(
+        summary,
+        severity,
+        source,
+        custom_details,
+        component,
+        group,
+        class_type,
+        action,
+        dedup_key,
+        images,
+        links,
+        action_key_name: str = "action",
+    ) -> dict:
+        """Prepare event data for send_event / post('/v2/enqueue') method."""
         payload = {
             "summary": summary,
             "severity": severity,
@@ -217,22 +236,22 @@ def send_event(
         if action not in actions:
             raise ValueError(f"Event action must be one of: {', '.join(actions)}")
         data = {
-            "action": action,
+            action_key_name: action,
             "payload": payload,
         }
         if dedup_key:
             data["dedup_key"] = dedup_key
         elif action != "trigger":
             raise ValueError(
-                f"The dedup_key property is required for action={action} events, and it must be a string."
+                f"The dedup_key property is required for {action_key_name}={action} events,"
+                f" and it must be a string."
             )
         if images is not None:
             data["images"] = images
         if links is not None:
             data["links"] = links
 
-        session = pdpyras.EventsAPISession(self.integration_key)
-        return session.send_event(**data)
+        return data
 
     def create_change_event(
         self,
