Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix dynamic templates personalisation #597

Closed
28 changes: 28 additions & 0 deletions examples/helpers/mail/mail_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,34 @@ def send_kitchen_sink():
print(response.body)


def dynamic_template_usage():
"""
Sample usage of dynamic (handlebars) transactional templates.
To make this work, you should have dynamic template created within your
SendGrid account.
In this example the template may look like:
<p>Hello {{name}}! Here is {{foo}} and also {{extra}}!<p>
"""
sg = SendGridAPIClient(apikey=os.environ.get('SENDGRID_API_KEY'))
from_email = Email("[email protected]")
subject = "I'm replacing the subject tag"
to_email = Email("[email protected]")
content = Content("text/html", "I'm replacing the <strong>body tag</strong>")
mail = Mail(from_email, subject, to_email, content)

p = Personalization()
p.add_dynamic_template_data(DynamicTemplateTag("name", "Example User"))
p.add_dynamic_template_data(DynamicTemplateTag("foo", "bar"))
p.add_dynamic_template_data(DynamicTemplateTag("extra", "SG rocks!"))
mail.template_id = "13b8f94f-bcae-4ec6-b752-70d6cb59f932"
mail.add_personalization(p)

response = sg.client.mail.send.post(request_body=mail.get())
print(response.status_code)
print(response.body)
print(response.headers)


# this will actually send an email
send_hello_email()

Expand Down
1 change: 1 addition & 0 deletions sendgrid/helpers/mail/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
from .substitution import Substitution
from .tracking_settings import TrackingSettings
from .validators import ValidateAPIKey
from .dynamic_template_tag import DynamicTemplateTag
43 changes: 43 additions & 0 deletions sendgrid/helpers/mail/dynamic_template_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
class DynamicTemplateTag(object):
"""A dynamic template tag can be used to be applied to the text and HTML contents of
the body of your email, as well as in the Subject and Reply-To parameters.
"""

def __init__(self, key=None, value=None):
"""Create a DynamicTemplateTag with the given key and value.

:param key: Text to be replaced with "value" param
:type key: string, optional
:param value: Value to substitute into email, can be any JSON serializable object
:type value: any
"""
self._key = key
self._value = value

@property
def key(self):
return self._key

@key.setter
def key(self, value):
self._key = value

@property
def value(self):
return self._value

@value.setter
def value(self, value):
self._value = value

def get(self):
"""
Get a JSON-ready representation of this Substitution.

:returns: This DynamicTemplateTag, ready for use in a request body.
:rtype: dict
"""
dynamic_template_tag = dict()
if self.key is not None and self.value is not None:
dynamic_template_tag[self.key] = self.value
return dynamic_template_tag
26 changes: 26 additions & 0 deletions sendgrid/helpers/mail/personalization.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from copy import deepcopy

class Personalization(object):
"""A Personalization defines who should receive an individual message and
how that message should be handled.
Expand All @@ -13,6 +15,7 @@ def __init__(self):
self._substitutions = []
self._custom_args = []
self._send_at = None
self._dynamic_template_data = dict()

@property
def tos(self):
Expand Down Expand Up @@ -158,6 +161,25 @@ def send_at(self):
def send_at(self, value):
self._send_at = value

@property
def dynamic_template_data(self):
"""The DynamicTemplateData that will be carried along with this Personalization.

:rtype: dict
"""
return self._dynamic_template_data

@dynamic_template_data.setter
def dynamic_template_data(self, value):
self._dynamic_template_data = value

def add_dynamic_template_data(self, dynamic_template_data):
"""Append item to DynamicTemplateData

:type dynamic_template_data: DynamicTemplateTag
"""
self._dynamic_template_data.update(dynamic_template_data.get())

def get(self):
"""
Get a JSON-ready representation of this Personalization.
Expand Down Expand Up @@ -198,4 +220,8 @@ def get(self):

if self.send_at is not None:
personalization["send_at"] = self.send_at

if self.dynamic_template_data:
personalization['dynamic_template_data'] = deepcopy(self.dynamic_template_data)

return personalization
48 changes: 45 additions & 3 deletions test/test_mail.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
SubscriptionTracking,
Substitution,
TrackingSettings,
ValidateAPIKey
)
ValidateAPIKey,
DynamicTemplateTag)

try:
import unittest2 as unittest
Expand Down Expand Up @@ -73,7 +73,7 @@ def test_sendgridAPIKey(self):
)

#Exception should be thrown
except Exception as e:
except Exception:
pass

#Exception not thrown
Expand Down Expand Up @@ -562,3 +562,45 @@ def test_disable_tracking(self):
def test_directly_setting_substitutions(self):
personalization = Personalization()
personalization.substitutions = [{'a': 0}]

def test_dynamic_template_data(self):
"""Test for minimum requirements, redundancy and nested items"""
mail = Mail()

mail.from_email = Email("[email protected]")

mail.subject = "Hello World from the SendGrid Python Library"

personalization = Personalization()
personalization.add_to(Email("[email protected]"))
personalization.add_dynamic_template_data(DynamicTemplateTag('foo', 'bar'))
personalization.add_dynamic_template_data(DynamicTemplateTag('one', 'more'))
personalization.add_dynamic_template_data(DynamicTemplateTag('foo', 'bar1'))
personalization.add_dynamic_template_data(DynamicTemplateTag('items', [{"total": "100"}, {"more": "things"}]))
mail.add_personalization(personalization)

mail.add_content(Content("text/plain", "some text here"))
mail.add_content(
Content(
"text/html",
"<html><body>some text here</body></html>"))

self.assertEqual(
json.dumps(
mail.get(),
sort_keys=True),
'{"content": [{"type": "text/plain", "value": "some text here"}, '
'{"type": "text/html", '
'"value": "<html><body>some text here</body></html>"}], '
'"from": {"email": "[email protected]"}, '
'"personalizations": [{'
'"dynamic_template_data": {"foo": "bar1", "items": [{"total": "100"}, {"more": "things"}], "one": "more"}, '
'"to": [{"email": "[email protected]"}]'
'}], '
'"subject": "Hello World from the SendGrid Python Library"'
'}'
)

self.assertTrue(isinstance(str(mail), str))


1 change: 1 addition & 0 deletions use_cases/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ This directory provides examples for specific use cases of this library. Please
* [Attachment](attachment.md)
* [Transactional Templates](transational_templates.md)
* [Integrate with Slack Events API](slack_event_api_integration.md)
* [Dynamic Transactional Templates](dynamic_transactional_templates.md)

### Library Features
* [Error Handling](error_handling.md)
133 changes: 133 additions & 0 deletions use_cases/dynamic_transactional_templates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Dynamic Transactional Templates

For this example, we assume you have created a [dynamic transactional template](https://sendgrid.com/docs/User_Guide/Transactional_Templates/Create_and_edit_dynamic_transactional_templates.html). Following is the template content we used for testing.

Template ID (replace with your own):

```text
13b8f94f-bcae-4ec6-b752-70d6cb59f932
```

Email Subject:

```text
{{subject}}
```

Template Body:

```html
<html>
<head>
<title></title>
</head>
<body>
Hello {{name}},
<br /><br/>
I'm glad you are trying out the dynamic template feature!
<br /><br/>
<p align="center">
<table border="1" cellspacing="0" cellpadding="10">
<tr>
<td colspan="2" align="center" bgcolor="#D3D3D3">
<strong><pre>Values provided</pre></strong>
</td>
</tr>
{{#each this}}
<tr>
<td bgcolor="#F8F8F8"><strong><pre>{{@key}}</pre></strong></td>
<td bgcolor="#FFFFFF"><pre>{{this}}</pre></td>
</tr>
{{/each}}
</table>
</p>
<br /><br/>
I hope you are having a great day :)
<br /><br/>
</body>
</html>
```

## With Mail Helper Class

```python
import sendgrid
import os
from sendgrid.helpers.mail import Email, Content, DynamicTemplateTag, Mail
try:
# Python 3
import urllib.request as urllib
except ImportError:
# Python 2
import urllib2 as urllib

sg = sendgrid.SendGridAPIClient(apikey=os.environ.get('SENDGRID_API_KEY'))
from_email = Email("[email protected]")
subject = "I'm replacing the subject tag"
to_email = Email("[email protected]")
content = Content("text/html", "I'm replacing the <strong>body tag</strong>")
mail = Mail(from_email, subject, to_email, content)

p = Personalization()
p.add_dynamic_template_data(DynamicTemplateTag("name", "Example User"))
p.add_dynamic_template_data(DynamicTemplateTag("foo", "bar"))
p.add_dynamic_template_data(DynamicTemplateTag("extra", "SG rocks!"))
mail.template_id = "13b8f94f-bcae-4ec6-b752-70d6cb59f932"
mail.add_personalization(p)

try:
response = sg.client.mail.send.post(request_body=mail.get())
except urllib.HTTPError as e:
print (e.read())
exit()
print(response.status_code)
print(response.body)
print(response.headers)
```

## Without Mail Helper Class

```python
import sendgrid
import os
try:
# Python 3
import urllib.request as urllib
except ImportError:
# Python 2
import urllib2 as urllib

sg = sendgrid.SendGridAPIClient(apikey=os.environ.get('SENDGRID_API_KEY'))
data = {
"personalizations": [
{
"to": [
{
"email": "[email protected]"
}
],
"dynamic_template_data": {"extra": "SG rocks!", "foo": "bar", "name": "Example User"},
"subject": "I'm replacing the subject tag"
},
],
"from": {
"email": "[email protected]"
},
"content": [
{
"type": "text/html",
"value": "I'm replacing the <strong>body tag</strong>"
}
],
"template_id": "13b8f94f-bcae-4ec6-b752-70d6cb59f932"
}
try:
response = sg.client.mail.send.post(request_body=data)
except urllib.HTTPError as e:
print (e.read())
exit()
print(response.status_code)
print(response.body)
print(response.headers)
```