diff --git a/airflow/contrib/hooks/opsgenie_alert_hook.py b/airflow/contrib/hooks/opsgenie_alert_hook.py index a65de9c3329c9..abd9c89e09308 100644 --- a/airflow/contrib/hooks/opsgenie_alert_hook.py +++ b/airflow/contrib/hooks/opsgenie_alert_hook.py @@ -15,14 +15,14 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -"""This module is deprecated. Please use :mod:`airflow.providers.opsgenie.hooks.opsgenie_alert`.""" +"""This module is deprecated. Please use :mod:`airflow.providers.opsgenie.hooks.opsgenie`.""" import warnings -from airflow.providers.opsgenie.hooks.opsgenie_alert import OpsgenieAlertHook # noqa +from airflow.providers.opsgenie.hooks.opsgenie import OpsgenieAlertHook # noqa warnings.warn( - "This module is deprecated. Please use `airflow.providers.opsgenie.hooks.opsgenie_alert`.", + "This module is deprecated. Please use `airflow.providers.opsgenie.hooks.opsgenie`.", DeprecationWarning, stacklevel=2, ) diff --git a/airflow/contrib/operators/opsgenie_alert_operator.py b/airflow/contrib/operators/opsgenie_alert_operator.py index b8ce1ddcf4a9e..81d5fe539b696 100644 --- a/airflow/contrib/operators/opsgenie_alert_operator.py +++ b/airflow/contrib/operators/opsgenie_alert_operator.py @@ -15,14 +15,14 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -"""This module is deprecated. Please use :mod:`airflow.providers.opsgenie.operators.opsgenie_alert`.""" +"""This module is deprecated. Please use :mod:`airflow.providers.opsgenie.operators.opsgenie`.""" import warnings -from airflow.providers.opsgenie.operators.opsgenie_alert import OpsgenieAlertOperator # noqa +from airflow.providers.opsgenie.operators.opsgenie import OpsgenieAlertOperator # noqa warnings.warn( - "This module is deprecated. Please use `airflow.providers.opsgenie.operators.opsgenie_alert`.", + "This module is deprecated. Please use `airflow.providers.opsgenie.operators.opsgenie`.", DeprecationWarning, stacklevel=2, ) diff --git a/airflow/providers/opsgenie/example_dags/example_opsgenie_alert.py b/airflow/providers/opsgenie/example_dags/example_opsgenie_alert.py index a39d356f9d78c..72f2db37630fd 100644 --- a/airflow/providers/opsgenie/example_dags/example_opsgenie_alert.py +++ b/airflow/providers/opsgenie/example_dags/example_opsgenie_alert.py @@ -17,7 +17,7 @@ from datetime import datetime from airflow import DAG -from airflow.providers.opsgenie.operators.opsgenie_alert import OpsgenieAlertOperator +from airflow.providers.opsgenie.operators.opsgenie import OpsgenieAlertOperator with DAG( dag_id="opsgenie_alert_operator_dag", diff --git a/airflow/providers/opsgenie/hooks/opsgenie.py b/airflow/providers/opsgenie/hooks/opsgenie.py new file mode 100644 index 0000000000000..c3baae32a5411 --- /dev/null +++ b/airflow/providers/opsgenie/hooks/opsgenie.py @@ -0,0 +1,100 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from typing import Optional + +from opsgenie_sdk import ( + AlertApi, + ApiClient, + Configuration, + CreateAlertPayload, + OpenApiException, + SuccessResponse, +) + +from airflow.hooks.base import BaseHook + + +class OpsgenieAlertHook(BaseHook): + """ + This hook allows you to post alerts to Opsgenie. + Accepts a connection that has an Opsgenie API key as the connection's password. + This hook sets the domain to conn_id.host, and if not set will default + to ``https://api.opsgenie.com``. + + Each Opsgenie API key can be pre-configured to a team integration. + You can override these defaults in this hook. + + :param opsgenie_conn_id: The name of the Opsgenie connection to use + :type opsgenie_conn_id: str + + """ + + conn_name_attr = 'opsgenie_conn_id' + default_conn_name = 'opsgenie_default' + conn_type = 'opsgenie' + hook_name = 'Opsgenie' + + def __init__(self, opsgenie_conn_id: str = 'opsgenie_default') -> None: + super().__init__() # type: ignore[misc] + self.conn_id = opsgenie_conn_id + configuration = Configuration() + conn = self.get_connection(self.conn_id) + configuration.api_key['Authorization'] = conn.password + configuration.host = conn.host or 'https://api.opsgenie.com' + self.alert_api_instance = AlertApi(ApiClient(configuration)) + + def _get_api_key(self) -> str: + """ + Get the API key from the connection + + :return: API key + :rtype: str + """ + conn = self.get_connection(self.conn_id) + return conn.password + + def get_conn(self) -> AlertApi: + """ + Get the underlying AlertApi client + + :return: AlertApi client + :rtype: opsgenie_sdk.AlertApi + """ + return self.alert_api_instance + + def create_alert(self, payload: Optional[dict] = None) -> SuccessResponse: + """ + Create an alert on Opsgenie + + :param payload: Opsgenie API Create Alert payload values + See https://docs.opsgenie.com/docs/alert-api#section-create-alert + :type payload: dict + :return: api response + :rtype: opsgenie_sdk.SuccessResponse + """ + payload = payload or {} + + try: + create_alert_payload = CreateAlertPayload(**payload) + api_response = self.alert_api_instance.create_alert(create_alert_payload) + return api_response + except OpenApiException as e: + self.log.exception('Exception when sending alert to opsgenie with payload: %s', payload) + raise e diff --git a/airflow/providers/opsgenie/hooks/opsgenie_alert.py b/airflow/providers/opsgenie/hooks/opsgenie_alert.py index c3baae32a5411..abd9c89e09308 100644 --- a/airflow/providers/opsgenie/hooks/opsgenie_alert.py +++ b/airflow/providers/opsgenie/hooks/opsgenie_alert.py @@ -15,86 +15,14 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -# - -from typing import Optional - -from opsgenie_sdk import ( - AlertApi, - ApiClient, - Configuration, - CreateAlertPayload, - OpenApiException, - SuccessResponse, -) - -from airflow.hooks.base import BaseHook - - -class OpsgenieAlertHook(BaseHook): - """ - This hook allows you to post alerts to Opsgenie. - Accepts a connection that has an Opsgenie API key as the connection's password. - This hook sets the domain to conn_id.host, and if not set will default - to ``https://api.opsgenie.com``. - - Each Opsgenie API key can be pre-configured to a team integration. - You can override these defaults in this hook. - - :param opsgenie_conn_id: The name of the Opsgenie connection to use - :type opsgenie_conn_id: str +"""This module is deprecated. Please use :mod:`airflow.providers.opsgenie.hooks.opsgenie`.""" - """ +import warnings - conn_name_attr = 'opsgenie_conn_id' - default_conn_name = 'opsgenie_default' - conn_type = 'opsgenie' - hook_name = 'Opsgenie' +from airflow.providers.opsgenie.hooks.opsgenie import OpsgenieAlertHook # noqa - def __init__(self, opsgenie_conn_id: str = 'opsgenie_default') -> None: - super().__init__() # type: ignore[misc] - self.conn_id = opsgenie_conn_id - configuration = Configuration() - conn = self.get_connection(self.conn_id) - configuration.api_key['Authorization'] = conn.password - configuration.host = conn.host or 'https://api.opsgenie.com' - self.alert_api_instance = AlertApi(ApiClient(configuration)) - - def _get_api_key(self) -> str: - """ - Get the API key from the connection - - :return: API key - :rtype: str - """ - conn = self.get_connection(self.conn_id) - return conn.password - - def get_conn(self) -> AlertApi: - """ - Get the underlying AlertApi client - - :return: AlertApi client - :rtype: opsgenie_sdk.AlertApi - """ - return self.alert_api_instance - - def create_alert(self, payload: Optional[dict] = None) -> SuccessResponse: - """ - Create an alert on Opsgenie - - :param payload: Opsgenie API Create Alert payload values - See https://docs.opsgenie.com/docs/alert-api#section-create-alert - :type payload: dict - :return: api response - :rtype: opsgenie_sdk.SuccessResponse - """ - payload = payload or {} - - try: - create_alert_payload = CreateAlertPayload(**payload) - api_response = self.alert_api_instance.create_alert(create_alert_payload) - return api_response - except OpenApiException as e: - self.log.exception('Exception when sending alert to opsgenie with payload: %s', payload) - raise e +warnings.warn( + "This module is deprecated. Please use `airflow.providers.opsgenie.hooks.opsgenie`.", + DeprecationWarning, + stacklevel=2, +) diff --git a/airflow/providers/opsgenie/operators/opsgenie.py b/airflow/providers/opsgenie/operators/opsgenie.py new file mode 100644 index 0000000000000..739cf0619163c --- /dev/null +++ b/airflow/providers/opsgenie/operators/opsgenie.py @@ -0,0 +1,144 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +from typing import Any, Dict, List, Optional + +from airflow.models import BaseOperator +from airflow.providers.opsgenie.hooks.opsgenie import OpsgenieAlertHook + + +class OpsgenieAlertOperator(BaseOperator): + """ + This operator allows you to post alerts to Opsgenie. + Accepts a connection that has an Opsgenie API key as the connection's password. + This operator sets the domain to conn_id.host, and if not set will default + to ``https://api.opsgenie.com``. + + Each Opsgenie API key can be pre-configured to a team integration. + You can override these defaults in this operator. + + .. seealso:: + For more information on how to use this operator, take a look at the guide: + :ref:`howto/operator:OpsgenieAlertOperator` + + :param opsgenie_conn_id: The name of the Opsgenie connection to use + :type opsgenie_conn_id: str + :param message: The Message of the Opsgenie alert (templated) + :type message: str + :param alias: Client-defined identifier of the alert (templated) + :type alias: str + :param description: Description field of the alert (templated) + :type description: str + :param responders: Teams, users, escalations and schedules that + the alert will be routed to send notifications. + :type responders: list[dict] + :param visible_to: Teams and users that the alert will become visible + to without sending any notification. + :type visible_to: list[dict] + :param actions: Custom actions that will be available for the alert. + :type actions: list[str] + :param tags: Tags of the alert. + :type tags: list[str] + :param details: Map of key-value pairs to use as custom properties of the alert. + :type details: dict + :param entity: Entity field of the alert that is + generally used to specify which domain alert is related to. (templated) + :type entity: str + :param source: Source field of the alert. Default value is + IP address of the incoming request. + :type source: str + :param priority: Priority level of the alert. Default value is P3. (templated) + :type priority: str + :param user: Display name of the request owner. + :type user: str + :param note: Additional note that will be added while creating the alert. (templated) + :type note: str + """ + + template_fields = ('message', 'alias', 'description', 'entity', 'priority', 'note') + + def __init__( + self, + *, + message: str, + opsgenie_conn_id: str = 'opsgenie_default', + alias: Optional[str] = None, + description: Optional[str] = None, + responders: Optional[List[dict]] = None, + visible_to: Optional[List[dict]] = None, + actions: Optional[List[str]] = None, + tags: Optional[List[str]] = None, + details: Optional[dict] = None, + entity: Optional[str] = None, + source: Optional[str] = None, + priority: Optional[str] = None, + user: Optional[str] = None, + note: Optional[str] = None, + **kwargs, + ) -> None: + super().__init__(**kwargs) + + self.message = message + self.opsgenie_conn_id = opsgenie_conn_id + self.alias = alias + self.description = description + self.responders = responders + self.visible_to = visible_to + self.actions = actions + self.tags = tags + self.details = details + self.entity = entity + self.source = source + self.priority = priority + self.user = user + self.note = note + self.hook: Optional[OpsgenieAlertHook] = None + + def _build_opsgenie_payload(self) -> Dict[str, Any]: + """ + Construct the Opsgenie JSON payload. All relevant parameters are combined here + to a valid Opsgenie JSON payload. + + :return: Opsgenie payload (dict) to send + """ + payload = {} + + for key in [ + "message", + "alias", + "description", + "responders", + "visible_to", + "actions", + "tags", + "details", + "entity", + "source", + "priority", + "user", + "note", + ]: + val = getattr(self, key) + if val: + payload[key] = val + return payload + + def execute(self, context) -> None: + """Call the OpsgenieAlertHook to post message""" + self.hook = OpsgenieAlertHook(self.opsgenie_conn_id) + self.hook.create_alert(self._build_opsgenie_payload()) diff --git a/airflow/providers/opsgenie/operators/opsgenie_alert.py b/airflow/providers/opsgenie/operators/opsgenie_alert.py index 7554f88d2db24..81d5fe539b696 100644 --- a/airflow/providers/opsgenie/operators/opsgenie_alert.py +++ b/airflow/providers/opsgenie/operators/opsgenie_alert.py @@ -15,130 +15,14 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -# -from typing import Any, Dict, List, Optional - -from airflow.models import BaseOperator -from airflow.providers.opsgenie.hooks.opsgenie_alert import OpsgenieAlertHook - - -class OpsgenieAlertOperator(BaseOperator): - """ - This operator allows you to post alerts to Opsgenie. - Accepts a connection that has an Opsgenie API key as the connection's password. - This operator sets the domain to conn_id.host, and if not set will default - to ``https://api.opsgenie.com``. - - Each Opsgenie API key can be pre-configured to a team integration. - You can override these defaults in this operator. - - .. seealso:: - For more information on how to use this operator, take a look at the guide: - :ref:`howto/operator:OpsgenieAlertOperator` - - :param opsgenie_conn_id: The name of the Opsgenie connection to use - :type opsgenie_conn_id: str - :param message: The Message of the Opsgenie alert (templated) - :type message: str - :param alias: Client-defined identifier of the alert (templated) - :type alias: str - :param description: Description field of the alert (templated) - :type description: str - :param responders: Teams, users, escalations and schedules that - the alert will be routed to send notifications. - :type responders: list[dict] - :param visible_to: Teams and users that the alert will become visible - to without sending any notification. - :type visible_to: list[dict] - :param actions: Custom actions that will be available for the alert. - :type actions: list[str] - :param tags: Tags of the alert. - :type tags: list[str] - :param details: Map of key-value pairs to use as custom properties of the alert. - :type details: dict - :param entity: Entity field of the alert that is - generally used to specify which domain alert is related to. (templated) - :type entity: str - :param source: Source field of the alert. Default value is - IP address of the incoming request. - :type source: str - :param priority: Priority level of the alert. Default value is P3. (templated) - :type priority: str - :param user: Display name of the request owner. - :type user: str - :param note: Additional note that will be added while creating the alert. (templated) - :type note: str - """ - - template_fields = ('message', 'alias', 'description', 'entity', 'priority', 'note') - - def __init__( - self, - *, - message: str, - opsgenie_conn_id: str = 'opsgenie_default', - alias: Optional[str] = None, - description: Optional[str] = None, - responders: Optional[List[dict]] = None, - visible_to: Optional[List[dict]] = None, - actions: Optional[List[str]] = None, - tags: Optional[List[str]] = None, - details: Optional[dict] = None, - entity: Optional[str] = None, - source: Optional[str] = None, - priority: Optional[str] = None, - user: Optional[str] = None, - note: Optional[str] = None, - **kwargs, - ) -> None: - super().__init__(**kwargs) - - self.message = message - self.opsgenie_conn_id = opsgenie_conn_id - self.alias = alias - self.description = description - self.responders = responders - self.visible_to = visible_to - self.actions = actions - self.tags = tags - self.details = details - self.entity = entity - self.source = source - self.priority = priority - self.user = user - self.note = note - self.hook: Optional[OpsgenieAlertHook] = None - - def _build_opsgenie_payload(self) -> Dict[str, Any]: - """ - Construct the Opsgenie JSON payload. All relevant parameters are combined here - to a valid Opsgenie JSON payload. +"""This module is deprecated. Please use :mod:`airflow.providers.opsgenie.operators.opsgenie`.""" - :return: Opsgenie payload (dict) to send - """ - payload = {} +import warnings - for key in [ - "message", - "alias", - "description", - "responders", - "visible_to", - "actions", - "tags", - "details", - "entity", - "source", - "priority", - "user", - "note", - ]: - val = getattr(self, key) - if val: - payload[key] = val - return payload +from airflow.providers.opsgenie.operators.opsgenie import OpsgenieAlertOperator # noqa - def execute(self, context) -> None: - """Call the OpsgenieAlertHook to post message""" - self.hook = OpsgenieAlertHook(self.opsgenie_conn_id) - self.hook.create_alert(self._build_opsgenie_payload()) +warnings.warn( + "This module is deprecated. Please use `airflow.providers.opsgenie.operators.opsgenie`.", + DeprecationWarning, + stacklevel=2, +) diff --git a/airflow/providers/opsgenie/provider.yaml b/airflow/providers/opsgenie/provider.yaml index eb2d5ba7de0f0..0f1dede450852 100644 --- a/airflow/providers/opsgenie/provider.yaml +++ b/airflow/providers/opsgenie/provider.yaml @@ -43,15 +43,17 @@ operators: - integration-name: Opsgenie python-modules: - airflow.providers.opsgenie.operators.opsgenie_alert + - airflow.providers.opsgenie.operators.opsgenie hooks: - integration-name: Opsgenie python-modules: - airflow.providers.opsgenie.hooks.opsgenie_alert + - airflow.providers.opsgenie.hooks.opsgenie hook-class-names: # deprecated - to be removed after providers add dependency on Airflow 2.2.0+ - - airflow.providers.opsgenie.hooks.opsgenie_alert.OpsgenieAlertHook + - airflow.providers.opsgenie.hooks.opsgenie.OpsgenieAlertHook connection-types: - - hook-class-name: airflow.providers.opsgenie.hooks.opsgenie_alert.OpsgenieAlertHook + - hook-class-name: airflow.providers.opsgenie.hooks.opsgenie.OpsgenieAlertHook connection-type: opsgenie diff --git a/dev/provider_packages/prepare_provider_packages.py b/dev/provider_packages/prepare_provider_packages.py index 0b534c25379cb..7432bc0c5847d 100755 --- a/dev/provider_packages/prepare_provider_packages.py +++ b/dev/provider_packages/prepare_provider_packages.py @@ -2174,6 +2174,8 @@ def summarise_total_vs_bad_and_warnings(total: int, bad: int, warns: List[warnin 'This module is deprecated. Please use `airflow.providers.amazon.aws.operators.sagemaker`.', 'This module is deprecated. Please use `airflow.providers.amazon.aws.sensors.sagemaker`.', 'This module is deprecated. Please use `airflow.providers.amazon.aws.hooks.emr`.', + 'This module is deprecated. Please use `airflow.providers.opsgenie.hooks.opsgenie`.', + 'This module is deprecated. Please use `airflow.providers.opsgenie.operators.opsgenie`.', } diff --git a/docs/apache-airflow-providers-opsgenie/operators/opsgenie_alert.rst b/docs/apache-airflow-providers-opsgenie/operators/opsgenie_alert.rst index 7f6d094754050..2c0f0a4cf4453 100644 --- a/docs/apache-airflow-providers-opsgenie/operators/opsgenie_alert.rst +++ b/docs/apache-airflow-providers-opsgenie/operators/opsgenie_alert.rst @@ -20,7 +20,7 @@ OpsgenieAlertOperator ========================== -Use the :class:`~airflow.providers.opsgenie.operators.opsgenie_alert.OpsgenieAlertOperator` to send an alert to opsgenie. +Use the :class:`~airflow.providers.opsgenie.operators.opsgenie.OpsgenieAlertOperator` to send an alert to opsgenie. Using the Operator diff --git a/tests/deprecated_classes.py b/tests/deprecated_classes.py index 310c7d63a96d1..0fb00636dcbe0 100644 --- a/tests/deprecated_classes.py +++ b/tests/deprecated_classes.py @@ -344,7 +344,7 @@ 'airflow.contrib.hooks.jira_hook.JiraHook', ), ( - 'airflow.providers.opsgenie.hooks.opsgenie_alert.OpsgenieAlertHook', + 'airflow.providers.opsgenie.hooks.opsgenie.OpsgenieAlertHook', 'airflow.contrib.hooks.opsgenie_alert_hook.OpsgenieAlertHook', ), ( @@ -1304,7 +1304,7 @@ 'airflow.contrib.operators.jenkins_job_trigger_operator.JenkinsJobTriggerOperator', ), ( - 'airflow.providers.opsgenie.operators.opsgenie_alert.OpsgenieAlertOperator', + 'airflow.providers.opsgenie.operators.opsgenie.OpsgenieAlertOperator', 'airflow.contrib.operators.opsgenie_alert_operator.OpsgenieAlertOperator', ), ( diff --git a/tests/providers/opsgenie/hooks/test_opsgenie_alert.py b/tests/providers/opsgenie/hooks/test_opsgenie.py similarity index 98% rename from tests/providers/opsgenie/hooks/test_opsgenie_alert.py rename to tests/providers/opsgenie/hooks/test_opsgenie.py index a03b350545d14..6373dc110e2c2 100644 --- a/tests/providers/opsgenie/hooks/test_opsgenie_alert.py +++ b/tests/providers/opsgenie/hooks/test_opsgenie.py @@ -24,7 +24,7 @@ from opsgenie_sdk.exceptions import AuthenticationException from airflow.models import Connection -from airflow.providers.opsgenie.hooks.opsgenie_alert import OpsgenieAlertHook +from airflow.providers.opsgenie.hooks.opsgenie import OpsgenieAlertHook from airflow.utils import db diff --git a/tests/providers/opsgenie/operators/test_opsgenie_alert.py b/tests/providers/opsgenie/operators/test_opsgenie.py similarity index 98% rename from tests/providers/opsgenie/operators/test_opsgenie_alert.py rename to tests/providers/opsgenie/operators/test_opsgenie.py index 321ba756aa452..6ee6599c8c8c8 100644 --- a/tests/providers/opsgenie/operators/test_opsgenie_alert.py +++ b/tests/providers/opsgenie/operators/test_opsgenie.py @@ -20,7 +20,7 @@ import unittest from airflow.models.dag import DAG -from airflow.providers.opsgenie.operators.opsgenie_alert import OpsgenieAlertOperator +from airflow.providers.opsgenie.operators.opsgenie import OpsgenieAlertOperator from airflow.utils import timezone DEFAULT_DATE = timezone.datetime(2017, 1, 1)