Skip to content

Commit 9e83208

Browse files
authored
New custom dyanmic templates (#5)
* Adding the ability to specify a custom dynamic template with dynamic template data using the SendGrid API. * huge refactor to test theory related to content hack. * update the README with some usage docs * a little clarity on the README
1 parent 56a1444 commit 9e83208

File tree

6 files changed

+272
-68
lines changed

6 files changed

+272
-68
lines changed

README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,61 @@ private def raise_missing_key_message
4242
end
4343
```
4444

45+
### Sending Dynamic Template emails
46+
47+
SendGrid allows you to use [Dynamic Transactional Templates](https://docs.sendgrid.com/ui/sending-email/how-to-send-an-email-with-dynamic-transactional-templates) when
48+
sending your emails. These templates are designed and created inside of the
49+
SendGrid website.
50+
51+
Define a `template_id`, and `dynamic_template_data` method in your
52+
email class to use the dynamic template.
53+
54+
1. Login to SendGrid
55+
2. Select Email API > Dynamic Templates
56+
3. Create a new template
57+
4. Copy the "Template-ID" value for that template.
58+
5. Update your email class
59+
60+
```crystal
61+
# Using built-in templates
62+
class WelcomeEmail < BaseEmail
63+
def initialize(@user : User)
64+
end
65+
66+
to @user
67+
subject "Welcome - Confirm Your Email"
68+
templates html, text
69+
end
70+
```
71+
72+
```crystal
73+
# Using dynamic templates
74+
class WelcomeEmail < BaseEmail
75+
def initialize(@user : User)
76+
end
77+
78+
# This must be the String value of your ID
79+
def template_id
80+
"d-12345abcd6543dcbaffeedd1122aabb"
81+
end
82+
83+
# This is optional. Define a Hash with your
84+
# custom handlebars variables
85+
def dynamic_template_data
86+
{
87+
"username" => @user.username,
88+
"confirmEmailUrl" => "https://myapp.com/confirm?token=..."
89+
}
90+
end
91+
92+
to @user
93+
subject "Welcome - Confirm Your Email"
94+
end
95+
```
96+
97+
NOTE: SendGrid requires you to either define `template_id` or use the `templates` macro
98+
to generate an email body content.
99+
45100
## Contributing
46101

47102
1. Fork it (<https://github.com/your-github-user/carbon_sendgrid_adapter/fork>)

spec/carbon_sendgrid_adapter_spec.cr

Lines changed: 66 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,42 @@ describe Carbon::SendGridAdapter do
1616
end
1717
{% end %}
1818

19+
describe "errors" do
20+
it "raises SendGridInvalidTemplateError if no template is defined in params" do
21+
expect_raises(Carbon::SendGridInvalidTemplateError) do
22+
email = FakeEmail.new
23+
Carbon::SendGridAdapter::Email.new(email, api_key: "fake_key").params
24+
end
25+
end
26+
end
27+
1928
describe "params" do
2029
it "is not sandboxed by default" do
21-
params_for[:mail_settings][:sandbox_mode][:enable].should be_false
30+
settings = params_for(text_body: "0")["mail_settings"].as(NamedTuple)
31+
settings[:sandbox_mode][:enable].should be_false
2232
end
2333

2434
it "handles headers" do
2535
headers = {"Header1" => "value1", "Header2" => "value2"}
26-
params = params_for(headers: headers)
36+
params = params_for(headers: headers, text_body: "0")
2737

28-
params[:headers].should eq headers
38+
params["headers"].should eq headers
2939
end
3040

3141
it "sets extracts reply-to header" do
3242
headers = {"reply-to" => "[email protected]", "Header" => "value"}
33-
params = params_for(headers: headers)
43+
params = params_for(headers: headers, text_body: "0")
3444

35-
params[:headers].should eq({"Header" => "value"})
36-
params[:reply_to].should eq({email: "[email protected]"})
45+
params["headers"].should eq({"Header" => "value"})
46+
params["reply_to"].should eq({"email" => "[email protected]"})
3747
end
3848

3949
it "sets extracts reply-to header regardless of case" do
4050
headers = {"Reply-To" => "[email protected]", "Header" => "value"}
41-
params = params_for(headers: headers)
51+
params = params_for(headers: headers, text_body: "0")
4252

43-
params[:headers].should eq({"Header" => "value"})
44-
params[:reply_to].should eq({email: "[email protected]"})
53+
params["headers"].should eq({"Header" => "value"})
54+
params["reply_to"].should eq({"email" => "[email protected]"})
4555
end
4656

4757
it "sets personalizations" do
@@ -55,58 +65,83 @@ describe Carbon::SendGridAdapter do
5565
recipient_params = params_for(
5666
to: [to_without_name, to_with_name],
5767
cc: [cc_without_name, cc_with_name],
58-
bcc: [bcc_without_name, bcc_with_name]
59-
)[:personalizations].first
68+
bcc: [bcc_without_name, bcc_with_name],
69+
text_body: "0"
70+
)["personalizations"].as(Array).first
6071

61-
recipient_params[:to].should eq(
72+
recipient_params["to"].should eq(
6273
[
63-
{name: nil, email: "[email protected]"},
64-
{name: "Jimmy", email: "[email protected]"},
74+
{"email" => "[email protected]"},
75+
{"name" => "Jimmy", "email" => "[email protected]"},
6576
]
6677
)
67-
recipient_params[:cc].should eq(
78+
recipient_params["cc"].should eq(
6879
[
69-
{name: nil, email: "[email protected]"},
70-
{name: "Kim", email: "[email protected]"},
80+
{"email" => "[email protected]"},
81+
{"name" => "Kim", "email" => "[email protected]"},
7182
]
7283
)
73-
recipient_params[:bcc].should eq(
84+
recipient_params["bcc"].should eq(
7485
[
75-
{name: nil, email: "[email protected]"},
76-
{name: "James", email: "[email protected]"},
86+
{"email" => "[email protected]"},
87+
{"name" => "James", "email" => "[email protected]"},
7788
]
7889
)
7990
end
8091

8192
it "removes empty recipients from personalizations" do
8293
to_without_name = Carbon::Address.new("[email protected]")
8394

84-
recipient_params = params_for(to: [to_without_name])[:personalizations].first
95+
recipient_params = params_for(to: [to_without_name], text_body: "0")["personalizations"].as(Array).first
8596

86-
recipient_params.keys.should eq [:to]
87-
recipient_params[:to].should eq [{name: nil, email: "[email protected]"}]
97+
recipient_params.keys.should eq ["to"]
98+
recipient_params["to"].should eq [{"email" => "[email protected]"}]
8899
end
89100

90101
it "sets the subject" do
91-
params_for(subject: "My subject")[:subject].should eq "My subject"
102+
params_for(subject: "My subject", text_body: "0")["subject"].should eq "My subject"
92103
end
93104

94105
it "sets the from address" do
95106
address = Carbon::Address.new("[email protected]")
96-
params_for(from: address)[:from].should eq({email: "[email protected]"}.to_h)
107+
params_for(from: address, text_body: "0")["from"].should eq({"email" => "[email protected]"})
97108

98109
address = Carbon::Address.new("Sally", "[email protected]")
99-
params_for(from: address)[:from].should eq({name: "Sally", email: "[email protected]"}.to_h)
110+
params_for(from: address, text_body: "0")["from"].should eq({"name" => "Sally", "email" => "[email protected]"})
100111
end
101112

102113
it "sets the content" do
103-
params_for(text_body: "text")[:content].should eq [{type: "text/plain", value: "text"}]
104-
params_for(html_body: "html")[:content].should eq [{type: "text/html", value: "html"}]
105-
params_for(text_body: "text", html_body: "html")[:content].should eq [
106-
{type: "text/plain", value: "text"},
107-
{type: "text/html", value: "html"},
114+
params_for(text_body: "text")["content"].should eq [{"type" => "text/plain", "value" => "text"}]
115+
params_for(html_body: "html")["content"].should eq [{"type" => "text/html", "value" => "html"}]
116+
params_for(text_body: "text", html_body: "html")["content"].should eq [
117+
{"type" => "text/plain", "value" => "text"},
118+
{"type" => "text/html", "value" => "html"},
108119
]
109120
end
121+
122+
it "allows for a custom template_id" do
123+
custom_email = CustomTemplateEmail.new
124+
params = Carbon::SendGridAdapter::Email.new(custom_email, api_key: "fake_key").params
125+
126+
params["template_id"].should eq("welcome-abc-123")
127+
128+
normal_email = FakeEmail.new(text_body: "0")
129+
params = Carbon::SendGridAdapter::Email.new(normal_email, api_key: "fake_key").params
130+
131+
params.has_key?("template_id").should eq(false)
132+
end
133+
134+
it "allows for custom template data" do
135+
custom_email = CustomTemplateEmail.new
136+
params = Carbon::SendGridAdapter::Email.new(custom_email, api_key: "fake_key").params
137+
138+
params["personalizations"].as(Array).first["dynamic_template_data"].should_not eq(nil)
139+
140+
normal_email = FakeEmail.new(text_body: "0")
141+
params = Carbon::SendGridAdapter::Email.new(normal_email, api_key: "fake_key").params
142+
143+
params["personalizations"].as(Array).first.has_key?("dynamic_template_data").should eq(false)
144+
end
110145
end
111146
end
112147

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
class CustomTemplateEmail < Carbon::Email
2+
def initialize(
3+
@from = Carbon::Address.new("[email protected]"),
4+
@to = [] of Carbon::Address,
5+
@cc = [] of Carbon::Address,
6+
@bcc = [] of Carbon::Address,
7+
@headers = {} of String => String,
8+
@subject = "subject",
9+
@text_body : String? = nil,
10+
@html_body : String? = nil
11+
)
12+
end
13+
14+
def template_id
15+
"welcome-abc-123"
16+
end
17+
18+
def dynamic_template_data
19+
{
20+
"total" => "$ 239.85",
21+
"items" => [
22+
{
23+
"text" => "New Line Sneakers",
24+
"image" => "https://marketing-image-production.s3.amazonaws.com/uploads/8dda1131320a6d978b515cc04ed479df259a458d5d45d58b6b381cae0bf9588113e80ef912f69e8c4cc1ef1a0297e8eefdb7b270064cc046b79a44e21b811802.png",
25+
"price" => "$ 79.95",
26+
},
27+
{
28+
"text" => "Old Line Sneakers",
29+
"image" => "https://marketing-image-production.s3.amazonaws.com/uploads/3629f54390ead663d4eb7c53702e492de63299d7c5f7239efdc693b09b9b28c82c924225dcd8dcb65732d5ca7b7b753c5f17e056405bbd4596e4e63a96ae5018.png",
30+
"price" => "$ 79.95",
31+
},
32+
{
33+
"text" => "Blue Line Sneakers",
34+
"image" => "https://marketing-image-production.s3.amazonaws.com/uploads/00731ed18eff0ad5da890d876c456c3124a4e44cb48196533e9b95fb2b959b7194c2dc7637b788341d1ff4f88d1dc88e23f7e3704726d313c57f350911dd2bd0.png",
35+
"price" => "$ 79.95",
36+
},
37+
],
38+
"receipt" => true,
39+
"name" => "Sample Name",
40+
"address01" => "1234 Fake St.",
41+
"address02" => "Apt. 123",
42+
"city" => "Place",
43+
"state" => "CO",
44+
"zip" => "80202",
45+
}
46+
end
47+
48+
from @from
49+
to @to
50+
cc @cc
51+
bcc @bcc
52+
subject @subject
53+
end

0 commit comments

Comments
 (0)