Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 164 additions & 5 deletions lib/ex_aws/ses.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,149 @@ defmodule ExAws.SES do
@service :ses
@v2_path "/v2/email"

@type create_email_identity_opt ::
{:configuration_set_name, String.t()}
| {:dkim_signing_attributes,
%{
DomainSigningAttributesOrigin: String.t(),
DomainSigningPrivateKey: String.t(),
DomainSigningSelector: String.t(),
NextSigningKeyLength: String.t()
}}
| {:tags, %{(String.t() | atom) => String.t()}}

@doc """
Create an Email identity list via the SES V2 API.
See https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_CreateEmailIdentity.html.

## Examples

ExAws.SES.create_email_identity(
"mydomain.com",
[
configuration_set_name: "my_configuration_set",
dkim_signing_attributes: %{
DomainSigningAttributesOrigin: "AWS_SES",
DomainSigningPrivateKey: "my_private_key",
DomainSigningSelector: "my_selector",
NextSigningKeyLength: "2048"
},
tags: [%{"Key" => "environment", "Value" => "test"}]
]
)

"""
@spec create_email_identity(identity :: binary, opts :: [] | [create_email_identity_opt]) :: ExAws.Operation.JSON.t()
def create_email_identity(identity, opts \\ []) do
data =
prune_map(%{
"EmailIdentity" => identity,
"ConfigurationSetName" => opts[:configuration_set_name],
"DkimSigningAttributes" => opts[:dkim_signing_attributes],
"Tags" => format_tags_v2(opts[:tags])
})

:post
|> request_v2("identities")
|> Map.put(:data, data)
end

@doc """
Provides information about a specific identity via the SES V2 API.
See https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_GetEmailIdentity.html

## Examples

ExAws.SES.get_email_identity("mydomain.com")
"""
@spec get_email_identity(identity :: binary) :: ExAws.Operation.JSON.t()
def get_email_identity(identity) do
encoded_identity = ExAws.Request.Url.uri_encode(identity)

request_v2(:get, "identities/#{encoded_identity}")
end

@type list_email_identities_opt :: {:page_size, pos_integer} | {:next_token, String.t()}
@doc """
List email identities using the SES V2 API.
See https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_ListEmailIdentities.html

## Examples

ExAws.SES.list_email_identities([page_size: 100, next_token: nil])
"""
@spec list_email_identities(opts :: [] | [list_email_identities_opt]) :: ExAws.Operation.JSON.t()
def list_email_identities(opts \\ []) do
params = build_opts(opts, [:page_size, :next_token])
request_v2(:get, "identities?#{URI.encode_query(params)}")
end

@doc """
Used to associate a configuration set with an email identity.
See https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_PutEmailIdentityConfigurationSetAttributes.html
"""

@spec put_email_identity_configuration_set_attributes(identity :: binary, configuration_set_name :: binary) ::
ExAws.Operation.JSON.t()
def put_email_identity_configuration_set_attributes(identity, configuration_set_name) do
encoded_identity = ExAws.Request.Url.uri_encode(identity)

data = prune_map(%{"ConfigurationSetName" => configuration_set_name})

request_v2(:put, "identities/#{encoded_identity}/configuration-set")
|> Map.put(:data, data)
end

@type put_email_identity_mail_from_attributes_opt ::
{:BehaviorOnMxFailure, String.t()} | {:MailFromDomain, String.t()}

@doc """
Used to enable or disable the custom Mail-From domain configuration for an email identity via the SES V2 API.
See https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_PutEmailIdentityMailFromAttributes.html

## Examples

ExAws.SES.put_email_identity_mail_from_attributes(
"mydomain.com",
[
BehaviorOnMxFailure: "USE_DEFAULT_VALUE" || "REJECT_MESSAGE",
MailFromDomain: "subdomain.mydomain.com"
]
)
"""
@spec put_email_identity_mail_from_attributes(
identity :: binary,
opts :: [put_email_identity_mail_from_attributes_opt]
) :: ExAws.Operation.JSON.t()
def put_email_identity_mail_from_attributes(identity, opts) do
encoded_identity = ExAws.Request.Url.uri_encode(identity)

data =
prune_map(%{
"BehaviorOnMxFailure" => opts[:BehaviorOnMxFailure],
"MailFromDomain" => opts[:MailFromDomain]
})

:put
|> request_v2("identities/#{encoded_identity}/mail-from")
|> Map.put(:data, data)
end

@doc """
Deletes an email identity via the SES V2 API.
See https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_DeleteEmailIdentity.html

## Examples

ExAws.SES.delete_email_identity("mydomain.com")
"""
@spec delete_email_identity(identity :: binary) :: ExAws.Operation.JSON.t()
def delete_email_identity(identity) do
encoded_identity = ExAws.Request.Url.uri_encode(identity)

request_v2(:delete, "identities/#{encoded_identity}")
end

@doc """
Verifies an email address.
"""
Expand Down Expand Up @@ -61,6 +204,20 @@ defmodule ExAws.SES do
request(:get_identity_verification_attributes, params)
end

## Configuration sets
######################

@doc """
Fetch configuration set from configuration set name via the SES V2 API.
See https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_GetConfigurationSet.html
"""
@spec get_configuration_set(identity :: binary) :: ExAws.Operation.JSON.t()
def get_configuration_set(configuration_set_name) do
encoded_configuration_set_name = ExAws.Request.Url.uri_encode(configuration_set_name)

request_v2(:get, "configuration-sets/#{encoded_configuration_set_name}")
end

@type list_configuration_sets_opt ::
{:max_items, pos_integer}
| {:next_token, String.t()}
Expand Down Expand Up @@ -387,11 +544,10 @@ defmodule ExAws.SES do
|> put_if_not_nil("Html", html)
|> put_if_not_nil("Text", text)

params =
%{
"TemplateName" => template_name,
"TemplateContent" => template_content
}
params = %{
"TemplateName" => template_name,
"TemplateContent" => template_content
}

request_v2(:post, "templates")
|> Map.put(:data, params)
Expand Down Expand Up @@ -936,6 +1092,9 @@ defmodule ExAws.SES do
end)
end

defp format_tags_v2(tags),
do: Enum.map(tags, fn {k, v} -> %{"Key" => to_string(k), "Value" => to_string(v)} end)

## Request
######################

Expand Down
58 changes: 31 additions & 27 deletions lib/ex_aws/ses/parsers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,28 @@ if Code.ensure_loaded?(SweetXml) do
end

def parse({:ok, %{body: xml} = resp}, :verify_domain_identity) do
parsed_body = SweetXml.xpath(xml, ~x"//VerifyDomainIdentityResponse",
verification_token: ~x"./VerifyDomainIdentityResult/VerificationToken/text()"s,
request_id: request_id_xpath()
)
parsed_body =
SweetXml.xpath(xml, ~x"//VerifyDomainIdentityResponse",
verification_token: ~x"./VerifyDomainIdentityResult/VerificationToken/text()"s,
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end

def parse({:ok, %{body: xml} = resp}, :verify_domain_dkim) do
parsed_body = SweetXml.xpath(xml, ~x"//VerifyDomainDkimResponse",
dkim_tokens: [
~x"./VerifyDomainDkimResult",
members: ~x"./DkimTokens/member/text()"ls
],
request_id: request_id_xpath()
)
parsed_body =
SweetXml.xpath(xml, ~x"//VerifyDomainDkimResponse",
dkim_tokens: [
~x"./VerifyDomainDkimResult",
members: ~x"./DkimTokens/member/text()"ls
],
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end


def parse({:ok, %{body: xml} = resp}, :get_identity_verification_attributes) do
parsed_body =
xml
Expand All @@ -57,7 +58,9 @@ if Code.ensure_loaded?(SweetXml) do
~x"./ListIdentitiesResult",
members: ~x"./Identities/member/text()"ls,
next_token: ~x"./NextToken/text()"so
], #TODO: Remove this key in the next major version, 3.x.x
],

# TODO: Remove this key in the next major version, 3.x.x
identities: [
~x"./ListIdentitiesResult",
members: ~x"./Identities/member/text()"ls,
Expand Down Expand Up @@ -201,20 +204,21 @@ if Code.ensure_loaded?(SweetXml) do
end

def parse({:ok, %{body: xml} = resp}, :describe_receipt_rule_set) do
parsed_body = SweetXml.xpath(xml, ~x"//DescribeReceiptRuleSetResponse",
rules: [
~x"./DescribeReceiptRuleSetResult",
members: [
~x"./Rules/member"l,
enabled: ~x"./Enabled/text()"so |> transform_to_boolean(),
name: ~x"./Name/text()"s,
recipients: ~x"./Recipients/member/text()"ls,
scan_enabled: ~x"./ScanEnabled/text()"so |> transform_to_boolean(),
tls_policy: ~x"./TlsPolicy/text()"so,
],
],
request_id: request_id_xpath()
)
parsed_body =
SweetXml.xpath(xml, ~x"//DescribeReceiptRuleSetResponse",
rules: [
~x"./DescribeReceiptRuleSetResult",
members: [
~x"./Rules/member"l,
enabled: ~x"./Enabled/text()"so |> transform_to_boolean(),
name: ~x"./Name/text()"s,
recipients: ~x"./Recipients/member/text()"ls,
scan_enabled: ~x"./ScanEnabled/text()"so |> transform_to_boolean(),
tls_policy: ~x"./TlsPolicy/text()"so
]
],
request_id: request_id_xpath()
)

{:ok, Map.put(resp, :body, parsed_body)}
end
Expand Down
30 changes: 22 additions & 8 deletions test/lib/ses/parser_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ defmodule ExAws.SES.ParserTest do
|> to_success

{:ok, %{body: parsed_doc}} = Parsers.parse(rsp, :verify_domain_identity)
assert parsed_doc == %{request_id: "d8eb8250-be9b-11e6-b7f7-d570946af758", verification_token: "u4GmlJ3cPJfxxZbLSPMkLOPjQvJW1HPvA6Pmi21CPIE="}

assert parsed_doc == %{
request_id: "d8eb8250-be9b-11e6-b7f7-d570946af758",
verification_token: "u4GmlJ3cPJfxxZbLSPMkLOPjQvJW1HPvA6Pmi21CPIE="
}
end

test "#parse a verify_domain_dkim response" do
Expand All @@ -64,9 +68,18 @@ defmodule ExAws.SES.ParserTest do
|> to_success

{:ok, %{body: parsed_doc}} = Parsers.parse(rsp, :verify_domain_dkim)
assert parsed_doc == %{request_id: "d8eb8250-be9b-11e6-b7f7-d570946af758", dkim_tokens: %{members: ["5livxhounddpfqprdog22m4c337ake5o", "tbnwx5g3l0zmstwf2c258r36pvpnksbt", "bbtl43drumsloilm2zfjlhj3c7v12a5d"]}}
end

assert parsed_doc == %{
request_id: "d8eb8250-be9b-11e6-b7f7-d570946af758",
dkim_tokens: %{
members: [
"5livxhounddpfqprdog22m4c337ake5o",
"tbnwx5g3l0zmstwf2c258r36pvpnksbt",
"bbtl43drumsloilm2zfjlhj3c7v12a5d"
]
}
}
end

test "#parse identity_verification_attributes" do
rsp =
Expand Down Expand Up @@ -230,11 +243,12 @@ defmodule ExAws.SES.ParserTest do
"""
|> to_success()

{:ok, %{body: parsed_doc}} = Parsers.parse(rsp, :send_raw_email)
assert parsed_doc == %{
request_id: "3c2ddfd4-ff21-4a3d-af5f-97ec74811e22",
message_id: "0101018264278cea-406a0406-5f46-412b-8f32-05ef31a62aa0-000000"
}
{:ok, %{body: parsed_doc}} = Parsers.parse(rsp, :send_raw_email)

assert parsed_doc == %{
request_id: "3c2ddfd4-ff21-4a3d-af5f-97ec74811e22",
message_id: "0101018264278cea-406a0406-5f46-412b-8f32-05ef31a62aa0-000000"
}
end

test "#parse a delete_identity response" do
Expand Down