diff --git a/docs/docs/administration/settings/messaging.mdx b/docs/docs/administration/settings/messaging.mdx new file mode 100644 index 000000000000..90411744ec67 --- /dev/null +++ b/docs/docs/administration/settings/messaging.mdx @@ -0,0 +1,50 @@ +# Messaging + +Dispatch supports sending email notifications to participants of, for example, an incident. + +## Notification templates + +Templates for emails are [part](https://github.com/Netflix/dispatch/tree/master/src/dispatch/messaging/email/templates) of Dispatch +and are [Jinja](https://jinja.palletsprojects.com/) templates that during runtime are compiled into [MJML](https://mjml.io/) format. + +There is a way to customize these templates. To do this, if you run Dispatch with [Docker Compose](https://github.com/Netflix/dispatch-docker/), +mount a volume with a customized templates dir as part of the docker compose: + +``` + web: + image: dispatch-local + ... + volumes: + - "../dispatch-templates/messaging-email-templates:/usr/local/lib/python3.11/site-packages/dispatch/messaging/email/templates" +``` + +Such approach allows you to customize the common template for *all projects*. + +You can also "patch" the templates *per project*. Create a folder per project (identified by project id): + +``` +dispatch/messaging/email/templates/project_id//base.mjml +``` + +This will be used at the first place if exists, +otherwise the resolution process will gracefully fall back to the default template: + +``` +dispatch/messaging/email/templates/base.mjml +``` + + +## Markdown in the notifications + +:::warning +Watch out for security implications related to unescaped HTML that may propagate through the system. +::: + +By default, notification text is just a plain text with special characters and HTML escaped. + +It is possible, however, to enable Markdown syntax with a server setting: + +``` +DISPATCH_MARKDOWN_IN_INCIDENT_DESC=True +DISPATCH_ESCAPE_HTML=False +``` diff --git a/src/dispatch/messaging/email/utils.py b/src/dispatch/messaging/email/utils.py index bccdf4eefe82..063b6208059d 100644 --- a/src/dispatch/messaging/email/utils.py +++ b/src/dispatch/messaging/email/utils.py @@ -3,6 +3,7 @@ import subprocess import tempfile +import jinja2.exceptions from dispatch.config import MJML_PATH @@ -20,7 +21,7 @@ log = logging.getLogger(__name__) -def get_template(message_type: MessageType): +def get_template(message_type: MessageType, project_id: int): """Fetches the correct template based on the message type.""" template_map = { MessageType.incident_executive_report: ("executive_report.mjml", None), @@ -45,19 +46,27 @@ def get_template(message_type: MessageType): ), } - template_path, description = template_map.get(message_type, (None, None)) + template_key, description = template_map.get(message_type, (None, None)) - if not template_path: + if not template_key: raise Exception(f"Unable to determine template. MessageType: {message_type}") - return env.get_template(os.path.join("templates", template_path)), description + try: + template_path = os.path.join("templates", "project_id", f"{project_id}", template_key) + template = env.get_template(template_path) + except jinja2.exceptions.TemplateNotFound: + template_path = os.path.join("templates", template_key) + template = env.get_template(template_path) + log.debug("Resolved template path: %s", template_path) + + return template, description def create_multi_message_body( - message_template: dict, message_type: MessageType, items: list, **kwargs + message_template: dict, message_type: MessageType, items: list, project_id: int, **kwargs ): """Creates a multi message message body based on message type.""" - template, description = get_template(message_type) + template, description = get_template(message_type, project_id) master_map = [] for item in items: @@ -67,9 +76,11 @@ def create_multi_message_body( return render_html(template.render(**kwargs)) -def create_message_body(message_template: dict, message_type: MessageType, **kwargs): +def create_message_body( + message_template: dict, message_type: MessageType, project_id: int, **kwargs +): """Creates the correct message body based on message type.""" - template, description = get_template(message_type) + template, description = get_template(message_type, project_id) items_grouped_rendered = [] if kwargs.get("items_grouped"): diff --git a/src/dispatch/plugins/dispatch_google/gmail/plugin.py b/src/dispatch/plugins/dispatch_google/gmail/plugin.py index 8c5641047bfb..4fd84c974df3 100644 --- a/src/dispatch/plugins/dispatch_google/gmail/plugin.py +++ b/src/dispatch/plugins/dispatch_google/gmail/plugin.py @@ -103,10 +103,12 @@ def send( cc = kwargs["cc"] if not items: - message_body = create_message_body(notification_template, notification_type, **kwargs) + message_body = create_message_body( + notification_template, notification_type, self.project_id, **kwargs + ) else: message_body = create_multi_message_body( - notification_template, notification_type, items, **kwargs + notification_template, notification_type, items, self.project_id, **kwargs ) html_message = create_html_message(