send_grid_mailer 0.5.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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