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