send_grid_mailer 0.5.0 → 2.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 +5 -5
- data/.circleci/config.yml +102 -0
- data/.circleci/setup-rubygems.sh +3 -0
- data/.rubocop.yml +31 -591
- data/.ruby-version +1 -1
- data/CHANGELOG.md +39 -0
- data/README.md +69 -8
- data/lib/send_grid_mailer/api.rb +42 -0
- data/lib/send_grid_mailer/definition.rb +35 -23
- data/lib/send_grid_mailer/deliverer.rb +12 -35
- data/lib/send_grid_mailer/dev_deliverer.rb +70 -0
- data/lib/send_grid_mailer/engine.rb +7 -3
- data/lib/send_grid_mailer/errors.rb +20 -1
- data/lib/send_grid_mailer/interceptor/recipient_interceptor.rb +42 -0
- data/lib/send_grid_mailer/interceptors_handler.rb +14 -0
- data/lib/send_grid_mailer/logger.rb +36 -40
- data/lib/send_grid_mailer/mailer_base_ext.rb +18 -7
- data/lib/send_grid_mailer/version.rb +1 -1
- data/send_grid_mailer.gemspec +12 -6
- data/spec/dummy/Rakefile +1 -1
- data/spec/dummy/app/assets/config/manifest.js +3 -0
- data/spec/dummy/app/assets/stylesheets/application.css +3 -3
- data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
- data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
- data/spec/dummy/app/controllers/application_controller.rb +0 -3
- data/spec/dummy/app/{assets/javascripts → javascript/packs}/application.js +3 -1
- data/spec/dummy/app/jobs/application_job.rb +7 -0
- data/spec/dummy/app/mailers/application_mailer.rb +1 -1
- data/spec/dummy/app/mailers/test_mailer.rb +12 -5
- data/spec/dummy/app/models/application_record.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +10 -9
- data/spec/dummy/bin/rails +3 -3
- data/spec/dummy/bin/rake +2 -2
- data/spec/dummy/bin/setup +18 -14
- data/spec/dummy/config.ru +3 -1
- data/spec/dummy/config/application.rb +12 -22
- data/spec/dummy/config/boot.rb +3 -3
- data/spec/dummy/config/cable.yml +10 -0
- data/spec/dummy/config/database.yml +2 -2
- data/spec/dummy/config/environment.rb +1 -1
- data/spec/dummy/config/environments/development.rb +48 -18
- data/spec/dummy/config/environments/production.rb +63 -22
- data/spec/dummy/config/environments/test.rb +29 -12
- data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/spec/dummy/config/initializers/assets.rb +4 -3
- data/spec/dummy/config/initializers/backtrace_silencers.rb +4 -3
- data/spec/dummy/config/initializers/content_security_policy.rb +28 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +2 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +3 -1
- data/spec/dummy/config/initializers/permissions_policy.rb +11 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +2 -2
- data/spec/dummy/config/locales/en.yml +11 -1
- data/spec/dummy/config/puma.rb +43 -0
- data/spec/dummy/config/routes.rb +1 -54
- data/spec/dummy/config/storage.yml +34 -0
- data/spec/dummy/public/404.html +6 -6
- data/spec/dummy/public/422.html +6 -6
- data/spec/dummy/public/500.html +6 -6
- data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/spec/dummy/public/apple-touch-icon.png +0 -0
- data/spec/dummy/spec/lib/send_grid_mailer/definition_spec.rb +52 -23
- data/spec/dummy/spec/mailers/test_mailer_spec.rb +558 -327
- data/spec/rails_helper.rb +5 -7
- metadata +127 -43
- data/.hound.yml +0 -4
- data/.travis.yml +0 -15
- data/lib/send_grid_mailer/mail_message_ext.rb +0 -7
- data/spec/dummy/bin/bundle +0 -3
- data/spec/dummy/config/initializers/session_store.rb +0 -3
- data/spec/dummy/config/secrets.yml +0 -22
- data/spec/dummy/db/schema.rb +0 -16
- data/spec/dummy/spec/support/test_helpers.rb +0 -5
@@ -4,20 +4,24 @@ module SendGridMailer
|
|
4
4
|
|
5
5
|
config.generators do |g|
|
6
6
|
g.test_framework :rspec, fixture: false
|
7
|
-
g.fixture_replacement :
|
7
|
+
g.fixture_replacement :factory_bot, dir: "spec/factories"
|
8
8
|
end
|
9
9
|
|
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
|
|
18
|
-
initializer "
|
20
|
+
initializer "add_sendgrid_deliverers", before: "action_mailer.set_configs" do
|
21
|
+
require_relative "./dev_deliverer"
|
19
22
|
require_relative "./deliverer"
|
20
23
|
ActionMailer::Base.add_delivery_method(:sendgrid, SendGridMailer::Deliverer)
|
24
|
+
ActionMailer::Base.add_delivery_method(:sendgrid_dev, SendGridMailer::DevDeliverer)
|
21
25
|
end
|
22
26
|
end
|
23
27
|
end
|
@@ -1,2 +1,21 @@
|
|
1
|
-
|
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
|
+
error_message = errors.map { |err| err['message'] }.join('. ')
|
18
|
+
super("Sendgrid API error. Code: #{error_code}. Errors: #{error_message}")
|
19
|
+
end
|
20
|
+
end
|
2
21
|
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,72 +1,75 @@
|
|
1
1
|
module SendGridMailer
|
2
|
-
|
3
|
-
|
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
|
25
|
-
|
26
|
-
"#{k}: #{(d.blank? ? '-' : d)}"
|
27
|
-
end.join("\n")
|
20
|
+
log(build_definition_message(data))
|
21
|
+
end
|
28
22
|
|
29
|
-
|
23
|
+
def log_api_success_response(status_code, api_call_type)
|
24
|
+
log("Succesfully called the SendGrid API :)\nStatus Code: #{status_code}")
|
30
25
|
end
|
31
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
32
26
|
|
33
|
-
def
|
34
|
-
msg = "
|
27
|
+
def log_api_error_response(status_code, errors, api_call_type)
|
28
|
+
msg = "There was a problem calling the SendGrid API :(\nStatus Code: #{status_code}\nErrors:"
|
29
|
+
msg += log_errors(errors)
|
30
|
+
log(msg)
|
31
|
+
end
|
35
32
|
|
36
|
-
|
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
|
-
|
40
|
+
def build_definition_message(data)
|
41
|
+
data = data.keys.map do |k|
|
42
|
+
d = data[k].to_s
|
43
|
+
"#{k}: #{d.presence || '-'}"
|
44
|
+
end.join("\n")
|
45
|
+
end
|
47
46
|
|
48
47
|
def log_email(email)
|
49
48
|
return if email.blank?
|
49
|
+
|
50
50
|
email["email"]
|
51
51
|
end
|
52
52
|
|
53
|
-
def log_emails(origin)
|
53
|
+
def log_emails(personalization, origin)
|
54
54
|
emails = personalization.send(origin)
|
55
55
|
return if emails.blank?
|
56
|
+
|
56
57
|
emails.map do |email|
|
57
58
|
log_email(email)
|
58
59
|
end.join(", ")
|
59
60
|
end
|
60
61
|
|
61
|
-
def log_attachments
|
62
|
+
def log_attachments(mail)
|
62
63
|
return if mail.attachments.blank?
|
64
|
+
|
63
65
|
mail.attachments.map do |f|
|
64
66
|
"\n\t#{f['filename']}"
|
65
67
|
end.join("")
|
66
68
|
end
|
67
69
|
|
68
|
-
def log_contents
|
70
|
+
def log_contents(mail)
|
69
71
|
return if mail.contents.blank?
|
72
|
+
|
70
73
|
mail.contents.map do |content|
|
71
74
|
"\n\ttype: #{content['type']}\n\tvalue: #{content['value']}"
|
72
75
|
end.join("")
|
@@ -74,17 +77,14 @@ module SendGridMailer
|
|
74
77
|
|
75
78
|
def log_pairs(hash)
|
76
79
|
return if hash.blank?
|
80
|
+
|
77
81
|
hash.keys.map do |k|
|
78
82
|
"\n\t#{k} => #{hash[k]}"
|
79
83
|
end.join("")
|
80
84
|
end
|
81
85
|
|
82
|
-
def
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
def log_errors(body)
|
87
|
-
JSON.parse(body)["errors"].map do |error|
|
86
|
+
def log_errors(errors)
|
87
|
+
errors.map do |error|
|
88
88
|
msg = []
|
89
89
|
msg << "#{error['field']}: " if error['field']
|
90
90
|
msg << error['message']
|
@@ -92,9 +92,5 @@ module SendGridMailer
|
|
92
92
|
"\n\t* #{msg.join('')}"
|
93
93
|
end.join("")
|
94
94
|
end
|
95
|
-
|
96
|
-
def personalization
|
97
|
-
definition.personalization
|
98
|
-
end
|
99
95
|
end
|
100
96
|
end
|
@@ -9,8 +9,7 @@ module ActionMailer
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def mail(headers = {}, &_block)
|
12
|
-
return old_mail(headers, &_block)
|
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
|
-
|
28
|
-
@_mail_was_called = true
|
29
|
-
m
|
26
|
+
deliverer&.new&.deliver!(sg_definition)
|
30
27
|
end
|
31
28
|
|
32
29
|
private
|
@@ -60,11 +57,12 @@ 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?
|
62
|
+
|
66
63
|
set_content(params[:body], params[:content_type])
|
67
64
|
return if sg_definition.content?
|
65
|
+
|
68
66
|
set_body_from_tpl(params)
|
69
67
|
end
|
70
68
|
|
@@ -83,7 +81,20 @@ module ActionMailer
|
|
83
81
|
end
|
84
82
|
|
85
83
|
def sg_definition
|
86
|
-
@
|
84
|
+
@sg_definition ||= SendGridMailer::Definition.new
|
85
|
+
end
|
86
|
+
|
87
|
+
def deliverer
|
88
|
+
case self.class.delivery_method
|
89
|
+
when :sendgrid_dev
|
90
|
+
SendGridMailer::DevDeliverer
|
91
|
+
when :sendgrid
|
92
|
+
SendGridMailer::Deliverer
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def enabled_sendgrid?
|
97
|
+
[:sendgrid, :sendgrid_dev].include?(self.class.delivery_method)
|
87
98
|
end
|
88
99
|
end
|
89
100
|
end
|
data/send_grid_mailer.gemspec
CHANGED
@@ -19,13 +19,19 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
s.test_files = Dir["spec/**/*"]
|
21
21
|
|
22
|
+
s.add_dependency "handlebars", "~> 0.8.0"
|
23
|
+
s.add_dependency "letter_opener", "~> 1.7.0"
|
22
24
|
s.add_dependency "rails", ">= 4.2.0"
|
23
|
-
s.add_dependency "sendgrid-ruby", "~>
|
25
|
+
s.add_dependency "sendgrid-ruby", "~> 5", ">= 5.3.0"
|
26
|
+
|
27
|
+
s.add_development_dependency "coveralls"
|
28
|
+
s.add_development_dependency "factory_bot_rails"
|
29
|
+
s.add_development_dependency "guard-rspec", "~> 4.7"
|
24
30
|
s.add_development_dependency "pry"
|
25
31
|
s.add_development_dependency "pry-rails"
|
26
|
-
s.add_development_dependency "
|
27
|
-
s.add_development_dependency "
|
28
|
-
s.add_development_dependency "
|
29
|
-
s.add_development_dependency "
|
30
|
-
s.add_development_dependency "
|
32
|
+
s.add_development_dependency "rspec-rails", "~> 3.5.0"
|
33
|
+
s.add_development_dependency "rspec_junit_formatter"
|
34
|
+
s.add_development_dependency "rubocop", "0.65.0"
|
35
|
+
s.add_development_dependency "rubocop-rspec"
|
36
|
+
s.add_development_dependency "sqlite3", "~> 1.4"
|
31
37
|
end
|
data/spec/dummy/Rakefile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
2
2
|
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
3
3
|
|
4
|
-
|
4
|
+
require_relative "config/application"
|
5
5
|
|
6
6
|
Rails.application.load_tasks
|
@@ -6,9 +6,9 @@
|
|
6
6
|
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
7
|
*
|
8
8
|
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
-
* compiled file so the styles you add here take precedence over styles defined in any
|
10
|
-
*
|
11
|
-
* file per style scope.
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
12
|
*
|
13
13
|
*= require_tree .
|
14
14
|
*= require_self
|
@@ -5,9 +5,11 @@
|
|
5
5
|
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
6
|
//
|
7
7
|
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
-
// compiled file.
|
8
|
+
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
9
9
|
//
|
10
10
|
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
11
|
// about supported directives.
|
12
12
|
//
|
13
|
+
//= require rails-ujs
|
14
|
+
//= require activestorage
|
13
15
|
//= require_tree .
|
@@ -0,0 +1,7 @@
|
|
1
|
+
class ApplicationJob < ActiveJob::Base
|
2
|
+
# Automatically retry jobs that encountered a deadlock
|
3
|
+
# retry_on ActiveRecord::Deadlocked
|
4
|
+
|
5
|
+
# Most jobs are safe to ignore if the underlying records are no longer available
|
6
|
+
# discard_on ActiveJob::DeserializationError
|
7
|
+
end
|
@@ -44,11 +44,6 @@ class TestMailer < ApplicationMailer
|
|
44
44
|
mail
|
45
45
|
end
|
46
46
|
|
47
|
-
def template_name_email
|
48
|
-
set_template_name("my template name")
|
49
|
-
mail(body: "X")
|
50
|
-
end
|
51
|
-
|
52
47
|
def template_id_params_email
|
53
48
|
mail(template_id: "XXX")
|
54
49
|
end
|
@@ -83,4 +78,16 @@ class TestMailer < ApplicationMailer
|
|
83
78
|
substitute "%key2%", "value2"
|
84
79
|
mail(body: "X")
|
85
80
|
end
|
81
|
+
|
82
|
+
def template_with_substitutions_email(value)
|
83
|
+
set_template_id("XXX")
|
84
|
+
substitute "%key%", value
|
85
|
+
mail(to: "r1@platan.us", body: "X")
|
86
|
+
end
|
87
|
+
|
88
|
+
def dynamic_template_email(value)
|
89
|
+
set_template_id("XXX")
|
90
|
+
dynamic_template_data(key: value)
|
91
|
+
mail(to: "r1@platan.us")
|
92
|
+
end
|
86
93
|
end
|