defmodule SendGrid.Email do @moduledoc """ Email primitive for composing emails with SendGrid's API. You can easily compose on an Email to set the fields of your email. ## Example Email.build() |> Email.add_to("test@email.com") |> Email.put_from("test2@email.com") |> Email.put_subject("Hello from Elixir") |> Email.put_text("Sent with Elixir") |> SendGrid.Mail.send() ## SendGrid Specific Features Many common features of SendGrid V3 API for transactional emails are supported. ### Templates You can use a SendGrid template by providing a template id. put_template(email, "some_template_id") ### Substitutions You can provided a key-value pair for subsititions to have text replaced. add_substitution(email, "-key-", "value") ### Scheduled Sending You can provide a Unix timestamp to have an email delivered in the future. put_send_at(email, 1409348513) ## Phoenix Views You can use Phoenix Views to set your HTML and text content of your emails. You just have to provide a view module and template name and you're good to go! Additionally, you can set a layout to render the view in with `put_phoenix_layout/2`. See `put_phoenix_template/3` for complete usage. ### Examples # Using an HTML template %Email{} |> put_phoenix_view(MyApp.Web.EmailView) |> put_phoenix_template("welcome_email.html", user: user) # Using a text template %Email{} |> put_phoenix_view(MyApp.Web.EmailView) |> put_phoenix_template("welcome_email.txt", user: user) # Using both an HTML and text template %Email{} |> put_phoenix_view(MyApp.Web.EmailView) |> put_phoenix_template(:welcome_email, user: user) # Setting the layout %Email{} |> put_phoenix_layout({MyApp.Web.EmailView, :layout}) |> put_phoenix_view(MyApp.Web.EmailView) |> put_phoenix_template(:welcome_email, user: user) ### Using a Default Phoenix View You can set a default Phoenix View to use for rendering templates. Just set the `:phoenix_view` config value. config :noizu_sendgrid, phoenix_view: MyApp.Web.EmailView ### Using a Default View Layout You can set a default layout to render the view in. Just set the `:phoenix_layout` config value. config :noizu_sendgrid, phoenix_layout: {MyApp.Web.EmailView, :layout} """ alias SendGrid.{Email, Personalization} defstruct to: nil, cc: nil, bcc: nil, from: nil, reply_to: nil, reply_to_list: nil, subject: nil, content: nil, template_id: nil, version_id: nil, substitutions: nil, custom_args: nil, personalizations: nil, send_at: nil, headers: nil, categories: nil, batch_id: nil, asm: nil, ip_pool_name: nil, mail_settings: nil, tracking_settings: nil, attachments: nil, dynamic_template_data: nil, sandbox: false, __phoenix_view__: nil, __phoenix_layout__: nil @type t :: %Email{ to: nil | [recipient], cc: nil | [recipient], bcc: nil | [recipient], from: nil | recipient, reply_to: nil | recipient, reply_to_list: nil | [recipient], subject: nil | String.t(), content: nil | [content], template_id: nil | String.t(), version_id: nil | String.t(), substitutions: nil | substitutions, custom_args: nil | custom_args, personalizations: nil | [Personalization.t()], dynamic_template_data: nil | dynamic_template_data, send_at: nil | integer, headers: nil | headers(), categories: nil | [String.t], batch_id: nil | String.t, asm: nil | asm(), ip_pool_name: nil | String.t, mail_settings: nil | mail_settings(), tracking_settings: nil | tracking_settings(), attachments: nil | [attachment], sandbox: boolean(), __phoenix_view__: nil | atom, __phoenix_layout__: nil | %{optional(:text) => String.t(), optional(:html) => String.t()} } @type recipient :: %{required(:email) => String.t(), optional(:name) => String.t()} @type content :: %{type: String.t(), value: String.t()} @type headers :: %{String.t() => String.t()} @type asm :: %{ required(:group_id) => integer, optional(:groups_to_display) => [integer] } @type mail_settings :: %{ optional(:bypass_list_management) => bypass_filter(), optional(:bypass_spam_management) => bypass_filter(), optional(:bypass_bounce_management) => bypass_filter(), optional(:bypass_unsubscribe_management) => bypass_filter(), optional(:footer) => footer(), optional(:sandbox_mode) => enable_status() } @type attachment :: %{ required(:content) => String.t(), optional(:type) => String.t(), required(:filename) => String.t(), optional(:disposition) => String.t(), optional(:content_id) => String.t() } @type enable_status ::%{ required(:enable) => boolean } @type bypass_filter :: enable_status() @type footer :: %{ required(:enable) => boolean, required(:text) => String.t, required(:html) => String.t } @type substitutions :: %{String.t() => String.t()} @type custom_args :: %{String.t() => String.t()} @type dynamic_template_data :: %{String.t() => String.t()} @type tracking_settings :: %{ optional(:click_tracking) => click_tracking(), optional(:open_tracking) => open_tracking(), optional(:subscription_tracking) => subscription_tracking(), optional(:ganalytics) => google_analytics_tracking() } @type click_tracking :: %{ optional(:enable) => boolean, optional(:enable_text) => boolean } @type open_tracking :: %{ optional(:enable) => boolean, optional(:substitution_tag) => nil | String.t } @type subscription_tracking :: %{ optional(:enable) => boolean, optional(:text) => String.t, optional(:html) => String.t, optional(:substitution_tag) => String.t } @type google_analytics_tracking :: %{ optional(:enable) => boolean, optional(:utm_source) => String.t, optional(:utm_medium) => String.t, optional(:utm_content) => String.t, optional(:utm_campaign) => String.t } @doc """ Builds an an empty email to compose on. ## Examples iex> build() %Email{...} """ @spec build :: t def build do %Email{} end @doc """ Sets the `to` field for the email. A to-name can be passed as the third parameter. ## Examples add_to(%Email{}, "test@email.com") add_to(%Email{}, "test@email.com", "John Doe") """ @spec add_to(t, String.t()) :: t def add_to(%Email{to: to} = email, to_address) do addresses = add_address_to_list(to, to_address) %Email{email | to: addresses} end @spec add_to(t, String.t(), String.t()) :: t def add_to(%Email{to: to} = email, to_address, to_name) do addresses = add_address_to_list(to, to_address, to_name) %Email{email | to: addresses} end @doc """ Sets the `from` field for the email. The from-name can be specified as the third parameter. ## Examples put_from(%Email{}, "test@email.com") put_from(%Email{}, "test@email.com", "John Doe") """ @spec put_from(t, String.t()) :: t def put_from(%Email{} = email, from_address) do %Email{email | from: address(from_address)} end @spec put_from(t, String.t(), String.t()) :: t def put_from(%Email{} = email, from_address, from_name) do %Email{email | from: address(from_address, from_name)} end @doc """ Add recipients to the `CC` address field. The cc-name can be specified as the third parameter. ## Examples add_cc(%Email{}, "test@email.com") add_cc(%Email{}, "test@email.com", "John Doe") """ @spec add_cc(t, String.t()) :: t def add_cc(%Email{cc: cc} = email, cc_address) do addresses = add_address_to_list(cc, cc_address) %Email{email | cc: addresses} end @spec add_cc(Email.t(), String.t(), String.t()) :: Email.t() def add_cc(%Email{cc: cc} = email, cc_address, cc_name) do addresses = add_address_to_list(cc, cc_address, cc_name) %Email{email | cc: addresses} end @doc """ Add recipients to the `BCC` address field. The bcc-name can be specified as the third parameter. ## Examples add_bcc(%Email{}, "test@email.com") add_bcc(%Email{}, "test@email.com", "John Doe") """ @spec add_bcc(t, String.t()) :: t def add_bcc(%Email{bcc: bcc} = email, bcc_address) do addresses = add_address_to_list(bcc, bcc_address) %Email{email | bcc: addresses} end @spec add_bcc(t, String.t(), String.t()) :: t def add_bcc(%Email{bcc: bcc} = email, bcc_address, bcc_name) do addresses = add_address_to_list(bcc, bcc_address, bcc_name) %Email{email | bcc: addresses} end @doc """ Adds an attachment to the email. An attachment is a map with the keys: * `:content` * `:type` * `:filename` * `:disposition` * `:content_id` ## Examples attachment = %{content: "base64string", filename: "image.jpg"} add_attachment(%Email{}, attachment} """ @spec add_attachment(t, attachment) :: t def add_attachment(%Email{} = email, attachment) do attachments = case email.attachments do nil -> [attachment] list -> list ++ [attachment] end %Email{email | attachments: attachments} end @doc """ Sets the `reply_to` field for the email. The reply-to name can be specified as the third parameter. ## Examples put_reply_to(%Email{}, "test@email.com") put_reply_to(%Email{}, "test@email.com", "John Doe") """ @spec put_reply_to(t, String.t()) :: t def put_reply_to(%Email{} = email, reply_to_address) do %Email{email | reply_to: address(reply_to_address)} end @spec put_reply_to(t, String.t(), String.t()) :: t def put_reply_to(%Email{} = email, reply_to_address, reply_to_name) do %Email{email | reply_to: address(reply_to_address, reply_to_name)} end @doc """ Sets the `reply_to_list` field for email. You may not use reply_to_list and reply_to at the same time. """ @spec put_reply_to_list(t, [String.t()]) :: t def put_reply_to_list(%Email{} = email, reply_to_addresses) do list = Enum.map(reply_to_addresses, fn(v) -> case v do {address, name} -> address(address, name) address -> address(address) end end) %Email{email | reply_to_list: list} end @doc """ Set/Replace email categories. """ def put_categories(%Email{} = email, categories) do %Email{email| categories: categories} end @doc """ Add/set email category. """ def put_category(%Email{} = email, category) do case email.categories do nil -> %Email{email| categories: [category]} v -> %Email{email| categories: Enum.uniq(v ++ [category])} end end @doc """ Set batch_id """ def put_batch(%Email{} = email, value) do %Email{email| batch_id: value} end @doc """ Set asm """ def put_asm(%Email{} = email, value) do %Email{email| asm: value} end @doc """ Set ip_pool_name """ def put_ip_pool(%Email{} = email, value) do %Email{email| ip_pool_name: value} end # Initialize mail_settings defp init_mail_settings(%Email{mail_settings: nil} = email) do %Email{email| mail_settings: %{}} end defp init_mail_settings(%Email{} = email), do: email @doc """ Set email.mail_settings.bypass_list_management """ def configure_list_management_bypass(%Email{} = email, enable) do init_mail_settings(email) |> put_in([Access.key(:mail_settings), :bypass_list_management], %{enable: enable}) end @doc """ Set email.mail_settings.bypass_spam_management """ def configure_spam_management_bypass(%Email{} = email, enable) do init_mail_settings(email) |> put_in([Access.key(:mail_settings), :bypass_spam_management], %{enable: enable}) end @doc """ Set email.mail_settings.bypass_bounce_management """ def configure_bounce_management_bypass(%Email{} = email, enable) do init_mail_settings(email) |> put_in([Access.key(:mail_settings), :bypass_bounce_management], %{enable: enable}) end @doc """ Set email.mail_settings.bypass_unsubscribe_management """ def configure_unsubscribe_management_bypass(%Email{} = email, enable) do init_mail_settings(email) |> put_in([Access.key(:mail_settings), :bypass_unsubscribe_management], %{enable: enable}) end @doc """ Set email.mail_settings.bypass_unsubscribe_management """ def put_footer(%Email{} = email, footer) do init_mail_settings(email) |> put_in([Access.key(:mail_settings), :footer], footer) end @doc """ Set entire mail_settings field, replacing any previous settings. """ def put_mail_settings(%Email{} = email, value) do %Email{mail_settings: value} end # Initialize track_settings defp init_tracking_settings(%Email{tracking_settings: nil} = email) do %Email{email| tracking_settings: %{}} end defp init_tracking_settings(%Email{} = email), do: email def configure_click_tracking(%Email{} = email, value) do email |> init_tracking_settings() |> put_in([Access.key(:tracking_settings), :click_tracking], value) end def configure_open_tracking(%Email{} = email, value) do email |> init_tracking_settings() |> put_in([Access.key(:tracking_settings), :open_tracking], value) end def configure_subscription_tracking(%Email{} = email, value) do email |> init_tracking_settings() |> put_in([Access.key(:tracking_settings), :subscription_tracking], value) end def configure_google_analytics(%Email{} = email, value) do email |> init_tracking_settings() |> put_in([Access.key(:tracking_settings), :ganalytics], value) end def put_tracking_settings(%Email{} = email, value) do email |> put_in([Access.key(:tracking_settings)], value) end @doc """ Sets the `subject` field for the email. ## Examples put_subject(%Email{}, "Hello from Elixir") """ @spec put_subject(t, String.t()) :: t def put_subject(%Email{} = email, subject) do %Email{email | subject: subject} end @doc """ Sets `text` content of the email. ## Examples put_text(%Email{}, "Sent from Elixir!") """ @spec put_text(t, String.t()) :: t def put_text(%Email{content: [%{type: "text/plain"} | tail]} = email, text_body) do content = [%{type: "text/plain", value: text_body} | tail] %Email{email | content: content} end def put_text(%Email{content: content} = email, text_body) do content = [%{type: "text/plain", value: text_body} | List.wrap(content)] %Email{email | content: content} end @doc """ Sets the `html` content of the email. ## Examples Email.put_html(%Email{}, "

Sent from Elixir!

") """ @spec put_html(t, String.t()) :: t def put_html(%Email{content: [head | %{type: "text/html"}]} = email, html_body) do content = [head | %{type: "text/html", value: html_body}] %Email{email | content: content} end def put_html(%Email{content: content} = email, html_body) do content = List.wrap(content) ++ [%{type: "text/html", value: html_body}] %Email{email | content: content} end @doc """ Sets a custom header. ## Examples Email.add_header(%Email{}, "HEADER_KEY", "HEADER_VALUE") """ @spec add_header(t, String.t(), String.t()) :: t def add_header(%Email{headers: headers} = email, header_key, header_value) when is_binary(header_key) and is_binary(header_value) do new_headers = Map.put(headers || %{}, header_key, header_value) %Email{email | headers: new_headers} end @doc """ Uses a predefined SendGrid template for the email. ## Examples Email.put_template(%Email{}, "the_template_id") """ @spec put_template(t, String.t() | SendGrid.Template) :: t def put_template(%Email{} = email, template = %SendGrid.LegacyTemplate{}), do: put_template(email, template.id) def put_template(%Email{} = email, template = %SendGrid.DynamicTemplate{}), do: put_template(email, template.id) def put_template(%Email{} = email, template_id) do %Email{email | template_id: template_id} end @doc """ Uses a predefined SendGrid template version for the email. ## Examples Email.put_template_version(%Email{}, "the_template_version_id") """ @spec put_template_version(t, String.t()) :: t def put_template_version(%Email{} = email, version_id) when is_bitstring(version_id) do %Email{email | version_id: version_id} end @doc """ Adds a substitution value to be used with a template. If a substitution for a given name is already set, it will be replaced when adding a substitution with the same name. ## Examples Email.add_substitution(%Email{}, "-sentIn-", "Elixir") """ @spec add_substitution(t, String.t(), String.t()) :: t def add_substitution(%Email{substitutions: substitutions} = email, sub_name, sub_value) do substitutions = Map.put(substitutions || %{}, sub_name, sub_value) %Email{email | substitutions: substitutions} end @doc """ Adds a custom_arg value to the email. If an argument for a given name is already set, it will be replaced when adding a argument with the same name. ## Examples Email.add_custom_arg(%Email{}, "-sentIn-", "Elixir") """ @spec add_custom_arg(t, String.t(), String.t()) :: t def add_custom_arg(%Email{custom_args: custom_args} = email, arg_name, arg_value) do custom_args = Map.put(custom_args || %{}, arg_name, arg_value) %Email{email | custom_args: custom_args} end @doc """ Adds a custom_arg value to the email. If an argument for a given name is already set, it will be replaced when adding a argument with the same name. ## Examples Email.add_dynamic_template_data(%Email{}, "-sentIn-", "Elixir") """ @spec add_dynamic_template_data(t, String.t(), String.t()) :: t def add_dynamic_template_data( %Email{dynamic_template_data: dynamic_template_data} = email, arg_name, arg_value ) do dynamic_template_data = Map.put(dynamic_template_data || %{}, arg_name, arg_value) %Email{email | dynamic_template_data: dynamic_template_data} end @doc """ Sets a future date of when to send the email. ## Examples Email.put_send_at(%Email{}, 1409348513) """ @spec put_send_at(t, integer) :: t def put_send_at(%Email{} = email, send_at) do %Email{email | send_at: send_at} end defp address(email), do: %{email: email} defp address(email, name), do: %{email: email, name: name} defp add_address_to_list(nil, email) do [address(email)] end defp add_address_to_list(list, email) when is_list(list) do list ++ [address(email)] end defp add_address_to_list(nil, email, name) do [address(email, name)] end defp add_address_to_list(list, email, name) when is_list(list) do list ++ [address(email, name)] end @doc """ Sets the layout to use for the Phoenix Template. Expects a tuple of the view module and layout to use. If you provide an atom as the second element, the text and HMTL versions of that template will be used for the respective content types. Alernatively, you can set a default layout to use by setting the `:phoenix_view` key in your config as an atom which will be used for both text and HTML emails. config :noizu_sendgrid, phoenix_layout: {MyApp.Web.EmailView, :layout} ## Examples put_phoenix_layout(email, {MyApp.Web.EmailView, "layout.html"}) put_phoenix_layout(email, {MyApp.Web.EmailView, "layout.txt"}) put_phoenix_layout(email, {MyApp.Web.EmailView, :layout}) """ @spec put_phoenix_layout(t, {atom, atom}) :: t def put_phoenix_layout(%Email{} = email, {module, layout}) when is_atom(module) and is_atom(layout) do layouts = build_layouts({module, layout}) %Email{email | __phoenix_layout__: layouts} end @spec put_phoenix_layout(t, {atom, String.t()}) :: t def put_phoenix_layout(%Email{__phoenix_layout__: layouts} = email, {module, layout}) when is_atom(module) do layouts = layouts || %{} updated_layout = build_layouts({module, layout}) %Email{email | __phoenix_layout__: Map.merge(layouts, updated_layout)} end # Build layout map defp build_layouts({module, layout}) when is_atom(module) and is_atom(layout) do base_name = Atom.to_string(layout) %{ text: {module, base_name <> ".txt"}, html: {module, base_name <> ".html"} } end defp build_layouts({module, layout} = args) when is_atom(module) do case Path.extname(layout) do ".html" -> %{html: args} ".txt" -> %{text: args} _ -> raise ArgumentError, "unsupported file type" end end @doc """ Sets the Phoenix View to use. This will override the default Phoenix View if set in under the `:phoenix_view` config value. ## Examples put_phoenix_view(email, MyApp.Web.EmailView) """ @spec put_phoenix_view(t, atom) :: t def put_phoenix_view(%Email{} = email, module) when is_atom(module) do %Email{email | __phoenix_view__: module} end @doc """ Renders the Phoenix template with the given assigns. You can set the default Phoenix View to use for your templates by setting the `:phoenix_view` config value. Additionally, you can set the view on a per email basis by calling `put_phoenix_view/2`. Furthermore, you can have the template rendered inside a layout. See `put_phoenix_layout/2` for more details. ## Explicit Template Extensions You can provide a template name with an explicit extension such as `"some_template.html"` or `"some_template.txt"`. This is set the content of the email respective to the content type of the template rendered. For example, if you render an HTML template, the output of the rendering will be the HTML content of the email. ## Implicit Template Extensions You can omit a template's extension and attempt to have both a text template and HTML template rendered. To have both types rendered, both templates must share the same base file name. For example, if you have a template named `"some_template.txt"` and a template named `"some_template.html"` and you call `put_phoenix_template(email, :some_template)`, both templates will be used and will set the email content for both content types. The only caveat is *both files must exist*, otherwise you'll have an exception raised. ## Examples iex> put_phoenix_template(email, "some_template.html") %Email{content: [%{type: "text/html", value: ...}], ...} iex> put_phoenix_template(email, "some_template.txt", name: "John Doe") %Email{content: [%{type: "text/plain", value: ...}], ...} iex> put_phoenix_template(email, :some_template, user: user) %Email{content: [%{type: "text/plain", value: ...}, %{type: "text/html", value: ...}], ...} """ def put_phoenix_template(email, template_name, assigns \\ []) @spec put_phoenix_template(t, atom, list()) :: t def put_phoenix_template(%Email{} = email, template_name, assigns) when is_atom(template_name) do with true <- ensure_phoenix_loaded(), view_mod <- phoenix_view_module(email), layouts <- phoenix_layouts(email), template_name <- Atom.to_string(template_name) do email |> render_html(view_mod, template_name <> ".html", layouts, assigns) |> render_text(view_mod, template_name <> ".txt", layouts, assigns) end end @spec put_phoenix_template(t, String.t(), list()) :: t def put_phoenix_template(%Email{} = email, template_name, assigns) do with true <- ensure_phoenix_loaded(), view_mod <- phoenix_view_module(email), layouts <- phoenix_layouts(email) do case Path.extname(template_name) do ".html" -> render_html(email, view_mod, template_name, layouts, assigns) ".txt" -> render_text(email, view_mod, template_name, layouts, assigns) end end end defp render_html(email, view_mod, template_name, layouts, assigns) do assigns = if Map.has_key?(layouts, :html) do Keyword.put(assigns, :layout, Map.get(layouts, :html)) else assigns end html = Phoenix.View.render_to_string(view_mod, template_name, assigns) put_html(email, html) end defp render_text(email, view_mod, template_name, layouts, assigns) do assigns = if Map.has_key?(layouts, :text) do Keyword.put(assigns, :layout, Map.get(layouts, :text)) else assigns end text = Phoenix.View.render_to_string(view_mod, template_name, assigns) put_text(email, text) end defp ensure_phoenix_loaded do unless Code.ensure_loaded?(Phoenix) do raise ArgumentError, "Attempted to call function that depends on Phoenix. " <> "Make sure Phoenix is part of your dependencies" end true end defp phoenix_layouts(%Email{__phoenix_layout__: layouts}) do layouts = layouts || %{} case config(:phoenix_layout) do nil -> layouts {module, layout} when is_atom(module) and is_atom(layout) -> configured_layouts = build_layouts({module, layout}) Map.merge(configured_layouts, layouts) _ -> raise ArgumentError, "Invalid configuration set for :phoenix_layout. " <> "Ensure the configuration is a tuple of a module and atom ({MyApp.View, :layout})." end end defp phoenix_view_module(%Email{__phoenix_view__: nil}) do mod = config(:phoenix_view) unless mod do raise ArgumentError, "Phoenix view is expected to be set or configured. " <> "Ensure your config for :noizu_sendgrid includes a value for :phoenix_view or " <> "explicity set the Phoenix view with `put_phoenix_view/2`." end mod end defp phoenix_view_module(%Email{__phoenix_view__: view_module}), do: view_module @doc """ Sets the email to be sent with sandbox mode enabled or disabled. The sandbox mode will default to what is explicitly configured with SendGrid's configuration. """ @spec set_sandbox(t(), boolean()) :: t() def set_sandbox(%Email{} = email, enabled?) when is_boolean(enabled?) do %Email{email | sandbox: enabled?} end @doc """ Transforms an `t:Email.t/0` to a `t:Personalization.t/0`. """ @spec to_personalization(t()) :: Personalization.t() def to_personalization(%Email{} = email) do %Personalization{ to: email.to, cc: email.cc, bcc: email.bcc, subject: email.subject, substitutions: email.substitutions, custom_args: email.custom_args, dynamic_template_data: email.dynamic_template_data, send_at: email.send_at, headers: email.headers } end @doc """ Adds a `t:Personalization.t/0` to an email. """ @spec add_personalization(t(), Personalization.t()) :: t() def add_personalization(%Email{} = email, %Personalization{} = personalization) do personalizations = List.wrap(email.personalizations) ++ [personalization] %Email{email | personalizations: personalizations} end defp config(key) do Application.get_env(:noizu_sendgrid, key) end defimpl Jason.Encoder do defp conditional_insert(params, email, field, as_field \\ nil) do cond do v = Map.get(email, field) -> put_in(params, [as_field || field], v) :else -> params end end def encode(%Email{personalizations: [_ | _]} = email, opts) do params = %{ personalizations: email.personalizations, from: email.from, subject: email.subject, content: email.content, send_at: email.send_at, attachments: email.attachments, headers: email.headers, } # Template |> then( fn(params) -> cond do email.template_id && email.version_id -> put_in(params, [:template_id], email.template_id <> "." <> email.version_id) email.template_id -> put_in(params, [:template_id], email.template_id) email.version_id -> raise ArgumentError, "You must specify template if specifying template version" :else -> params end end) # Reply List |> then( fn(params) -> cond do email.reply_to_list && email.reply_to -> raise ArgumentError, "You may not set reply_to_list and reply_to at the same time." v = email.reply_to -> put_in(params, [:reply_to], v) v = email.reply_to_list -> put_in(params, [:reply_to_list], v) :else -> params end end) # Mail Settings |> conditional_insert(email, :mail_settings) # Track Settings |> conditional_insert(email, :tracking_settings, :track_settings) # Categories |> conditional_insert(email, :categories) # Batch |> conditional_insert(email, :batch_id) # ASM |> conditional_insert(email, :asm) # IP Pool |> conditional_insert(email, :ip_pool_name) # sandbox_mode |> then( fn(params) -> # Insure partially populated. params |> update_in([:mail_settings], &(&1 || %{})) |> update_in([:mail_settings, :sandbox_mode], &(&1 || %{})) |> update_in([:mail_settings, :sandbox_mode, :enable], fn(p) -> cond do is_boolean(p) -> p :else -> Application.get_env(:noizu_sendgrid, :sandbox_enable, email.sandbox) end end) end) Jason.Encode.map(params, opts) end def encode(%Email{personalizations: nil} = email, opts) do personalization = Email.to_personalization(email) email |> Email.add_personalization(personalization) |> encode(opts) end end end