send_grid_mailer 0.5.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9fc4e94f60e1028da201de83ef0e353105a1135b
4
- data.tar.gz: a6e7a11542b6160bbdf77b3313bb09fca37a4d34
3
+ metadata.gz: d8269564f91417e0b4e8813c6be1db5f04be828a
4
+ data.tar.gz: b49dbc23ca7b63e7f8be14c8a47340bb0cda26ad
5
5
  SHA512:
6
- metadata.gz: 18c31da2785607f422c4bacb5a4454a0852f32865f42aa1a2826f93e25d27ecf003dea9a547dceac7e3a8d44eeabbaaf45009dbdbdb2b03d7d8de19fc340f6fd
7
- data.tar.gz: 22ebd4fc60adc44d42a4be3fa0fa8716248f63cc69aacb3d506a5787553145023f41234af415ec4d7c4b15bd6fc0f63fa05a2ab4160f58be3e1e5bc54b4ca234
6
+ metadata.gz: 420118f02069b82f5a6779f481dfb32a0181e29e191b64c31f7afed86fcd4df8b361c7433128ce1504175b3459a47e1de84de119397e4c5bdfdf5d4848290161
7
+ data.tar.gz: 75f581ffef9bdba13de8fa9c4790ebd0fef055d2aef4f38f987966b73d962cb678e4f67dd2a7d52497faf2bff163143d8ed56a9ea4f727ae2c68f1ddb0e33efc
@@ -2,6 +2,17 @@
2
2
  All notable changes to this project will be documented in this file.
3
3
  This project adheres to [Semantic Versioning](http://semver.org/).
4
4
 
5
+ ### v1.0.0
6
+
7
+ ##### Added
8
+
9
+ * Implement "a version" of Recipients Interceptor https://github.com/croaky/recipient_interceptor compatible with SendGrid.
10
+ * Raise exceptions when api fails.
11
+
12
+ ##### Removed
13
+
14
+ * Remove ability to set templates by name
15
+
5
16
  ### v0.5.0
6
17
 
7
18
  ##### Changed
data/README.md CHANGED
@@ -141,11 +141,6 @@ class TestMailer < ApplicationMailer
141
141
  mail
142
142
  end
143
143
 
144
- def my_email # through template's name
145
- set_template_name("my template name")
146
- mail
147
- end
148
-
149
144
  def my_email # through mail method's params
150
145
  mail(template_id: "XXX")
151
146
  end
@@ -164,7 +159,31 @@ class TestMailer < ApplicationMailer
164
159
  end
165
160
  ```
166
161
 
167
- > Remember: you need to specify al least: `body`, `template`, `template name` or a Rails template.
162
+ > Remember: you need to specify al least: `body`, `template_id` or a Rails template.
163
+
164
+ ## Recipient Interceptor
165
+
166
+ This gem is compatible with [Recipient Interceptor gem](https://github.com/croaky/recipient_interceptor/tree/v0.1.2).
167
+ However, this gem only uses its configuration. Internally, we modify the behaviour to play nice with [sengrid-ruby](https://github.com/sendgrid/sendgrid-ruby) gem.
168
+ So, the current code is based on [Recipient Interceptor v0.1.2](https://github.com/croaky/recipient_interceptor/tree/v0.1.2). New versions may not work.
169
+
170
+ To make it work...
171
+
172
+ Add to your Gemfile:
173
+
174
+ ```ruby
175
+ gem "send_grid_mailer"
176
+ gem "recipient_interceptor"
177
+ ```
178
+
179
+ In, for example, your `/my-project/config/environments/development.rb` file:
180
+
181
+ ```ruby
182
+ Mail.register_interceptor RecipientInterceptor.new(
183
+ ENV["EMAIL_RECIPIENTS"],
184
+ subject_prefix: '[STAGING]'
185
+ )
186
+ ```
168
187
 
169
188
  ## Testing
170
189
 
@@ -0,0 +1,37 @@
1
+ module SendGridMailer
2
+ class Api
3
+ include Logger
4
+
5
+ SUCCESS_CODE = 202
6
+
7
+ def initialize(api_key)
8
+ @api_key = api_key || raise(SendGridMailer::InvalidApiKey)
9
+ end
10
+
11
+ def send_mail(sg_definition)
12
+ response = sg_api.client.mail._('send').post(request_body: sg_definition.to_json)
13
+ handle_response(response)
14
+ end
15
+
16
+ private
17
+
18
+ def handle_response(response)
19
+ if response.status_code.to_i != SUCCESS_CODE
20
+ errors = response_errors(response)
21
+ log_api_error_response(response.status_code, errors)
22
+ raise SendGridMailer::ApiError.new(response.status_code, errors)
23
+ end
24
+
25
+ log_api_success_response(response)
26
+ response
27
+ end
28
+
29
+ def response_errors(response)
30
+ JSON.parse(response.body)["errors"]
31
+ end
32
+
33
+ def sg_api
34
+ @sg_api ||= SendGrid::API.new(api_key: @api_key)
35
+ end
36
+ end
37
+ end
@@ -3,7 +3,6 @@ module SendGridMailer
3
3
  METHODS = [
4
4
  :substitute,
5
5
  :set_template_id,
6
- :set_template_name,
7
6
  :set_sender,
8
7
  :set_recipients,
9
8
  :set_subject,
@@ -12,8 +11,6 @@ module SendGridMailer
12
11
  :add_header
13
12
  ]
14
13
 
15
- attr_reader :template_name
16
-
17
14
  def substitute(key, value, default = "")
18
15
  personalization.substitutions = SendGrid::Substitution.new(
19
16
  key: key, value: value.to_s || default
@@ -25,11 +22,6 @@ module SendGridMailer
25
22
  mail.template_id = value
26
23
  end
27
24
 
28
- def set_template_name(value)
29
- return unless value
30
- @template_name = value
31
- end
32
-
33
25
  def set_sender(email)
34
26
  return unless email
35
27
  matched_format = email.match(/<(.+)>/)
@@ -84,6 +76,10 @@ module SendGridMailer
84
76
  @mail ||= SendGrid::Mail.new
85
77
  end
86
78
 
79
+ def clean_recipients(mode)
80
+ personalization.instance_variable_set("@#{mode}s", nil)
81
+ end
82
+
87
83
  def personalization
88
84
  @personalization ||= SendGrid::Personalization.new
89
85
  end
@@ -97,7 +93,5 @@ module SendGridMailer
97
93
  def subject?; !personalization.subject.blank? end
98
94
 
99
95
  def template_id?; !mail.template_id.blank? end
100
-
101
- def template_name?; !template_name.blank? end
102
96
  end
103
97
  end
@@ -1,47 +1,24 @@
1
1
  module SendGridMailer
2
2
  class Deliverer
3
- attr_accessor :settings
3
+ include InterceptorsHandler
4
+ include Logger
4
5
 
5
- def initialize(settings)
6
- self.settings = settings.merge(return_response: true)
7
- end
8
-
9
- def api_key
10
- settings[:api_key] || raise(SendGridMailer::Exception.new("Missing sendgrid API key"))
11
- end
12
-
13
- def deliver!(msg)
14
- set_template_id_from_name(msg.sg_definition)
15
- logger = SendGridMailer::Logger.new(msg.sg_definition)
16
- logger.log_definition
17
- response = sg_api.client.mail._('send').post(request_body: msg.sg_definition.to_json)
18
- logger.log_result(response)
19
- response
6
+ def deliver!(sg_definition)
7
+ execute_interceptors(sg_definition)
8
+ log_definition(sg_definition)
9
+ sg_api.send_mail(sg_definition)
20
10
  end
21
11
 
22
12
  private
23
13
 
24
- def set_template_id_from_name(definition)
25
- return unless definition.template_name
26
- response = sg_api.client.templates.get
27
-
28
- if response.status_code != "200"
29
- m = "Error trying to get templates. Status Code: #{response.status_code}"
30
- raise SendGridMailer::Exception.new(m)
31
- end
32
-
33
- JSON.parse(response.body)["templates"].each do |tpl|
34
- definition.set_template_id(tpl["id"]) if tpl["name"] == definition.template_name
35
- end
36
-
37
- if !definition.template_id?
38
- m = "No template with name #{definition.template_name}"
39
- raise SendGridMailer::Exception.new(m)
40
- end
14
+ def sg_api
15
+ @sg_api ||= Api.new(api_key)
41
16
  end
42
17
 
43
- def sg_api
44
- @sg_api ||= SendGrid::API.new(api_key: api_key)
18
+ def api_key
19
+ Rails.application.config.action_mailer.sendgrid_settings[:api_key]
20
+ rescue
21
+ nil
45
22
  end
46
23
  end
47
24
  end
@@ -10,8 +10,10 @@ module SendGridMailer
10
10
  initializer "initialize" do
11
11
  require_relative "./errors"
12
12
  require_relative "./logger"
13
+ require_relative "./api"
14
+ require_relative "./interceptors_handler"
15
+ require_relative "./interceptor/recipient_interceptor"
13
16
  require_relative "./definition"
14
- require_relative "./mail_message_ext"
15
17
  require_relative "./mailer_base_ext"
16
18
  end
17
19
 
@@ -1,2 +1,20 @@
1
- class SendGridMailer::Exception < ::Exception
1
+ module SendGridMailer
2
+ class Error < RuntimeError
3
+ end
4
+
5
+ class InvalidApiKey < Error
6
+ def initialize
7
+ super("the SendGrid API key is invalid or missing")
8
+ end
9
+ end
10
+
11
+ class ApiError < Error
12
+ attr_reader :error_code, :errors
13
+
14
+ def initialize(error_code, errors)
15
+ @error_code = error_code
16
+ @errors = errors
17
+ super("sendgrid api error")
18
+ end
19
+ end
2
20
  end
@@ -0,0 +1,42 @@
1
+ module SendGridMailer
2
+ module Interceptor
3
+ module RecipientInterceptor
4
+ def self.perform(sg_definition, interceptor)
5
+ add_custom_headers(sg_definition)
6
+ add_recipients(sg_definition, interceptor)
7
+ add_subject_prefix(sg_definition, interceptor)
8
+ end
9
+
10
+ def self.exec_recipients_interceptor(sg_definition, interceptor)
11
+ add_custom_headers(sg_definition)
12
+ add_recipients(sg_definition, interceptor)
13
+ add_subject_prefix(sg_definition, interceptor)
14
+ end
15
+
16
+ def self.add_subject_prefix(sg_definition, interceptor)
17
+ subject_prefix = interceptor.instance_variable_get(:@subject_prefix)
18
+ subject = [subject_prefix, sg_definition.personalization.subject].join(" ").strip
19
+ sg_definition.set_subject(subject)
20
+ end
21
+
22
+ def self.add_recipients(sg_definition, interceptor)
23
+ recipients = interceptor.instance_variable_get(:@recipients)
24
+ sg_definition.clean_recipients(:to)
25
+ sg_definition.clean_recipients(:cc)
26
+ sg_definition.clean_recipients(:bcc)
27
+ sg_definition.set_recipients(:to, recipients)
28
+ end
29
+
30
+ def self.add_custom_headers(sg_definition)
31
+ {
32
+ 'X-Intercepted-To' => sg_definition.personalization.tos || [],
33
+ 'X-Intercepted-Cc' => sg_definition.personalization.ccs || [],
34
+ 'X-Intercepted-Bcc' => sg_definition.personalization.bccs || []
35
+ }.each do |header, addresses|
36
+ addresses_str = addresses.map { |a| a["email"] }.join(", ")
37
+ sg_definition.add_header(header, addresses_str)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,14 @@
1
+ module SendGridMailer
2
+ module InterceptorsHandler
3
+ def execute_interceptors(sg_definition)
4
+ registered_interceptors.each do |interceptor|
5
+ interceptor_class = "SendGridMailer::Interceptor::#{interceptor.class}".constantize
6
+ interceptor_class.perform(sg_definition, interceptor)
7
+ end
8
+ end
9
+
10
+ def registered_interceptors
11
+ ::Mail.class_variable_get(:@@delivery_interceptors)
12
+ end
13
+ end
14
+ end
@@ -1,56 +1,55 @@
1
1
  module SendGridMailer
2
- class Logger
3
- attr_reader :definition
2
+ module Logger
3
+ def log_definition(definition)
4
+ mail = definition.mail
5
+ personalization = definition.personalization
4
6
 
5
- def initialize(definition)
6
- @definition = definition
7
- end
8
-
9
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
10
- def log_definition
11
7
  data = {
12
8
  "Subject" => personalization.subject,
13
9
  "Template ID" => mail.template_id,
14
10
  "From" => log_email(mail.from),
15
- "To" => log_emails(:tos),
16
- "Cc" => log_emails(:ccs),
17
- "Bcc" => log_emails(:bccs),
11
+ "To" => log_emails(personalization, :tos),
12
+ "Cc" => log_emails(personalization, :ccs),
13
+ "Bcc" => log_emails(personalization, :bccs),
18
14
  "Substitutions" => log_pairs(personalization.substitutions),
19
15
  "Headers" => log_pairs(personalization.headers),
20
- "body" => log_contents,
21
- "Attachments" => log_attachments
16
+ "body" => log_contents(mail),
17
+ "Attachments" => log_attachments(mail)
22
18
  }
23
19
 
24
- data = data.keys.map do |k|
25
- d = data[k].to_s
26
- "#{k}: #{(d.blank? ? '-' : d)}"
27
- end.join("\n")
20
+ log(build_definition_message(data))
21
+ end
28
22
 
29
- Rails.logger.info("\n#{data}")
23
+ def log_api_success_response(response)
24
+ log("The E-mail was successfully sent :)\nStatus Code: #{response.status_code}")
30
25
  end
31
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
32
26
 
33
- def log_result(response)
34
- msg = "The E-mail was successfully sent :)\nStatus Code: #{response.status_code}"
27
+ def log_api_error_response(status_code, errors)
28
+ msg = "The E-mail was not sent :(\nStatus Code: #{status_code}\nErrors:"
29
+ msg += log_errors(errors)
30
+ log(msg)
31
+ end
35
32
 
36
- if response.status_code != "202"
37
- msg = "The E-mail was not sent :(\nStatus Code: #{response.status_code}\nErrors:"
38
- msg += log_errors(response.body)
39
- msg = msg
40
- end
33
+ private
41
34
 
35
+ def log(msg)
42
36
  Rails.logger.info("\n#{msg}")
43
37
  nil
44
38
  end
45
39
 
46
- private
40
+ def build_definition_message(data)
41
+ data = data.keys.map do |k|
42
+ d = data[k].to_s
43
+ "#{k}: #{(d.blank? ? '-' : d)}"
44
+ end.join("\n")
45
+ end
47
46
 
48
47
  def log_email(email)
49
48
  return if email.blank?
50
49
  email["email"]
51
50
  end
52
51
 
53
- def log_emails(origin)
52
+ def log_emails(personalization, origin)
54
53
  emails = personalization.send(origin)
55
54
  return if emails.blank?
56
55
  emails.map do |email|
@@ -58,14 +57,14 @@ module SendGridMailer
58
57
  end.join(", ")
59
58
  end
60
59
 
61
- def log_attachments
60
+ def log_attachments(mail)
62
61
  return if mail.attachments.blank?
63
62
  mail.attachments.map do |f|
64
63
  "\n\t#{f['filename']}"
65
64
  end.join("")
66
65
  end
67
66
 
68
- def log_contents
67
+ def log_contents(mail)
69
68
  return if mail.contents.blank?
70
69
  mail.contents.map do |content|
71
70
  "\n\ttype: #{content['type']}\n\tvalue: #{content['value']}"
@@ -79,12 +78,8 @@ module SendGridMailer
79
78
  end.join("")
80
79
  end
81
80
 
82
- def mail
83
- definition.mail
84
- end
85
-
86
- def log_errors(body)
87
- JSON.parse(body)["errors"].map do |error|
81
+ def log_errors(errors)
82
+ errors.map do |error|
88
83
  msg = []
89
84
  msg << "#{error['field']}: " if error['field']
90
85
  msg << error['message']
@@ -92,9 +87,5 @@ module SendGridMailer
92
87
  "\n\t* #{msg.join('')}"
93
88
  end.join("")
94
89
  end
95
-
96
- def personalization
97
- definition.personalization
98
- end
99
90
  end
100
91
  end
@@ -9,8 +9,7 @@ module ActionMailer
9
9
  end
10
10
 
11
11
  def mail(headers = {}, &_block)
12
- return old_mail(headers, &_block) if self.class.delivery_method != :sendgrid
13
- m = @_message
12
+ return old_mail(headers, &_block) unless enabled_sendgrid?
14
13
 
15
14
  # Call all the procs (if any)
16
15
  default_values = {}
@@ -24,9 +23,7 @@ module ActionMailer
24
23
 
25
24
  define_sg_mail(headers)
26
25
 
27
- wrap_delivery_behavior!
28
- @_mail_was_called = true
29
- m
26
+ SendGridMailer::Deliverer.new.deliver!(sg_definition)
30
27
  end
31
28
 
32
29
  private
@@ -60,7 +57,6 @@ module ActionMailer
60
57
  end
61
58
 
62
59
  def set_body(params)
63
- return if sg_definition.template_name?
64
60
  set_template_id(params[:template_id])
65
61
  return if sg_definition.template_id?
66
62
  set_content(params[:body], params[:content_type])
@@ -83,7 +79,11 @@ module ActionMailer
83
79
  end
84
80
 
85
81
  def sg_definition
86
- @_message.sg_definition
82
+ @sg_definition ||= SendGridMailer::Definition.new
83
+ end
84
+
85
+ def enabled_sendgrid?
86
+ self.class.delivery_method == :sendgrid
87
87
  end
88
88
  end
89
89
  end