Skip to content

Commit

Permalink
New custom dyanmic templates (#5)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
jwoertink authored Nov 17, 2021
1 parent 56a1444 commit 9e83208
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 68 deletions.
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,61 @@ private def raise_missing_key_message
end
```

### Sending Dynamic Template emails

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
sending your emails. These templates are designed and created inside of the
SendGrid website.

Define a `template_id`, and `dynamic_template_data` method in your
email class to use the dynamic template.

1. Login to SendGrid
2. Select Email API > Dynamic Templates
3. Create a new template
4. Copy the "Template-ID" value for that template.
5. Update your email class

```crystal
# Using built-in templates
class WelcomeEmail < BaseEmail
def initialize(@user : User)
end
to @user
subject "Welcome - Confirm Your Email"
templates html, text
end
```

```crystal
# Using dynamic templates
class WelcomeEmail < BaseEmail
def initialize(@user : User)
end
# This must be the String value of your ID
def template_id
"d-12345abcd6543dcbaffeedd1122aabb"
end
# This is optional. Define a Hash with your
# custom handlebars variables
def dynamic_template_data
{
"username" => @user.username,
"confirmEmailUrl" => "https://myapp.com/confirm?token=..."
}
end
to @user
subject "Welcome - Confirm Your Email"
end
```

NOTE: SendGrid requires you to either define `template_id` or use the `templates` macro
to generate an email body content.

## Contributing

1. Fork it (<https://github.com/your-github-user/carbon_sendgrid_adapter/fork>)
Expand Down
97 changes: 66 additions & 31 deletions spec/carbon_sendgrid_adapter_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,42 @@ describe Carbon::SendGridAdapter do
end
{% end %}

describe "errors" do
it "raises SendGridInvalidTemplateError if no template is defined in params" do
expect_raises(Carbon::SendGridInvalidTemplateError) do
email = FakeEmail.new
Carbon::SendGridAdapter::Email.new(email, api_key: "fake_key").params
end
end
end

describe "params" do
it "is not sandboxed by default" do
params_for[:mail_settings][:sandbox_mode][:enable].should be_false
settings = params_for(text_body: "0")["mail_settings"].as(NamedTuple)
settings[:sandbox_mode][:enable].should be_false
end

it "handles headers" do
headers = {"Header1" => "value1", "Header2" => "value2"}
params = params_for(headers: headers)
params = params_for(headers: headers, text_body: "0")

params[:headers].should eq headers
params["headers"].should eq headers
end

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

params[:headers].should eq({"Header" => "value"})
params[:reply_to].should eq({email: "[email protected]"})
params["headers"].should eq({"Header" => "value"})
params["reply_to"].should eq({"email" => "[email protected]"})
end

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

params[:headers].should eq({"Header" => "value"})
params[:reply_to].should eq({email: "[email protected]"})
params["headers"].should eq({"Header" => "value"})
params["reply_to"].should eq({"email" => "[email protected]"})
end

it "sets personalizations" do
Expand All @@ -55,58 +65,83 @@ describe Carbon::SendGridAdapter do
recipient_params = params_for(
to: [to_without_name, to_with_name],
cc: [cc_without_name, cc_with_name],
bcc: [bcc_without_name, bcc_with_name]
)[:personalizations].first
bcc: [bcc_without_name, bcc_with_name],
text_body: "0"
)["personalizations"].as(Array).first

recipient_params[:to].should eq(
recipient_params["to"].should eq(
[
{name: nil, email: "[email protected]"},
{name: "Jimmy", email: "[email protected]"},
{"email" => "[email protected]"},
{"name" => "Jimmy", "email" => "[email protected]"},
]
)
recipient_params[:cc].should eq(
recipient_params["cc"].should eq(
[
{name: nil, email: "[email protected]"},
{name: "Kim", email: "[email protected]"},
{"email" => "[email protected]"},
{"name" => "Kim", "email" => "[email protected]"},
]
)
recipient_params[:bcc].should eq(
recipient_params["bcc"].should eq(
[
{name: nil, email: "[email protected]"},
{name: "James", email: "[email protected]"},
{"email" => "[email protected]"},
{"name" => "James", "email" => "[email protected]"},
]
)
end

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

recipient_params = params_for(to: [to_without_name])[:personalizations].first
recipient_params = params_for(to: [to_without_name], text_body: "0")["personalizations"].as(Array).first

recipient_params.keys.should eq [:to]
recipient_params[:to].should eq [{name: nil, email: "[email protected]"}]
recipient_params.keys.should eq ["to"]
recipient_params["to"].should eq [{"email" => "[email protected]"}]
end

it "sets the subject" do
params_for(subject: "My subject")[:subject].should eq "My subject"
params_for(subject: "My subject", text_body: "0")["subject"].should eq "My subject"
end

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

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

it "sets the content" do
params_for(text_body: "text")[:content].should eq [{type: "text/plain", value: "text"}]
params_for(html_body: "html")[:content].should eq [{type: "text/html", value: "html"}]
params_for(text_body: "text", html_body: "html")[:content].should eq [
{type: "text/plain", value: "text"},
{type: "text/html", value: "html"},
params_for(text_body: "text")["content"].should eq [{"type" => "text/plain", "value" => "text"}]
params_for(html_body: "html")["content"].should eq [{"type" => "text/html", "value" => "html"}]
params_for(text_body: "text", html_body: "html")["content"].should eq [
{"type" => "text/plain", "value" => "text"},
{"type" => "text/html", "value" => "html"},
]
end

it "allows for a custom template_id" do
custom_email = CustomTemplateEmail.new
params = Carbon::SendGridAdapter::Email.new(custom_email, api_key: "fake_key").params

params["template_id"].should eq("welcome-abc-123")

normal_email = FakeEmail.new(text_body: "0")
params = Carbon::SendGridAdapter::Email.new(normal_email, api_key: "fake_key").params

params.has_key?("template_id").should eq(false)
end

it "allows for custom template data" do
custom_email = CustomTemplateEmail.new
params = Carbon::SendGridAdapter::Email.new(custom_email, api_key: "fake_key").params

params["personalizations"].as(Array).first["dynamic_template_data"].should_not eq(nil)

normal_email = FakeEmail.new(text_body: "0")
params = Carbon::SendGridAdapter::Email.new(normal_email, api_key: "fake_key").params

params["personalizations"].as(Array).first.has_key?("dynamic_template_data").should eq(false)
end
end
end

Expand Down
53 changes: 53 additions & 0 deletions spec/support/custom_template_email.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
class CustomTemplateEmail < Carbon::Email
def initialize(
@from = Carbon::Address.new("[email protected]"),
@to = [] of Carbon::Address,
@cc = [] of Carbon::Address,
@bcc = [] of Carbon::Address,
@headers = {} of String => String,
@subject = "subject",
@text_body : String? = nil,
@html_body : String? = nil
)
end

def template_id
"welcome-abc-123"
end

def dynamic_template_data
{
"total" => "$ 239.85",
"items" => [
{
"text" => "New Line Sneakers",
"image" => "https://marketing-image-production.s3.amazonaws.com/uploads/8dda1131320a6d978b515cc04ed479df259a458d5d45d58b6b381cae0bf9588113e80ef912f69e8c4cc1ef1a0297e8eefdb7b270064cc046b79a44e21b811802.png",
"price" => "$ 79.95",
},
{
"text" => "Old Line Sneakers",
"image" => "https://marketing-image-production.s3.amazonaws.com/uploads/3629f54390ead663d4eb7c53702e492de63299d7c5f7239efdc693b09b9b28c82c924225dcd8dcb65732d5ca7b7b753c5f17e056405bbd4596e4e63a96ae5018.png",
"price" => "$ 79.95",
},
{
"text" => "Blue Line Sneakers",
"image" => "https://marketing-image-production.s3.amazonaws.com/uploads/00731ed18eff0ad5da890d876c456c3124a4e44cb48196533e9b95fb2b959b7194c2dc7637b788341d1ff4f88d1dc88e23f7e3704726d313c57f350911dd2bd0.png",
"price" => "$ 79.95",
},
],
"receipt" => true,
"name" => "Sample Name",
"address01" => "1234 Fake St.",
"address02" => "Apt. 123",
"city" => "Place",
"state" => "CO",
"zip" => "80202",
}
end

from @from
to @to
cc @cc
bcc @bcc
subject @subject
end
Loading

0 comments on commit 9e83208

Please sign in to comment.