Skip to content

Commit 5a05be2

Browse files
authored
Allow customizing notification templates per project (Netflix#3369)
* project specific templates
1 parent 4416b2d commit 5a05be2

File tree

3 files changed

+73
-10
lines changed

3 files changed

+73
-10
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Messaging
2+
3+
Dispatch supports sending email notifications to participants of, for example, an incident.
4+
5+
## Notification templates
6+
7+
Templates for emails are [part](https://github.com/Netflix/dispatch/tree/master/src/dispatch/messaging/email/templates) of Dispatch
8+
and are [Jinja](https://jinja.palletsprojects.com/) templates that during runtime are compiled into [MJML](https://mjml.io/) format.
9+
10+
There is a way to customize these templates. To do this, if you run Dispatch with [Docker Compose](https://github.com/Netflix/dispatch-docker/),
11+
mount a volume with a customized templates dir as part of the docker compose:
12+
13+
```
14+
web:
15+
image: dispatch-local
16+
...
17+
volumes:
18+
- "../dispatch-templates/messaging-email-templates:/usr/local/lib/python3.11/site-packages/dispatch/messaging/email/templates"
19+
```
20+
21+
Such approach allows you to customize the common template for *all projects*.
22+
23+
You can also "patch" the templates *per project*. Create a folder per project (identified by project id):
24+
25+
```
26+
dispatch/messaging/email/templates/project_id/<project_id>/base.mjml
27+
```
28+
29+
This will be used at the first place if exists,
30+
otherwise the resolution process will gracefully fall back to the default template:
31+
32+
```
33+
dispatch/messaging/email/templates/base.mjml
34+
```
35+
36+
37+
## Markdown in the notifications
38+
39+
:::warning
40+
Watch out for security implications related to unescaped HTML that may propagate through the system.
41+
:::
42+
43+
By default, notification text is just a plain text with special characters and HTML escaped.
44+
45+
It is possible, however, to enable Markdown syntax with a server setting:
46+
47+
```
48+
DISPATCH_MARKDOWN_IN_INCIDENT_DESC=True
49+
DISPATCH_ESCAPE_HTML=False
50+
```

src/dispatch/messaging/email/utils.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import subprocess
44
import tempfile
55

6+
import jinja2.exceptions
67
from dispatch.config import MJML_PATH
78

89

@@ -20,7 +21,7 @@
2021
log = logging.getLogger(__name__)
2122

2223

23-
def get_template(message_type: MessageType):
24+
def get_template(message_type: MessageType, project_id: int):
2425
"""Fetches the correct template based on the message type."""
2526
template_map = {
2627
MessageType.incident_executive_report: ("executive_report.mjml", None),
@@ -45,19 +46,27 @@ def get_template(message_type: MessageType):
4546
),
4647
}
4748

48-
template_path, description = template_map.get(message_type, (None, None))
49+
template_key, description = template_map.get(message_type, (None, None))
4950

50-
if not template_path:
51+
if not template_key:
5152
raise Exception(f"Unable to determine template. MessageType: {message_type}")
5253

53-
return env.get_template(os.path.join("templates", template_path)), description
54+
try:
55+
template_path = os.path.join("templates", "project_id", f"{project_id}", template_key)
56+
template = env.get_template(template_path)
57+
except jinja2.exceptions.TemplateNotFound:
58+
template_path = os.path.join("templates", template_key)
59+
template = env.get_template(template_path)
60+
log.debug("Resolved template path: %s", template_path)
61+
62+
return template, description
5463

5564

5665
def create_multi_message_body(
57-
message_template: dict, message_type: MessageType, items: list, **kwargs
66+
message_template: dict, message_type: MessageType, items: list, project_id: int, **kwargs
5867
):
5968
"""Creates a multi message message body based on message type."""
60-
template, description = get_template(message_type)
69+
template, description = get_template(message_type, project_id)
6170

6271
master_map = []
6372
for item in items:
@@ -67,9 +76,11 @@ def create_multi_message_body(
6776
return render_html(template.render(**kwargs))
6877

6978

70-
def create_message_body(message_template: dict, message_type: MessageType, **kwargs):
79+
def create_message_body(
80+
message_template: dict, message_type: MessageType, project_id: int, **kwargs
81+
):
7182
"""Creates the correct message body based on message type."""
72-
template, description = get_template(message_type)
83+
template, description = get_template(message_type, project_id)
7384

7485
items_grouped_rendered = []
7586
if kwargs.get("items_grouped"):

src/dispatch/plugins/dispatch_google/gmail/plugin.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,12 @@ def send(
103103
cc = kwargs["cc"]
104104

105105
if not items:
106-
message_body = create_message_body(notification_template, notification_type, **kwargs)
106+
message_body = create_message_body(
107+
notification_template, notification_type, self.project_id, **kwargs
108+
)
107109
else:
108110
message_body = create_multi_message_body(
109-
notification_template, notification_type, items, **kwargs
111+
notification_template, notification_type, items, self.project_id, **kwargs
110112
)
111113

112114
html_message = create_html_message(

0 commit comments

Comments
 (0)