Skip to content

Commit

Permalink
Merge pull request #475 from DrDroidLab/integration
Browse files Browse the repository at this point in the history
Integration
  • Loading branch information
dimittal authored Sep 3, 2024
2 parents da51fc7 + 78a8ec7 commit af483e0
Show file tree
Hide file tree
Showing 110 changed files with 2,837 additions and 322 deletions.
75 changes: 75 additions & 0 deletions connectors/handlers/bots/rootly_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import logging
from typing import Dict

from connectors.crud.connector_asset_model_crud import create_or_update_model_metadata
from connectors.crud.connectors_crud import get_db_connectors
from connectors.handlers.tasks import rootly_handle_webhook_call
from management.crud.task_crud import get_or_create_task
from management.models import TaskRun, PeriodicTaskStatus
from protos.base_pb2 import Source, SourceModelType
from protos.connectors.connector_pb2 import Connector

logger = logging.getLogger(__name__)


def create_or_update_rootly_incident_metadata(account_id, connector_id, incident_id, title=None):
try:
metadata = {
'incident_id': incident_id,
'title': title,
}
return create_or_update_model_metadata(account_id=account_id, connector_id=connector_id,
connector_type=Source.ROOTLY,
model_type=SourceModelType.ROOTLY_INCIDENT,
model_uid=incident_id, is_active=True, metadata=metadata)
except Exception as e:
logger.error(
f"Error while saving RootlyIncidentMetadata: {connector_id}:{incident_id} with error: {e}")
return None, False


def handle_rootly_incident(data: Dict):
active_account_rootly_connectors = get_db_connectors(connector_type=Source.ROOTLY, is_active=True)
if not active_account_rootly_connectors:
logger.error(f"Error handling rootly event callback api: active rootly connector not found")
raise Exception("No active rootly connector found")
rootly_connector = active_account_rootly_connectors.first()
rootly_connector_proto: Connector = rootly_connector.unmasked_proto

event = data.get('event')
event_data = data.get('data', {})
incident_id = event_data.get('id')
if not incident_id:
logger.error(f"Error handling rootly event callback api: incident id not found in request data: {data}")
raise Exception("Invalid data received")

title = event_data.get('title')
rootly_incident = {
'incident_id': incident_id,
'title': title,
}

event_type = event.get('type', '')
if event_type == 'incident.created':
try:
saved_task = get_or_create_task(rootly_handle_webhook_call.__name__, rootly_connector_proto.id.value,
rootly_incident)
if not saved_task:
logger.error("Failed to create task for rootly incident fetch job")
task = rootly_handle_webhook_call.delay(rootly_connector_proto.id.value, rootly_incident)
task_run = TaskRun.objects.create(task=saved_task, task_uuid=task.id,
status=PeriodicTaskStatus.SCHEDULED,
account_id=rootly_connector_proto.account_id.value)
except Exception as e:
logger.error(f"Error while creating task for rootly incident fetch job with error: {e}")
raise Exception("Error while creating task for rootly incident fetch job")

try:
create_or_update_rootly_incident_metadata(account_id=rootly_connector_proto.account_id.value,
connector_id=rootly_connector_proto.id.value,
incident_id=incident_id, title=title)
return True
except Exception as e:
logger.error(
f"Error while registering rootly incident metadata for connector: {rootly_connector_proto.id.value} with error: {e}")
return False
87 changes: 87 additions & 0 deletions connectors/handlers/bots/zenduty_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import logging
from typing import Dict

from connectors.crud.connector_asset_model_crud import create_or_update_model_metadata
from connectors.crud.connectors_crud import get_db_connectors
from connectors.handlers.tasks import zenduty_handle_webhook_call
from management.crud.task_crud import get_or_create_task
from management.models import TaskRun, PeriodicTaskStatus
from protos.base_pb2 import Source, SourceModelType
from protos.connectors.connector_pb2 import Connector

logger = logging.getLogger(__name__)


def create_or_update_zd_incident_metadata(account_id, connector_id, incident_id, title=None, service_id=None,
service_name=None):
try:
metadata = {
'incident_id': incident_id,
'title': title,
'service_id': service_id,
'service_name': service_name
}
return create_or_update_model_metadata(account_id=account_id, connector_id=connector_id,
connector_type=Source.ZENDUTY,
model_type=SourceModelType.ZENDUTY_INCIDENT,
model_uid=incident_id, is_active=True, metadata=metadata)
except Exception as e:
logger.error(
f"Error while saving ZenDutyIncidentMetadata: {connector_id}:{incident_id} with error: {e}")
return None, False


def handle_zd_incident(data: Dict):
payload = data.get('payload')
active_account_zd_connectors = get_db_connectors(connector_type=Source.ZENDUTY, is_active=True)
if not active_account_zd_connectors:
logger.error(f"Error handling zenduty event callback api: active zenduty connector not found")
raise Exception("No active zenduty connector found")
zenduty_connector = active_account_zd_connectors.first()
zenduty_connector_proto: Connector = zenduty_connector.unmasked_proto

incident_data = payload.get('incident', {})
incident_id = incident_data.get('incident_number')
if not incident_id:
logger.error(f"Error handling zenduty event callback api: incident number not found in request data: {data}")
raise Exception("Invalid data received")
service_id = incident_data.get('service', {}).get('unique_id')

if not service_id:
logger.error(f"Error handling zenduty event callback api: unique service id not found in request data: {data}")
raise Exception("Invalid data received")

title = incident_data.get('title')
service_name = incident_data.get('service', {}).get('name')
zenduty_incident = {
'incident_id': incident_id,
'title': title,
'service_id': service_id,
'service_name': service_name
}

incident_status = payload.get('event_type', '')
if incident_status == 'triggered':
try:
saved_task = get_or_create_task(zenduty_handle_webhook_call.__name__, zenduty_connector_proto.id.value,
zenduty_incident)
if not saved_task:
logger.error("Failed to create task for zenduty incident fetch job")
task = zenduty_handle_webhook_call.delay(zenduty_connector_proto.id.value, zenduty_incident)
task_run = TaskRun.objects.create(task=saved_task, task_uuid=task.id,
status=PeriodicTaskStatus.SCHEDULED,
account_id=zenduty_connector_proto.account_id.value)
except Exception as e:
logger.error(f"Error while creating task for zenduty incident fetch job with error: {e}")
raise Exception("Error while creating task for zenduty incident fetch job")

try:
create_or_update_zd_incident_metadata(account_id=zenduty_connector_proto.account_id.value,
connector_id=zenduty_connector_proto.id.value,
incident_id=incident_id, title=title, service_id=service_id,
service_name=service_name)
return True
except Exception as e:
logger.error(
f"Error while registering zenduty incident metadata for connector: {zenduty_connector_proto.id.value} with error: {e}")
return False
72 changes: 72 additions & 0 deletions connectors/handlers/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,75 @@ def pager_duty_handle_webhook_call(pagerduty_connector_id, pager_duty_incident):
pagerduty_handle_webhook_call_prerun_notifier = publish_pre_run_task(pager_duty_handle_webhook_call)
pagerduty_handle_webhook_call_failure_notifier = publish_task_failure(pager_duty_handle_webhook_call)
pagerduty_handle_webhook_call_postrun_notifier = publish_post_run_task(pager_duty_handle_webhook_call)

@shared_task(max_retries=3, default_retry_delay=10)
def rootly_handle_webhook_call(rootly_connector_id, rootly_incident):
try:
rootly_connector = get_db_connectors(connector_id=rootly_connector_id)
rootly_connector = rootly_connector.first()
if not rootly_connector:
logger.error(
f"Error while handling Rootly handle_receive_message: Connector not found for connector_id: "
f"{rootly_connector_id}")
return
rootly_connector_proto: Connector = rootly_connector.unmasked_proto
account_id = rootly_connector_proto.account_id.value
if 'incident_id' not in rootly_incident:
logger.error(
f"Error while handling rootly webhook call: Incident ID not found for rootly event")
return

all_rootly_incident_entry_points = get_db_workflow_entry_points(account_id=account_id,
entry_point_type=WorkflowEntryPoint.Type.ROOTLY_INCIDENT,
is_active=True)
ep_protos = [e.proto for e in all_rootly_incident_entry_points]
for ep in ep_protos:
is_triggered = entry_point_evaluator_facade.evaluate(ep, rootly_incident)
if is_triggered:
trigger_alert_entry_point_workflows(account_id, ep.id.value, 'ROOTLY',
WorkflowExecution.WorkflowExecutionMetadata.Type.ROOTLY_INCIDENT,
rootly_incident)
except Exception as e:
logger.error(f"Error while handling rootly webhook call with error: {e} for event: {rootly_incident}")
return


rootly_handle_webhook_call_prerun_notifier = publish_pre_run_task(rootly_handle_webhook_call)
rootly_handle_webhook_call_failure_notifier = publish_task_failure(rootly_handle_webhook_call)
rootly_handle_webhook_call_postrun_notifier = publish_post_run_task(rootly_handle_webhook_call)

@shared_task(max_retries=3, default_retry_delay=10)
def zenduty_handle_webhook_call(zenduty_connector_id, zenduty_incident):
try:
zenduty_connector = get_db_connectors(connector_id=zenduty_connector_id)
zenduty_connector = zenduty_connector.first()
if not zenduty_connector:
logger.error(
f"Error while handling Zenduty handle_receive_message: Connector not found for connector_id: "
f"{zenduty_connector}")
return
zenduty_connector_proto: Connector = zenduty_connector.unmasked_proto
account_id = zenduty_connector_proto.account_id.value
if 'incident_id' not in zenduty_incident or 'service_name' not in zenduty_incident:
logger.error(
f"Error while handling zenduty webhook call: Incident id or service name not found for zenduty event")
return

all_zd_incident_entry_points = get_db_workflow_entry_points(account_id=account_id,
entry_point_type=WorkflowEntryPoint.Type.ZENDUTY_INCIDENT,
is_active=True)
ep_protos = [e.proto for e in all_zd_incident_entry_points]
for ep in ep_protos:
is_triggered = entry_point_evaluator_facade.evaluate(ep, zenduty_incident)
if is_triggered:
trigger_alert_entry_point_workflows(account_id, ep.id.value, 'ZENDUTY',
WorkflowExecution.WorkflowExecutionMetadata.Type.ZENDUTY_INCIDENT,
zenduty_incident)
except Exception as e:
logger.error(f"Error while handling zenduty webhook call with error: {e} for event: {zenduty_incident}")
return


zenduty_handle_webhook_call_prerun_notifier = publish_pre_run_task(zenduty_handle_webhook_call)
zenduty_handle_webhook_call_failure_notifier = publish_task_failure(zenduty_handle_webhook_call)
zenduty_handle_webhook_call_postrun_notifier = publish_post_run_task(zenduty_handle_webhook_call)
6 changes: 5 additions & 1 deletion connectors/handlers/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,9 @@
path('slack_bot/handle_callback_events', handler_views.slack_bot_handle_callback_events),
path('slack_bot/app_manifest_create', handler_views.slack_manifest_create),
path('pagerduty/handle_incidents', handler_views.pagerduty_handle_incidents),
path('pagerduty/generate/webhook', handler_views.pagerduty_generate_webhook)
path('pagerduty/generate/webhook', handler_views.pagerduty_generate_webhook),
path('rootly/handle_incidents', handler_views.rootly_handle_incidents),
path('rootly/generate/webhook', handler_views.rootly_generate_webhook),
path('zenduty/generate/webhook', handler_views.zenduty_generate_webhook),
path('zenduty/handle_incidents', handler_views.zenuty_handle_incidents),
]
76 changes: 74 additions & 2 deletions connectors/handlers/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
from accounts.authentication import AccountApiTokenAuthentication
from accounts.models import get_request_account, Account, User, get_request_user, AccountApiToken
from connectors.handlers.bots.pager_duty_handler import handle_pd_incident
from connectors.handlers.bots.rootly_handler import handle_rootly_incident
from connectors.handlers.bots.zenduty_handler import handle_zd_incident
from connectors.handlers.bots.slack_bot_handler import handle_slack_event_callback
from connectors.models import Site
from playbooks.utils.decorators import web_api, account_post_api, api_auth_check
from utils.time_utils import current_epoch_timestamp
from protos.connectors.api_pb2 import GetSlackAppManifestResponse, GetSlackAppManifestRequest, \
GetPagerDutyWebhookRequest, GetPagerDutyWebhookResponse
GetPagerDutyWebhookRequest, GetPagerDutyWebhookResponse, GetRootlyWebhookRequest, GetRootlyWebhookResponse, GetPagerDutyWebhookRequest, GetPagerDutyWebhookResponse, GetZendutyWebhookRequest, GetZendutyWebhookResponse
from utils.uri_utils import build_absolute_uri, construct_curl

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -143,6 +145,50 @@ def pagerduty_generate_webhook(request_message: GetPagerDutyWebhookRequest) -> H
curl = construct_curl('POST', uri, headers=headers, payload=None)
return HttpResponse(curl, content_type="text/plain", status=200)

@web_api(GetZendutyWebhookRequest)
def zenduty_generate_webhook(request_message: GetZendutyWebhookRequest) -> HttpResponse:
account: Account = get_request_account()
user: User = get_request_user()

qs = account.account_api_token.filter(created_by=user)
if qs:
account_api_token = qs.first()
else:
api_token = AccountApiToken(account=account, created_by=user)
api_token.save()
account_api_token = api_token

headers = {'Authorization': f'Bearer {account_api_token.key}'}
location = settings.ZENDUTY_WEBHOOK_LOCATION
protocol = settings.ZENDUTY_WEBHOOK_HTTP_PROTOCOL
enabled = settings.ZENDUTY_WEBHOOK_USE_SITE
uri = build_absolute_uri(None, location, protocol, enabled)

curl = construct_curl('POST', uri, headers=headers, payload=None)
return HttpResponse(curl, content_type="text/plain", status=200)

@web_api(GetRootlyWebhookRequest)
def rootly_generate_webhook(request_message: GetRootlyWebhookRequest) -> HttpResponse:
account: Account = get_request_account()
user: User = get_request_user()

qs = account.account_api_token.filter(created_by=user)
if qs:
account_api_token = qs.first()
else:
api_token = AccountApiToken(account=account, created_by=user)
api_token.save()
account_api_token = api_token

headers = {'Authorization': f'Bearer {account_api_token.key}'}
location = settings.ROOTLY_WEBHOOK_LOCATION
protocol = settings.ROOTLY_WEBHOOK_HTTP_PROTOCOL
enabled = settings.ROOTLY_WEBHOOK_USE_SITE
uri = build_absolute_uri(None, location, protocol, enabled)

curl = construct_curl('POST', uri, headers=headers, payload=None)
return HttpResponse(curl, content_type="text/plain", status=200)


@csrf_exempt
@api_view(['POST'])
Expand All @@ -152,7 +198,33 @@ def pagerduty_handle_incidents(request_message: HttpRequest) -> JsonResponse:
try:
data = request_message.data
handle_pd_incident(data)
return JsonResponse({'success': False, 'message': 'pagerduty incident Handling failed'}, status=200)
return JsonResponse({'success': True, 'message': 'pagerduty incident Handling sucessfull'}, status=200)
except Exception as e:
logger.error(f'Error handling pagerduty incident: {str(e)}')
return JsonResponse({'success': False, 'message': f"pagerduty incident Handling failed"}, status=500)

@csrf_exempt
@api_view(['POST'])
@authentication_classes([AccountApiTokenAuthentication])
@api_auth_check
def rootly_handle_incidents(request_message: HttpRequest) -> JsonResponse:
try:
data = request_message.data
handle_rootly_incident(data)
return JsonResponse({'success': True, 'message': 'rootly incident Handling sucessfull'}, status=200)
except Exception as e:
logger.error(f'Error handling rootly incident: {str(e)}')
return JsonResponse({'success': False, 'message': f"rootly incident Handling failed"}, status=500)

@csrf_exempt
@api_view(['POST'])
@authentication_classes([AccountApiTokenAuthentication])
@api_auth_check
def zenuty_handle_incidents(request_message: HttpRequest) -> JsonResponse:
try:
data = request_message.data
handle_zd_incident(data)
return JsonResponse({'success': True, 'message': 'zenduty incident Handling sucessfull'}, status=200)
except Exception as e:
logger.error(f'Error handling zenduty incident: {str(e)}')
return JsonResponse({'success': False, 'message': f"zenduty incident Handling failed"}, status=500)
Loading

0 comments on commit af483e0

Please sign in to comment.