From 687f208e78b98a7865f2ac7f700604b748c4632a Mon Sep 17 00:00:00 2001 From: Kanva Bhatia <56185464+KanvaBhatia@users.noreply.github.com> Date: Fri, 17 May 2024 13:52:47 +0530 Subject: [PATCH] chore(provider): PagerDuty scopes added and fix: mongodb connection check (#1183) --- .../documentation/pagerduty-provider.mdx | 46 +++++++++----- .../mongodb_provider/mongodb_provider.py | 7 ++- .../pagerduty_provider/pagerduty_provider.py | 61 ++++++++++++++++++- 3 files changed, 94 insertions(+), 20 deletions(-) diff --git a/docs/providers/documentation/pagerduty-provider.mdx b/docs/providers/documentation/pagerduty-provider.mdx index 5e1b3ebce..f87a7d275 100644 --- a/docs/providers/documentation/pagerduty-provider.mdx +++ b/docs/providers/documentation/pagerduty-provider.mdx @@ -5,25 +5,19 @@ description: "Pagerduty Provider is a provider that allows to create incidents o ## Inputs -The `notify` function in the `PagerdutyProvider` class takes the following parameters: - -```python -kwargs (dict): - title (str): Title of the alert or incident. *Required* - alert_body (str): UTF-8 string of custom message for alert. Shown in incident body for events, and in the body for incidents. *Required for events, optional for incidents* - dedup (str | None): Any string, max 255 characters, used to deduplicate alerts for events. *Required for events, optional for incidents* - service_id (str): ID of the service for incidents. *Required for incidents, optional for events* - body (dict): Body of the incident. *Required for incidents, optional for events* - requester (str): Requester of the incident. *Required for incidents, optional for events* - incident_key (str | None): Key to identify the incident. If not given, a UUID will be generated. *Required for incidents, optional for events* -``` +- `title`: str: Title of the alert or incident. +- `alert_body`: str: UTF-8 string of custom message for alert. Shown in incident body for events, and in the body for incidents. +- `dedup`: str | None: Any string, max 255 characters, used to deduplicate alerts for events. +- `service_id`: str: ID of the service for incidents. +- `body`: dict: Body of the incident. +- `requester`: str: Requester of the incident. +- `incident_key`: str | None: Key to identify the incident. If not given, a UUID will be generated. ## Authentication Parameters -The PagerdutyProviderAuthConfig class takes the following parameters: -python -routing*key (str | None): Routing key, which is an integration or ruleset key. Optional, default is `None`. \_Required for events, optional for incidents*_No -api*key (str | None): API key, which is a user or team API key. Optional, default is `None`. \_Required for incidents, optional for events*_ +The `api_key` or `routing_key` are required for connecting to the Pagerduty provider. You can obtain them as described in the "Connecting with the Provider" section. + +Routing key, which is an integration or ruleset key. API key, which is a user or team API key. ## Connecting with the Provider @@ -35,6 +29,26 @@ You can find your API key in the PagerDuty web app under **Configuration** > **A The routing_key is used to post events to Pagerduty using the events API. The api_key is used to create incidents using the incidents API. +## Scopes + +Certain scopes may be required to perform specific actions or queries via the Pagerduty Provider. Below is a summary of relevant scopes and their use cases: + +- incidents_read (Incidents Read) + Required: True + Description: View incidents. +- incidents_write (Incidents Write) + Required: False + Description: Write incidents. +- webhook_subscriptions_read (Webhook Subscriptions Read) + Required: False + Description: View webhook subscriptions. + (*Required for auto-webhook integration) +- webhook_subscriptions_write (Webhook Subscriptions Write) + Required: False + Description: Write webhook subscriptions. + (*Required for auto-webhook integration) + + ## Notes The provider uses either the events API or the incidents API to create an alert or an incident. The choice of API to use is determined by the presence of either a routing_key or an api_key. diff --git a/keep/providers/mongodb_provider/mongodb_provider.py b/keep/providers/mongodb_provider/mongodb_provider.py index fce8df42c..50bd7d939 100644 --- a/keep/providers/mongodb_provider/mongodb_provider.py +++ b/keep/providers/mongodb_provider/mongodb_provider.py @@ -77,14 +77,15 @@ def validate_scopes(self): """ try: client = self.__generate_client() + client.admin.command('ping') # will raise an exception if the server is not available client.close() scopes = { "connect_to_server": True, } - except Exception as e: + except Exception: self.logger.exception("Error validating scopes") scopes = { - "connect_to_server": str(e), + "connect_to_server": "Unable to connect to server. Please check the connection details.", } return scopes @@ -117,7 +118,7 @@ def __generate_client(self): and k != "additional_options" # additional_options will go seperately and k != "database" } # database is not a valid mongo option - client = MongoClient(**client_conf, **additional_options) + client = MongoClient(**client_conf, **additional_options, serverSelectionTimeoutMS=10000) # 10 seconds timeout return client def dispose(self): diff --git a/keep/providers/pagerduty_provider/pagerduty_provider.py b/keep/providers/pagerduty_provider/pagerduty_provider.py index 1788de9dc..ddfd9f240 100644 --- a/keep/providers/pagerduty_provider/pagerduty_provider.py +++ b/keep/providers/pagerduty_provider/pagerduty_provider.py @@ -11,7 +11,7 @@ from keep.contextmanager.contextmanager import ContextManager from keep.exceptions.provider_config_exception import ProviderConfigException from keep.providers.base.base_provider import BaseProvider -from keep.providers.models.provider_config import ProviderConfig +from keep.providers.models.provider_config import ProviderConfig, ProviderScope from keep.providers.providers_factory import ProvidersFactory # Todo: think about splitting in to PagerdutyIncidentsProvider and PagerdutyAlertsProvider @@ -40,6 +40,34 @@ class PagerdutyProviderAuthConfig: class PagerdutyProvider(BaseProvider): """Pull alerts and query incidents from PagerDuty.""" + PROVIDER_SCOPES = [ + ProviderScope( + name="incidents_read", + description="Read incidents data.", + mandatory=True, + alias="Incidents Data Read", + ), + ProviderScope( + name="incidents_write", + description="Write incidents.", + mandatory=False, + alias="Incidents Write", + ), + ProviderScope( + name="webhook_subscriptions_read", + description="Read webhook data.", + mandatory=False, + mandatory_for_webhook=True, + alias="Webhooks Data Read", + ), + ProviderScope( + name="webhook_subscriptions_write", + description="Write webhooks.", + mandatory=False, + mandatory_for_webhook=True, + alias="Webhooks Write", + ), + ] SUBSCRIPTION_API_URL = "https://api.pagerduty.com/webhook_subscriptions" PROVIDER_DISPLAY_NAME = "PagerDuty" SEVERITIES_MAP = { @@ -71,6 +99,37 @@ def validate_config(self): "PagerdutyProvider requires either routing_key or api_key", provider_id=self.provider_id, ) + + def validate_scopes(self): + """ + Validate that the provider has the required scopes. + """ + headers = { + "Accept": "application/json", + "Authorization": f"Token token={self.authentication_config.api_key}", + } + scopes = {} + for scope in self.PROVIDER_SCOPES: + try: + # Todo: how to check validity for write scopes? + if scope.name.startswith("incidents"): + response = requests.get( + "https://api.pagerduty.com/incidents", + headers=headers, + ) + elif scope.name.startswith("webhook_subscriptions"): + response = requests.get( + self.SUBSCRIPTION_API_URL, + headers=headers, + ) + if response.ok: + scopes[scope.name] = True + else: + scopes[scope.name] = response.reason + except Exception as e: + self.logger.exception("Error validating scopes") + scopes[scope.name] = str(e) + return scopes def _build_alert( self, title: str, alert_body: str, dedup: str