Skip to content

Commit

Permalink
Merge pull request #83 from TeskaLabs/Enhancement/Optional-Services
Browse files Browse the repository at this point in the history
Enhancement: Optional services
  • Loading branch information
mithunbharadwaj authored Dec 11, 2024
2 parents 2836e87 + 10ab079 commit 70cdba8
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 111 deletions.
2 changes: 1 addition & 1 deletion asabiris/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def __init__(self, args=None):
self.SMSOutputService = SMSOutputService(self)
self.SendSMSOrchestrator = SendSMSOrchestrator(self)
else:
self.SendMSTeamsOrchestrator = None
self.SendSMSOrchestrator = None


# Orchestrators
Expand Down
199 changes: 91 additions & 108 deletions asabiris/handlers/kafkahandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def __init__(self, app, service_name="KafkaHandler"):
super().__init__(app, service_name)
self.Task = None
self.JinjaService = app.get_service("JinjaService")
# output service's
# Output services
try:
topic = check_config(asab.Config, "kafka", "topic")
group_id = check_config(asab.Config, "kafka", "group_id")
Expand Down Expand Up @@ -83,7 +83,6 @@ async def initialize(self, app):

self.Task = asyncio.ensure_future(self.consume(), loop=self.App.Loop)


async def finalize(self, app):
await self.Consumer.stop()
if self.Task.exception() is not None:
Expand All @@ -109,116 +108,112 @@ async def dispatch(self, msg):
try:
msg_type = msg.pop("type", "<missing>")
except (AttributeError, Exception) as e:
L.warning("Error sending notification from kafka. Reason : {}".format(str(e)))
L.warning("Error extracting message type: {}".format(str(e)))
return

if msg_type == "email":
try:
KafkaHandler.ValidationSchemaMail(msg)
except fastjsonschema.exceptions.JsonSchemaException as e:
L.warning("Invalid email notification format: {}".format(e))
return
try:
await self.send_email(msg)
except ASABIrisError as e:
# if it is a server error do not send notification.
server_errors = [
ErrorCode.SMTP_CONNECTION_ERROR,
ErrorCode.SMTP_AUTHENTICATION_ERROR,
ErrorCode.SMTP_RESPONSE_ERROR,
ErrorCode.SMTP_SERVER_DISCONNECTED,
ErrorCode.SMTP_GENERIC_ERROR,
ErrorCode.GENERAL_ERROR
]
if e.ErrorCode in server_errors:
L.warning("Unable to dispatch email: Explanation {}".format(e.TechMessage))
return
else:
# Handle other errors using handle_exception function
await self.handle_exception(e.TechMessage, 'email', msg)
except Exception as e:
# Handle any other unexpected exceptions using handle_exception function
await self.handle_exception(e, 'email', msg)
await self.handle_email(msg)

elif msg_type == "slack":
try:
KafkaHandler.ValidationSchemaSlack(msg)
except fastjsonschema.exceptions.JsonSchemaException as e:
L.warning("Invalid slack notification format: {}".format(e))
if self.App.SendSlackOrchestrator is None:
L.warning("Slack service is not configured. Discarding notification.")
return

try:
if self.App.SendSlackOrchestrator is not None:
await self.App.SendSlackOrchestrator.send_to_slack(msg)
else:
L.warning("Slack is not configured, a notification is discarded")
return
except ASABIrisError as e:
# if it is a server error do not send notification.
if e.ErrorCode == ErrorCode.SLACK_API_ERROR:
L.warning("Notification to Slack unsuccessful: Explanation: {}".format(e.TechMessage))
return
else:
# Handle other errors using handle_exception function
await self.handle_exception(e.TechMessage, 'slack')
except Exception as e:
# Handle any other unexpected exceptions using handle_exception function
await self.handle_exception(e, 'slack')
await self.handle_slack(msg)

elif msg_type == "msteams":
try:
KafkaHandler.ValidationSchemaMSTeams(msg)
except fastjsonschema.exceptions.JsonSchemaException as e:
L.warning("Invalid notification format: {}".format(e))
if self.App.SendMSTeamsOrchestrator is None:
L.warning("MS Teams service is not configured. Discarding notification.")
return
try:
if self.App.SendMSTeamsOrchestrator is not None:
await self.App.SendMSTeamsOrchestrator.send_to_msteams(msg)
else:
L.warning("MS Teams is not configured, a notification is discarded")
return
except ASABIrisError as e:
# if it is a server error do not send notification.
if e.ErrorCode == ErrorCode.SERVER_ERROR:
L.warning("Notification to MSTeams unsuccessful: Explanation: {}".format(e.TechMessage))
return
else:
# Handle other errors using handle_exception function
await self.handle_exception(e.TechMessage, 'msteams')
except Exception as e:
# Handle any other unexpected exceptions using handle_exception function
await self.handle_exception(e, 'msteams')
await self.handle_msteams(msg)

elif msg_type == "sms":
try:
KafkaHandler.ValidationSchemaSMS(msg)
except fastjsonschema.exceptions.JsonSchemaException as e:
L.warning("Invalid notification format: {}".format(e))
if self.App.SendSMSOrchestrator is None:
L.warning("SMS service is not configured. Discarding notification.")
return
try:
if self.App.SendSMSOrchestrator is not None:
await self.App.SendSMSOrchestrator.send_sms(msg)
else:
L.warning("SMS is not configured, a notification is discarded")
return
except ASABIrisError as e:
# if it is a server error do not send notification.
if e.ErrorCode == ErrorCode.SERVER_ERROR:
L.warning("Notification to SMS unsuccessful: Explanation: {}".format(e.TechMessage))
return
else:
# Handle other errors using handle_exception function
await self.handle_exception(e.TechMessage, 'sms', msg)
except Exception as e:
# Handle any other unexpected exceptions using handle_exception function
await self.handle_exception(e, 'sms', msg)
await self.handle_sms(msg)

else:
L.warning(
"Notification sending failed: Unsupported message type '{}'. "
"Supported types are 'email', 'slack', 'msteams', and 'sms'. ".format(msg_type)
"Supported types are 'email', 'slack', 'msteams', and 'sms'.".format(msg_type)
)

async def handle_email(self, msg):
try:
KafkaHandler.ValidationSchemaMail(msg)
except fastjsonschema.exceptions.JsonSchemaException as e:
L.warning("Invalid email notification format: {}".format(e))
return

try:
await self.send_email(msg)
except ASABIrisError as e:
server_errors = [
ErrorCode.SMTP_CONNECTION_ERROR,
ErrorCode.SMTP_AUTHENTICATION_ERROR,
ErrorCode.SMTP_RESPONSE_ERROR,
ErrorCode.SMTP_SERVER_DISCONNECTED,
ErrorCode.SMTP_GENERIC_ERROR,
ErrorCode.GENERAL_ERROR,
]
if e.ErrorCode in server_errors:
L.warning("Email dispatch failed: {}".format(e.TechMessage))
else:
await self.handle_exception(e.TechMessage, 'email', msg)
except Exception as e:
await self.handle_exception(e, 'email', msg)

async def handle_slack(self, msg):
try:
KafkaHandler.ValidationSchemaSlack(msg)
except fastjsonschema.exceptions.JsonSchemaException as e:
L.warning("Invalid Slack notification format: {}".format(e))
return

try:
await self.App.SendSlackOrchestrator.send_to_slack(msg)
except ASABIrisError as e:
if e.ErrorCode == ErrorCode.SLACK_API_ERROR:
L.warning("Slack notification failed: {}".format(e.TechMessage))
else:
await self.handle_exception(e.TechMessage, 'slack')
except Exception as e:
await self.handle_exception(e, 'slack')

async def handle_msteams(self, msg):
try:
KafkaHandler.ValidationSchemaMSTeams(msg)
except fastjsonschema.exceptions.JsonSchemaException as e:
L.warning("Invalid MSTeams notification format: {}".format(e))
return

try:
await self.App.SendMSTeamsOrchestrator.send_to_msteams(msg)
except ASABIrisError as e:
if e.ErrorCode == ErrorCode.SERVER_ERROR:
L.warning("MSTeams notification failed: {}".format(e.TechMessage))
else:
await self.handle_exception(e.TechMessage, 'msteams')
except Exception as e:
await self.handle_exception(e, 'msteams')

async def handle_sms(self, msg):
try:
KafkaHandler.ValidationSchemaSMS(msg)
except fastjsonschema.exceptions.JsonSchemaException as e:
L.warning("Invalid SMS notification format: {}".format(e))
return

try:
await self.App.SendSMSOrchestrator.send_sms(msg)
except ASABIrisError as e:
if e.ErrorCode == ErrorCode.SERVER_ERROR:
L.warning("SMS notification failed: {}".format(e.TechMessage))
else:
await self.handle_exception(e.TechMessage, 'sms', msg)
except Exception as e:
await self.handle_exception(e, 'sms', msg)

async def send_email(self, json_data):
await self.App.SendEmailOrchestrator.send_email(
email_from=json_data.get('from', None),
Expand All @@ -235,12 +230,10 @@ async def send_email(self, json_data):

async def handle_exception(self, exception, service_type, msg=None):
try:
# Log the problem first and then send error notification accordingly
L.warning("Encountered an issue while sending '{}'. Details: {}.".format(service_type, exception))

error_message, error_subject = self.generate_error_message(str(exception), service_type)

# Check if error_message is None
if error_message is None:
return

Expand All @@ -253,26 +246,20 @@ async def handle_exception(self, exception, service_type, msg=None):
email_subject=error_subject,
body=error_message
)
except ASABIrisError as e:
L.info("Error notification to email unsuccessful: Explanation: {}".format(e.TechMessage))
except Exception:
L.exception("Error notification to email unsuccessful.")

elif service_type == 'slack':
try:
L.log(asab.LOG_NOTICE, "Sending error notification to slack.")
L.log(asab.LOG_NOTICE, "Sending error notification to Slack.")
await self.App.SlackOutputService.send_message(None, error_message)
except ASABIrisError as e:
L.info("Error notification to Slack unsuccessful: Explanation: {}".format(e.TechMessage))
except Exception:
L.exception("Error notification to Slack unsuccessful.")

elif service_type == 'msteams':
try:
L.log(asab.LOG_NOTICE, "Sending error notification to MSTeams.")
await self.App.MSTeamsOutputService.send(error_message)
except ASABIrisError as e:
L.info("Error notification to MSTeams unsuccessful: Explanation: {}".format(e.TechMessage))
except Exception:
L.exception("Error notification to MSTeams unsuccessful.")

Expand All @@ -282,14 +269,10 @@ async def handle_exception(self, exception, service_type, msg=None):
msg_copy = msg.copy()
msg_copy['message_body'] = error_message
await self.App.SMSOutputService.send(msg_copy)
except ASABIrisError as e:
L.info("Error notification to SMS unsuccessful: Explanation: {}".format(e.TechMessage))
except Exception:
L.exception("Error notification to SMS unsuccessful.")


except Exception:
# Log any unexpected exceptions that might occur
L.exception("An unexpected error occurred while sending error message for {}.".format(service_type))

def generate_error_message(self, specific_error: str, service_type: str):
Expand Down Expand Up @@ -326,13 +309,13 @@ def generate_error_message(self, specific_error: str, service_type: str):
"Best regards,\nYour Team"
).format(specific_error, timestamp)
return error_message, None

elif service_type == 'sms':
error_message = (
"Hello! Issue processing your request: {}. Please check and retry. Time: {} UTC."
).format(specific_error[:50], timestamp) # Truncate specific_error if necessary
return error_message, None

except Exception:
# Log any unexpected exceptions that might occur while generating error message
L.exception("An unexpected error occurred while generating error message.")
return None, None
27 changes: 27 additions & 0 deletions asabiris/handlers/webhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,15 @@ async def send_slack(self, request, *, json_data):
---
tags: ['Send alerts']
"""
if self.App.SendSlackOrchestrator is None:
L.info("Slack orchestrator is not initialized. This feature is optional and not configured.")
return aiohttp.web.json_response(
{
"result": "FAILED",
"error": "Slack service is not configured."
},
status=400
)

try:
await self.App.SendSlackOrchestrator.send_to_slack(json_data)
Expand Down Expand Up @@ -208,6 +217,15 @@ async def send_msteams(self, request, *, json_data):
---
tags: ['Send MS Teams']
"""
if self.App.SendMSTeamsOrchestrator is None:
L.info("MSTeams orchestrator is not initialized. This feature is optional and not configured.")
return aiohttp.web.json_response(
{
"result": "FAILED",
"error": "MSTeams service is not configured."
},
status=400
)

try:
await self.App.SendMSTeamsOrchestrator.send_to_msteams(json_data)
Expand Down Expand Up @@ -337,6 +355,15 @@ async def send_sms(self, request, *, json_data):
---
```
"""
if self.App.SendSMSOrchestrator is None:
L.info("SMS orchestrator is not initialized. This feature is optional and not configured.")
return aiohttp.web.json_response(
{
"result": "FAILED",
"error": "SMS service is not configured."
},
status=400
)
# Render a body
try:
await self.App.SendSMSOrchestrator.send_sms(json_data)
Expand Down
4 changes: 2 additions & 2 deletions qa.md
Original file line number Diff line number Diff line change
Expand Up @@ -1545,8 +1545,8 @@ EXPECTED RESPONSE:
{"type":"sms", "phone": "123456789", "body":{"template":"/Templates/SMS/hello.md", "params":{"message": "I am testing a template" }}}
'SMS unsupportes'
{"type":"SMS", "body":{"template":"/Templates/SMS/hello.md", "params":{"name": "I am testing a template", "error": "None" }}}
{"type":"sms", "body":{"template":"/Templates/SMS/hello.md", "params":{"name": "I am testing a template", "error": "None" }}}
{"type":"SMS", "body":{"template":"/Templates/SMSS/hello.md", "params":{"name": "I am testing a template", "error": "None" }}}
{"type":"sms", "body":{"template":"/Templates/SMS/hello.mTR", "params":{"name": "I am testing a template", "error": "None" }}}
```


Expand Down

0 comments on commit 70cdba8

Please sign in to comment.