tt-mail-ses 1.1.0.pre

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5199d52725461a602ecd0ef5514c670248f1cd212ccdaac40b337b5b53da7660
4
+ data.tar.gz: 550bcb9cdd21e6a545358119726de54d6dba80613f2be39f35e284844b5484ec
5
+ SHA512:
6
+ metadata.gz: 73669644af86b647f248532e6988790a82cc2620aeebb9689f764ed5be45dab391751c038ecf7323cad734e467b0d7be0288b0b9a756f8f4cdb4a7f0ea367c93
7
+ data.tar.gz: b8cb0539bbc43413e76c3f1fe9e357775561d1ac51c5cce556f241d73101b6e54330a526e96da1d81d43dbdd131a7463066057fa8e66c56d5e4790a221db07d1
data/CHANGELOG.md ADDED
@@ -0,0 +1,50 @@
1
+ # Changelog
2
+
3
+ ### 1.2.0.pre
4
+ - Support options via message headers [#2](https://github.com/Teamtailor/mail-ses/pull/2)
5
+
6
+ ### 1.1.0
7
+
8
+ - Mail Gem: Bump minimum version dependency to 2.8.1.
9
+
10
+ ### 1.0.5
11
+
12
+ - Pass-thru invalid email addresses.
13
+
14
+ ### 1.0.4
15
+
16
+ - Fix missing method error related to message headers.
17
+
18
+ ### 1.0.3
19
+
20
+ - Support UTF-8 chars in from, to, etc addresses.
21
+
22
+ ### 1.0.2
23
+
24
+ - Fix labels in being stripped from email addresses.
25
+ - Support Reply-To address.
26
+
27
+ ### 1.0.1
28
+
29
+ - Add compatibility with Mail gem 2.8.0.
30
+
31
+ ### 1.0.0
32
+
33
+ - BREAKING CHANGE: Upgrade to AWS Ruby SDK v3 - SESv2 API ([@khrvi](https://github.com/khrvi))
34
+ - Drop support for Ruby 2.5 and earlier.
35
+ - Switch CI from Travis to Github Actions.
36
+ - Add Rubocop to CI.
37
+ - Refactor code.
38
+
39
+ ### 0.1.2
40
+
41
+ - Fix: Add #settings method for conformity with other Mail delivery methods.
42
+
43
+ ### 0.1.1
44
+
45
+ - Fix: Remove Base64 encoding from message body.
46
+
47
+ ### 0.1.0
48
+
49
+ - Initial release of gem.
50
+ - Support for sending ActionMailer mails via AWS SDK v3.
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ Additional modifications copyright (c) 2024 Teamtailor AB
2
+
3
+ Copyright (c) 2018 TableCheck Inc.
4
+
5
+ AWS::SES originally copyright (c) 2011 Drew V. Blas <drew.blas@gmail.com>
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining
8
+ a copy of this software and associated documentation files (the
9
+ "Software"), to deal in the Software without restriction, including
10
+ without limitation the rights to use, copy, modify, merge, publish,
11
+ distribute, sublicense, and/or sell copies of the Software, and to
12
+ permit persons to whom the Software is furnished to do so, subject to
13
+ the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,143 @@
1
+ [![Gem Version](https://badge.fury.io/rb/mail-ses.svg)](http://badge.fury.io/rb/mail-ses)
2
+ [![Github Actions](https://github.com/tablecheck/mail-ses/actions/workflows/test.yml/badge.svg)](https://github.com/tablecheck/mail-ses/actions/workflows/test.yml)
3
+
4
+ # Mail::SES
5
+
6
+ Mail::SES is a mail delivery method handler for Amazon SES (Simple Email Service) which can be used with Rails' [Action Mailer](https://guides.rubyonrails.org/action_mailer_basics.html).
7
+
8
+ This gem is inspired by [Drew Blas' AWS::SES gem](https://github.com/drewblas/aws-ses),
9
+ but uses the official [AWS SDK for Ruby v3 - SESv2](https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/SESV2.html) under-the-hood.
10
+ By passing parameters through to the SDK, this gem supports greater flexibility with less code (including IAM instance profiles, retry parameters, etc.)
11
+
12
+ ### Compatibility
13
+
14
+ * Ruby 2.6+
15
+ * Ruby on Rails 3.2+
16
+ * Mail gem 2.8.1+
17
+ * AWS SDK for Ruby v3 - SESv2
18
+
19
+ Please use version 0.1.x of this gem for legacy Ruby and AWS SDK support.
20
+
21
+ ## Getting Started
22
+
23
+ In your `Gemfile`:
24
+
25
+ ```ruby
26
+ gem 'mail-ses'
27
+ ```
28
+
29
+ Next, make a new initializer at `config/initializers/mail_ses.rb`:
30
+
31
+ ```ruby
32
+ ActionMailer::Base.add_delivery_method :ses, Mail::SES,
33
+ region: 'us-east-1',
34
+ access_key_id: 'abc',
35
+ secret_access_key: '123'
36
+ ```
37
+
38
+ Finally, in the appropriate `config/environments/*.rb`:
39
+
40
+ ```ruby
41
+ config.action_mailer.delivery_method = :ses
42
+ ```
43
+
44
+ ## Advanced Usage
45
+
46
+ ### AWS SES Client Options
47
+
48
+ Any options supported by the `Aws::SESV2::Client` class can be passed into the initializer, for example:
49
+
50
+ ```ruby
51
+ ActionMailer::Base.add_delivery_method :ses, Mail::SES,
52
+ region: 'us-east-1',
53
+ session_token: 'foobar',
54
+ retry_limit: 5,
55
+ retry_max_delay: 10
56
+ ```
57
+
58
+ In addition, the shortcut option `:use_iam_profile (Boolean)` which activates the IAM instance profile.
59
+
60
+ ```ruby
61
+ ActionMailer::Base.add_delivery_method :ses, Mail::SES,
62
+ region: 'us-east-1',
63
+ use_iam_profile: true
64
+ ```
65
+
66
+ ### Default Mail Options
67
+
68
+ In the initializer you can set `:mail_options (Hash)` which are default options to pass-through to each mail sent:
69
+
70
+ ```ruby
71
+ ActionMailer::Base.add_delivery_method :ses, Mail::SES,
72
+ # ...
73
+ mail_options: {
74
+ from_email_address_identity_arn: 'arn:aws:ses:us-east-1:123456789012:identity/example.com',
75
+ email_tags: [
76
+ { name: 'MessageTagName', value: 'MessageTagValue' },
77
+ ],
78
+ }
79
+ ```
80
+
81
+ ### Override Mail Options
82
+
83
+ You can override the default mail options on a per-mail basis by passing them in the `mail` method:
84
+
85
+
86
+ ```ruby
87
+ class ApplicationMailer < ActionMailer::Base
88
+ def example
89
+ mail(
90
+ to: "foo@example.com",
91
+ from: "bar@example.com",
92
+ mail_options: {
93
+ email_tags: [
94
+ { name: 'MessageTagName', value: 'MessageTagValue' },
95
+ ],
96
+ }
97
+ )
98
+ end
99
+ end
100
+ ```
101
+
102
+ ### AWS Error Handling
103
+
104
+ To handle errors from AWS API, in the initializer you can set `:error_handler (Proc)` which takes two args:
105
+ the error which was raised, and the raw_email options hash. This is useful for notifying your bug tracking service.
106
+ Setting `:error_handler` causes the error to be swallowed unless it is raised again in the handler itself.
107
+
108
+ ```ruby
109
+ ActionMailer::Base.add_delivery_method :ses, Mail::SES,
110
+ # ...
111
+ error_handler: ->(error, raw_email) do
112
+ Bugsnag.notify(error){|r| r.add_tab('email', { email: raw_email })}
113
+ raise error
114
+ end
115
+ ```
116
+
117
+ ### Send Email as a Standalone
118
+
119
+ You can send one-off mails using the `Mail::SES` object and `#deliver` method.
120
+
121
+ ```ruby
122
+ mail = Mail.new(args)
123
+
124
+ ses = Mail::SES.new(region: 'us-east-1',
125
+ access_key_id: 'abc',
126
+ secret_access_key: '123')
127
+
128
+ options = { from_email_address_identity_arn: 'arn:aws:ses:us-east-1:123456789012:identity/example.com' }
129
+
130
+ ses.deliver!(mail, options) #=> returns AWS API response
131
+
132
+ mail.message_id #=> "00000138111222aa-33322211-cccc-cccc-cccc-ddddaaaa0680-000000@email.amazonses.com"
133
+ ```
134
+
135
+ Please also see the [AWS SDK v3 for SES](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-using-sdk-ruby.html) for alternate approaches.
136
+
137
+ ### Statistics, Verified Addresses, Bounce Rate, etc.
138
+
139
+ Please use the official [AWS SDK v3 for SES](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-using-sdk-ruby.html).
140
+
141
+ ## Copyright
142
+
143
+ Copyright (c) 2024 [Teamtailor](http://www.teamtailor.com/). See LICENSE for further details.
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mail
4
+ class SES
5
+ # Validates a Mail::Message object before sending
6
+ class MessageValidator
7
+ # message - The Mail::Message object to be validated.
8
+ def initialize(message)
9
+ @message = message
10
+ end
11
+
12
+ # Validate the message.
13
+ def validate
14
+ validate_class
15
+ validate_delivery_params
16
+ validate_attachments
17
+ end
18
+
19
+ private
20
+
21
+ def validate_class
22
+ return if @message.is_a?(Mail::Message)
23
+
24
+ raise ArgumentError.new("mail must be an instance of Mail::Message class")
25
+ end
26
+
27
+ def validate_delivery_params
28
+ Mail::SmtpEnvelope.new(@message)
29
+ end
30
+
31
+ def validate_attachments
32
+ return unless @message.has_attachments? && @message.text_part.nil? && @message.html_part.nil?
33
+
34
+ raise ArgumentError.new("Attachment provided without message body")
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mail
4
+ class SES
5
+ # Builds options for Aws::SESV2::Client#send_email
6
+ class OptionsBuilder
7
+ SES_FIELDS = %i[ from_email_address
8
+ from_email_address_identity_arn
9
+ reply_to_addresses
10
+ feedback_forwarding_email_address
11
+ feedback_forwarding_email_address_identity_arn
12
+ email_tags
13
+ configuration_set_name ].freeze
14
+
15
+ # message - The Mail::Message object to be sent.
16
+ # options - The Hash options which override any defaults
17
+ # from the message.
18
+ def initialize(message, options = {})
19
+ @message = message
20
+ @options = options
21
+ end
22
+
23
+ # Returns the options for Aws::SESV2::Client#send_email.
24
+ def build
25
+ message_options.merge(ses_options, ses_options_from_message)
26
+ end
27
+
28
+ private
29
+
30
+ def ses_options
31
+ # TODO: address fields should be encoded to UTF-8
32
+ slice_hash(@options, *SES_FIELDS)
33
+ end
34
+
35
+ def ses_options_from_message
36
+ mail_options = @message[:mail_options]&.unparsed_value
37
+
38
+ if mail_options
39
+ @message[:mail_options] = nil
40
+ slice_hash(mail_options, *SES_FIELDS)
41
+ else
42
+ {}
43
+ end
44
+ end
45
+
46
+ def message_options
47
+ {
48
+ from_email_address: extract_value(:from)&.first,
49
+ reply_to_addresses: extract_value(:reply_to),
50
+ destination: {
51
+ to_addresses: extract_value(:to) || [],
52
+ cc_addresses: extract_value(:cc) || [],
53
+ bcc_addresses: extract_value(:bcc) || []
54
+ },
55
+ content: {raw: {data: @message.to_s}}
56
+ }.compact
57
+ end
58
+
59
+ def slice_hash(hash, *keys)
60
+ keys.each_with_object({}) { |k, h| h[k] = hash[k] if hash.key?(k) }
61
+ end
62
+
63
+ def extract_value(key)
64
+ value = @message.header[key]
65
+ return unless value
66
+
67
+ if value.respond_to?(:formatted)
68
+ value.formatted
69
+ elsif value.respond_to?(:unparsed_value)
70
+ Array(value.unparsed_value)
71
+ else
72
+ []
73
+ end&.map { |v| encode(v) }
74
+ end
75
+
76
+ def encode(value)
77
+ Mail::Encodings.address_encode(value)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mail
4
+ class SES
5
+ VERSION = "1.1.0.pre"
6
+ end
7
+ end
data/lib/mail/ses.rb ADDED
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mail/ses/version"
4
+ require "mail/ses/message_validator"
5
+ require "mail/ses/options_builder"
6
+
7
+ module Mail
8
+ # Mail delivery method handler for AWS SES
9
+ class SES
10
+ attr_accessor :settings
11
+ attr_reader :client
12
+
13
+ # Initializes the Mail::SES object.
14
+ #
15
+ # options - The Hash options (optional, default: {}):
16
+ # :mail_options - (Hash) Default AWS options to set on each mail object.
17
+ # :error_handler - (Proc<Error, Hash>) Handler for AWS API errors.
18
+ # :use_iam_profile - Shortcut to use AWS IAM instance profile.
19
+ # All other options are passed-thru to Aws::SESV2::Client.
20
+ def initialize(options = {})
21
+ @mail_options = options.delete(:mail_options) || {}
22
+
23
+ @error_handler = options.delete(:error_handler)
24
+ raise ArgumentError.new(":error_handler must be a Proc") if @error_handler && !@error_handler.is_a?(Proc)
25
+
26
+ @settings = {
27
+ return_response: options.delete(:return_response),
28
+ message_id_domain: options.delete(:message_id_domain)
29
+ }
30
+
31
+ options[:credentials] = Aws::InstanceProfileCredentials.new if options.delete(:use_iam_profile)
32
+ @client = Aws::SESV2::Client.new(options)
33
+ end
34
+
35
+ # Delivers a Mail::Message object via SES.
36
+ #
37
+ # message - The Mail::Message object to deliver (required).
38
+ # options - The Hash options which override any defaults set in :mail_options
39
+ # in the initializer (optional, default: {}). Refer to
40
+ # Aws::SESV2::Client#send_email
41
+ def deliver!(message, options = {})
42
+ MessageValidator.new(message).validate
43
+
44
+ options = @mail_options.merge(options || {})
45
+ send_options = OptionsBuilder.new(message, options).build
46
+
47
+ begin
48
+ response = client.send_email(send_options)
49
+ message.message_id = "#{response.to_h[:message_id]}@#{settings[:message_id_domain] || "email.amazonses.com"}"
50
+ settings[:return_response] ? response : self
51
+ rescue => e
52
+ handle_error(e, send_options)
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def handle_error(error, send_options)
59
+ raise(error) unless @error_handler
60
+
61
+ @error_handler.call(error, send_options.dup)
62
+ end
63
+ end
64
+ end
data/lib/mail-ses.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "aws-sdk-sesv2"
4
+ require "mail"
5
+ require "mail/ses"
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tt-mail-ses
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0.pre
5
+ platform: ruby
6
+ authors:
7
+ - Jonas Brusman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-10-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk-sesv2
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.27'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '1.27'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mail
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.8.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 2.8.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: net-smtp
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: nokogiri
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '1'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '3.8'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '3.8'
97
+ - !ruby/object:Gem::Dependency
98
+ name: standard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '='
102
+ - !ruby/object:Gem::Version
103
+ version: 1.41.1
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '='
109
+ - !ruby/object:Gem::Version
110
+ version: 1.41.1
111
+ description: Ruby Mail delivery method handler for Amazon SES
112
+ email: platform@teamtailor.com
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - CHANGELOG.md
118
+ - LICENSE
119
+ - README.md
120
+ - lib/mail-ses.rb
121
+ - lib/mail/ses.rb
122
+ - lib/mail/ses/message_validator.rb
123
+ - lib/mail/ses/options_builder.rb
124
+ - lib/mail/ses/version.rb
125
+ homepage: https://github.com/teamtailor/mail-ses
126
+ licenses:
127
+ - MIT
128
+ metadata:
129
+ rubygems_mfa_required: 'true'
130
+ post_install_message:
131
+ rdoc_options: []
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: 3.0.0
139
+ required_rubygems_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">"
142
+ - !ruby/object:Gem::Version
143
+ version: 1.3.1
144
+ requirements: []
145
+ rubygems_version: 3.4.10
146
+ signing_key:
147
+ specification_version: 4
148
+ summary: Ruby Mail delivery method handler for Amazon SES
149
+ test_files: []