mailjet 1.5.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +301 -190
- data/Rakefile +19 -14
- data/lib/generators/mailjet/initializer_generator.rb +35 -0
- data/lib/generators/mailjet/templates/mailjet.rb.erb +8 -0
- data/lib/mailjet/api_error.rb +19 -20
- data/lib/mailjet/configuration.rb +12 -12
- data/lib/mailjet/connection.rb +24 -13
- data/lib/mailjet/mailer.rb +93 -48
- data/lib/mailjet/resource.rb +83 -71
- data/lib/mailjet/resources/campaigndraft.rb +1 -1
- data/lib/mailjet/resources/campaigndraft_detailcontent.rb +1 -1
- data/lib/mailjet/resources/campaigndraft_schedule.rb +1 -1
- data/lib/mailjet/resources/campaigndraft_send.rb +1 -1
- data/lib/mailjet/resources/campaigndraft_status.rb +1 -1
- data/lib/mailjet/resources/campaigndraft_test.rb +1 -1
- data/lib/mailjet/resources/contact_getcontactslists.rb +17 -0
- data/lib/mailjet/resources/messagehistory.rb +16 -0
- data/lib/mailjet/resources/newsletter.rb +1 -2
- data/lib/mailjet/resources/newsletter_schedule.rb +1 -1
- data/lib/mailjet/resources/statcounters.rb +33 -0
- data/lib/mailjet/resources/template_detailcontent.rb +18 -2
- data/lib/mailjet/version.rb +1 -1
- metadata +23 -122
data/Rakefile
CHANGED
@@ -1,17 +1,22 @@
|
|
1
|
-
|
2
|
-
require 'rake/testtask'
|
3
|
-
require 'bundler'
|
4
|
-
Bundler::GemHelper.install_tasks
|
1
|
+
require "rspec/core/rake_task"
|
5
2
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
3
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
4
|
+
t.pattern = [
|
5
|
+
"spec/mailjet/api_error_spec.rb",
|
6
|
+
"spec/mailjet/apikey_spec.rb",
|
7
|
+
"spec/mailjet/mailer_spec.rb",
|
8
|
+
"spec/mailjet/resource_spec.rb",
|
9
|
+
"spec/configuration_spec.rb",
|
10
|
+
"spec/mailjet_spec.rb",
|
11
|
+
"spec/resources/contact_spec.rb",
|
12
|
+
"spec/resources/contactmetadata_spec.rb",
|
13
|
+
"spec/resources/messagehistory_spec.rb",
|
14
|
+
"spec/resources/getcontactslists_spec.rb",
|
15
|
+
"spec/resources/template_detailcontent_spec.rb",
|
16
|
+
"spec/resources/integration_spec.rb",
|
17
|
+
"spec/resources/newsletter_spec.rb",
|
18
|
+
"spec/resources/statcounters_spec.rb",
|
19
|
+
]
|
14
20
|
end
|
15
21
|
|
16
|
-
|
17
|
-
task :default => :spec
|
22
|
+
task default: [:spec]
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Mailjet
|
2
|
+
class InitializerGenerator < Rails::Generators::Base
|
3
|
+
desc 'This generator creates an initializer file mailjet.rb at config/initializers'
|
4
|
+
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
6
|
+
|
7
|
+
def generate_initializer_file
|
8
|
+
config_file_path = 'config/initializers/mailjet.rb'
|
9
|
+
|
10
|
+
say('Hey! We’re about to configure your Mailjet credentials for your application.')
|
11
|
+
say('You can find them on your account (https://app.mailjet.com/account/api_keys).')
|
12
|
+
say('Please help yourself by providing some intel:')
|
13
|
+
|
14
|
+
@api_key = ask('API key: ')
|
15
|
+
@secret_key = ask('Secret key: ')
|
16
|
+
@default_from = ask('Sender address:')
|
17
|
+
|
18
|
+
say("Don't forget that your sender address '#{@default_from}' has to be validated first on https://app.mailjet.com/account/sender.")
|
19
|
+
|
20
|
+
if @api_v3_1 = yes?('Do you want to use Mailjet API v3.1 for sending your emails? (y/n)')
|
21
|
+
@api_v3_1_notice = %{
|
22
|
+
Mailjet API v3.1 is at the moment limited to Send API.
|
23
|
+
We’ve not set the version to it directly since there is no other endpoint in that version.
|
24
|
+
We recommend you create a dedicated instance of the wrapper set with it to send your emails.
|
25
|
+
If you're only using the gem to send emails, then you can safely set it to this version.
|
26
|
+
Otherwise, you can remove the dedicated line into #{config_file_path}.
|
27
|
+
|
28
|
+
}
|
29
|
+
say(@api_v3_1_notice)
|
30
|
+
end
|
31
|
+
|
32
|
+
template 'mailjet.rb.erb', config_file_path
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# kindly generated by appropriated Rails generator
|
2
|
+
Mailjet.configure do |config|
|
3
|
+
config.api_key = '<%= @api_key %>'
|
4
|
+
config.secret_key = '<%= @secret_key %>'
|
5
|
+
config.default_from = '<%= @default_from %>'
|
6
|
+
<% if @api_v3_1 %><%= @api_v3_1_notice.split("\n").reject(&:empty?).map{ |l| ' # ' + l }.join("\n") %>
|
7
|
+
config.api_version = 'v3.1'<% end %>
|
8
|
+
end
|
data/lib/mailjet/api_error.rb
CHANGED
@@ -1,29 +1,28 @@
|
|
1
|
-
|
2
|
-
require 'active_support'
|
1
|
+
require "json"
|
3
2
|
|
4
3
|
module Mailjet
|
5
4
|
class ApiError < StandardError
|
5
|
+
attr_reader :code, :reason
|
6
6
|
|
7
|
-
|
7
|
+
# @param code [Integer] HTTP response status code
|
8
|
+
# @param body [String] JSON response body
|
9
|
+
# @param request [Object] any request object
|
10
|
+
# @param url [String] request URL
|
11
|
+
# @param params [Hash] request headers and parameters
|
12
|
+
def initialize(code, body, request, url, params)
|
13
|
+
@code = code
|
14
|
+
@reason = begin
|
15
|
+
resdec = JSON.parse(body)
|
16
|
+
resdec['ErrorMessage']
|
17
|
+
rescue JSON::ParserError
|
18
|
+
body
|
19
|
+
end
|
8
20
|
|
21
|
+
message = "error #{code} while sending #{request.inspect} to #{url} with #{params.inspect}"
|
22
|
+
error_details = body.inspect
|
23
|
+
hint = "Please see https://dev.mailjet.com/guides/#status-codes for more informations on error numbers."
|
9
24
|
|
10
|
-
|
11
|
-
self.code = code
|
12
|
-
self.reason = ""
|
13
|
-
unless res.blank?
|
14
|
-
resdec = ActiveSupport::JSON.decode(res)
|
15
|
-
self.reason = resdec['ErrorMessage']
|
16
|
-
end
|
17
|
-
# code is ugly, output is pretty
|
18
|
-
super("error #{code} while sending #{request.inspect} to #{request_path} with #{params.inspect}\n\n" +
|
19
|
-
if res['errors'].present?
|
20
|
-
[(res['errors'] || [])].flatten.map do |param, text|
|
21
|
-
[param, text].map(&:to_s).reject(&:blank?).join(': ')
|
22
|
-
end.join("\n")
|
23
|
-
else
|
24
|
-
res.inspect
|
25
|
-
end + "\n\nPlease see https://dev.mailjet.com/guides/#status-codes for more informations on error numbers.\n\n"
|
26
|
-
)
|
25
|
+
super("#{message}\n\n#{error_details}\n\n#{hint}\n\n")
|
27
26
|
end
|
28
27
|
end
|
29
28
|
end
|
@@ -2,18 +2,18 @@ require 'active_support/core_ext/module/attribute_accessors'
|
|
2
2
|
|
3
3
|
module Mailjet
|
4
4
|
module Configuration
|
5
|
-
mattr_accessor :api_key
|
6
|
-
mattr_accessor :secret_key
|
7
|
-
mattr_accessor :default_from
|
8
|
-
mattr_accessor :api_version do
|
9
|
-
'v3'
|
10
|
-
end
|
11
|
-
mattr_accessor :end_point do
|
12
|
-
'https://api.mailjet.com'
|
13
|
-
end
|
14
|
-
mattr_accessor :perform_api_call do
|
15
|
-
true
|
16
|
-
end
|
5
|
+
mattr_accessor :api_key, :secret_key, :default_from
|
17
6
|
|
7
|
+
DEFAULT = {
|
8
|
+
api_version: 'v3',
|
9
|
+
sandbox_mode: false,
|
10
|
+
end_point: 'https://api.mailjet.com',
|
11
|
+
perform_api_call: true,
|
12
|
+
}
|
13
|
+
|
14
|
+
DEFAULT.each do |param, default_value|
|
15
|
+
mattr_accessor param
|
16
|
+
self.send("#{param}=", default_value)
|
17
|
+
end
|
18
18
|
end
|
19
19
|
end
|
data/lib/mailjet/connection.rb
CHANGED
@@ -6,7 +6,7 @@ require 'json'
|
|
6
6
|
module Mailjet
|
7
7
|
class Connection
|
8
8
|
|
9
|
-
attr_accessor :adapter, :public_operations, :read_only
|
9
|
+
attr_accessor :adapter, :public_operations, :read_only, :perform_api_call, :read_timeout, :open_timeout
|
10
10
|
alias :read_only? :read_only
|
11
11
|
|
12
12
|
delegate :options, :concat_urls, :url, to: :adapter
|
@@ -35,38 +35,43 @@ module Mailjet
|
|
35
35
|
adapter_class = options[:adapter_class] || RestClient::Resource
|
36
36
|
self.public_operations = options[:public_operations] || []
|
37
37
|
self.read_only = options[:read_only]
|
38
|
+
self.read_timeout = options[:read_timeout]
|
39
|
+
self.open_timeout = options[:open_timeout]
|
38
40
|
# self.adapter = adapter_class.new(end_point, options.merge(user: api_key, password: secret_key, :verify_ssl => false, content_type: 'application/json'))
|
39
|
-
self.adapter = adapter_class.new(end_point, options.merge(user: api_key, password: secret_key, content_type: 'application/json'))
|
41
|
+
self.adapter = adapter_class.new(end_point, options.merge(user: api_key, password: secret_key, content_type: 'application/json', read_timeout: self.read_timeout, open_timeout: self.open_timeout))
|
42
|
+
self.perform_api_call = options.key?(:perform_api_call) ? options[:perform_api_call] : true
|
40
43
|
end
|
41
44
|
|
42
|
-
def get(additional_headers = {},
|
43
|
-
handle_api_call(:get, additional_headers,
|
45
|
+
def get(additional_headers = {}, &block)
|
46
|
+
handle_api_call(:get, additional_headers, &block)
|
44
47
|
end
|
45
48
|
|
46
|
-
def post(payload, additional_headers = {},
|
47
|
-
handle_api_call(:post, additional_headers, payload,
|
49
|
+
def post(payload, additional_headers = {}, &block)
|
50
|
+
handle_api_call(:post, additional_headers, payload, &block)
|
48
51
|
end
|
49
52
|
|
50
|
-
def put(payload, additional_headers = {},
|
51
|
-
handle_api_call(:put, additional_headers, payload,
|
53
|
+
def put(payload, additional_headers = {}, &block)
|
54
|
+
handle_api_call(:put, additional_headers, payload, &block)
|
52
55
|
end
|
53
56
|
|
54
|
-
def delete(additional_headers = {},
|
55
|
-
handle_api_call(:delete, additional_headers,
|
57
|
+
def delete(additional_headers = {}, &block)
|
58
|
+
handle_api_call(:delete, additional_headers, &block)
|
56
59
|
end
|
57
60
|
|
58
61
|
private
|
59
62
|
|
60
|
-
def handle_api_call(method, additional_headers = {}, payload = {},
|
63
|
+
def handle_api_call(method, additional_headers = {}, payload = {}, &block)
|
61
64
|
formatted_payload = (additional_headers[:content_type] == :json) ? payload.to_json : payload
|
62
65
|
raise Mailjet::MethodNotAllowed unless method_allowed(method)
|
63
66
|
|
64
|
-
if perform_api_call
|
67
|
+
if self.perform_api_call
|
65
68
|
if [:get, :delete].include?(method)
|
66
69
|
@adapter.send(method, additional_headers, &block)
|
67
70
|
else
|
68
71
|
@adapter.send(method, formatted_payload, additional_headers, &block)
|
69
72
|
end
|
73
|
+
else
|
74
|
+
return {'Count' => 0, 'Data' => [mock_api_call: true], 'Total' => 0}.to_json
|
70
75
|
end
|
71
76
|
rescue RestClient::Exception => e
|
72
77
|
handle_exception(e, additional_headers, formatted_payload)
|
@@ -82,7 +87,13 @@ module Mailjet
|
|
82
87
|
formatted_payload = (additional_headers[:content_type] == :json) ? JSON.parse(payload) : payload
|
83
88
|
params = params.merge(formatted_payload)
|
84
89
|
|
85
|
-
|
90
|
+
http_body = if e.http_headers[:content_type] == "application/json"
|
91
|
+
e.http_body
|
92
|
+
else
|
93
|
+
"{}"
|
94
|
+
end
|
95
|
+
|
96
|
+
raise Mailjet::ApiError.new(e.http_code, http_body, @adapter, @adapter.url, params)
|
86
97
|
end
|
87
98
|
|
88
99
|
end
|
data/lib/mailjet/mailer.rb
CHANGED
@@ -26,60 +26,75 @@ ActionMailer::Base.add_delivery_method :mailjet, Mailjet::Mailer
|
|
26
26
|
# Mailjet sends API expects a JSON payload as the input.
|
27
27
|
# The deliver methods maps the Mail::Message attributes to the MailjetSend API JSON expected structure
|
28
28
|
class Mailjet::APIMailer
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
29
|
+
V3_0_PERMITTED_OPTIONS = [
|
30
|
+
:recipients, :'mj-prio', :'mj-campaign', :'mj-deduplicatecampaign',
|
31
|
+
:'mj-templatelanguage', :'mj-templateerrorreporting', :'mj-templateerrordeliver', :'mj-templateid',
|
32
|
+
:'mj-trackopen', :'mj-trackclick',
|
33
|
+
:'mj-customid', :'mj-eventpayload', :vars, :headers,
|
34
|
+
]
|
35
|
+
|
36
|
+
V3_1_PERMITTED_OPTIONS = [
|
37
|
+
:'Priority', :'CustomCampaign', :'DeduplicateCampaign',
|
38
|
+
:'TemplateLanguage', :'TemplateErrorReporting', :'TemplateErrorDeliver', :'TemplateID',
|
39
|
+
:'TrackOpens', :'TrackClicks',
|
40
|
+
:'CustomID', :'EventPayload', :'Variables', :'Headers',
|
41
|
+
]
|
42
|
+
|
43
|
+
CONNECTION_PERMITTED_OPTIONS = [:api_key, :secret_key]
|
44
|
+
|
45
|
+
HEADER_BLACKLIST = [
|
46
|
+
'from', 'sender', 'subject', 'to', 'cc', 'bcc', 'return-path', 'delivered-to', 'dkim-signature',
|
47
|
+
'domainkey-status', 'received-spf', 'authentication-results', 'received', 'user-agent', 'x-mailer',
|
48
|
+
'x-feedback-id', 'list-id', 'date', 'x-csa-complaints', 'message-id', 'reply-to', 'content-type',
|
49
|
+
'mime-version', 'content-transfer-encoding'
|
50
|
+
]
|
51
|
+
|
52
|
+
def initialize(opts = {})
|
53
|
+
options = HashWithIndifferentAccess.new(opts)
|
54
|
+
|
55
|
+
@version = options[:version]
|
56
|
+
@delivery_method_options_v3_0 = options.slice(*V3_0_PERMITTED_OPTIONS)
|
57
|
+
@delivery_method_options_v3_1 = options.slice(*V3_1_PERMITTED_OPTIONS)
|
58
|
+
@connection_options = options.slice(*CONNECTION_PERMITTED_OPTIONS)
|
50
59
|
end
|
51
60
|
|
52
|
-
def deliver!(mail,
|
53
|
-
|
61
|
+
def deliver!(mail, opts = {})
|
62
|
+
options = HashWithIndifferentAccess.new(opts)
|
54
63
|
|
55
|
-
|
56
|
-
|
57
|
-
end
|
64
|
+
# Mailjet Send API does not support full from. Splitting the from field into two: name and email address
|
65
|
+
mail[:from] ||= Mailjet.config.default_from if Mailjet.config.default_from
|
58
66
|
|
59
|
-
if (
|
60
|
-
|
61
|
-
end
|
67
|
+
# add `@connection_options` in `options` only if not exist yet (values in `options` prime)
|
68
|
+
options.reverse_merge!(@connection_options)
|
62
69
|
|
63
|
-
#
|
64
|
-
|
65
|
-
mail[:from] = Mailjet.config.default_from
|
66
|
-
end
|
70
|
+
# add `@version` in options if set
|
71
|
+
options[:version] = @version if @version
|
67
72
|
|
68
|
-
|
69
|
-
|
73
|
+
# `options[:version]` primes on global config
|
74
|
+
version = options[:version] || Mailjet.config.api_version
|
75
|
+
|
76
|
+
if (version == 'v3.1')
|
77
|
+
Mailjet::Send.create({ :Messages => [setContentV3_1(mail)], SandboxMode: Mailjet.config.sandbox_mode }, options)
|
70
78
|
else
|
71
|
-
Mailjet::Send.create(setContentV3_0(mail))
|
79
|
+
Mailjet::Send.create(setContentV3_0(mail), options)
|
72
80
|
end
|
73
81
|
end
|
74
82
|
|
75
83
|
def setContentV3_1(mail)
|
76
84
|
content = {}
|
85
|
+
|
77
86
|
content[:TextPart] = mail.text_part.try(:decoded) if !mail.text_part.blank?
|
78
87
|
content[:HTMLPart] = mail.html_part.try(:decoded) if !mail.html_part.blank?
|
79
88
|
|
89
|
+
# try message `body` as fallback if no content found
|
90
|
+
unless content[:TextPart] || content[:HTMLPart] || mail.body.try(:raw_source).empty?
|
91
|
+
content[mail.content_type.try(:include?,'text/html') ? :HTMLPart : :TextPart] = mail.body.raw_source
|
92
|
+
end
|
93
|
+
|
94
|
+
|
80
95
|
if mail.attachments.any?
|
81
96
|
content[:Attachments] = []
|
82
|
-
content[:
|
97
|
+
content[:InlinedAttachments] = []
|
83
98
|
|
84
99
|
mail.attachments.each do |attachment|
|
85
100
|
mailjet_attachment = {
|
@@ -90,7 +105,7 @@ class Mailjet::APIMailer
|
|
90
105
|
|
91
106
|
if attachment.inline?
|
92
107
|
mailjet_attachment['ContentId'] = attachment.content_id
|
93
|
-
content[:
|
108
|
+
content[:InlinedAttachments].push(mailjet_attachment)
|
94
109
|
else
|
95
110
|
content[:Attachments].push(mailjet_attachment)
|
96
111
|
end
|
@@ -102,20 +117,20 @@ class Mailjet::APIMailer
|
|
102
117
|
if mail.header && mail.header.fields.any?
|
103
118
|
content[:Headers] = {}
|
104
119
|
mail.header.fields.each do |header|
|
105
|
-
if header.name.start_with?('X-') && !header.name.start_with?('X-
|
120
|
+
if !header.name.start_with?('X-MJ') && !header.name.start_with?('X-Mailjet') && !HEADER_BLACKLIST.include?(header.name.downcase)
|
106
121
|
content[:Headers][header.name] = header.value
|
107
122
|
end
|
108
123
|
end
|
109
124
|
end
|
110
125
|
|
111
|
-
#
|
126
|
+
# ReplyTo property was added in v3.1
|
112
127
|
# Passing it as an header if mail.reply_to
|
113
128
|
|
114
|
-
if mail
|
115
|
-
if mail.reply_to.display_names.first
|
116
|
-
content[:
|
129
|
+
if mail[:reply_to]
|
130
|
+
if mail[:reply_to].respond_to?(:display_names) && mail[:reply_to].display_names.first
|
131
|
+
content[:ReplyTo] = {:Email=> mail[:reply_to].addresses.first, :Name=> mail[:reply_to].display_names.first}
|
117
132
|
else
|
118
|
-
content[:
|
133
|
+
content[:ReplyTo] = {:Email=> mail[:reply_to].addresses.first}
|
119
134
|
end
|
120
135
|
end
|
121
136
|
|
@@ -155,8 +170,13 @@ class Mailjet::APIMailer
|
|
155
170
|
ccs =[{:Email=>mail[:cc].address.first}]
|
156
171
|
end
|
157
172
|
else
|
173
|
+
ccs = []
|
158
174
|
mail[:cc].each do |cc|
|
159
|
-
|
175
|
+
if cc.display_name
|
176
|
+
ccs << {:Email=> cc.address, :Name=>cc.display_name}
|
177
|
+
else
|
178
|
+
ccs << {:Email=> cc.address}
|
179
|
+
end
|
160
180
|
end
|
161
181
|
end
|
162
182
|
end
|
@@ -169,7 +189,8 @@ class Mailjet::APIMailer
|
|
169
189
|
payload[:Bcc] = [{:Email=>mail[:bcc].address.first}]
|
170
190
|
end
|
171
191
|
else
|
172
|
-
|
192
|
+
bccs = []
|
193
|
+
mail[:bcc].each do |bcc|
|
173
194
|
if bcc.display_name
|
174
195
|
bccs << {:Email=> bcc.address, :Name=>bcc.display_name}
|
175
196
|
else
|
@@ -190,7 +211,26 @@ class Mailjet::APIMailer
|
|
190
211
|
payload[:Cc] = ccs if mail[:cc]
|
191
212
|
payload[:Bcc] = bccs if mail[:bcc]
|
192
213
|
|
193
|
-
payload
|
214
|
+
decode_emails_V3_1!(payload)
|
215
|
+
end
|
216
|
+
|
217
|
+
def decode_emails_V3_1!(payload)
|
218
|
+
# ActionMailer may have handed us encoded email
|
219
|
+
# addresses, mailjet will reject. Therefore we
|
220
|
+
# walk through the payload to decode them back.
|
221
|
+
payload.each do |key, value|
|
222
|
+
if key == :Email
|
223
|
+
payload[key] = Mail::Encodings.value_decode(value)
|
224
|
+
elsif value.is_a?(Hash)
|
225
|
+
decode_emails_V3_1! value
|
226
|
+
elsif value.is_a?(Array)
|
227
|
+
value.each do |item|
|
228
|
+
if item.is_a?(Hash)
|
229
|
+
decode_emails_V3_1! item
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
194
234
|
end
|
195
235
|
|
196
236
|
def setContentV3_0(mail)
|
@@ -199,6 +239,11 @@ class Mailjet::APIMailer
|
|
199
239
|
content[:text_part] = mail.text_part.try(:decoded) if !mail.text_part.blank?
|
200
240
|
content[:html_part] = mail.html_part.try(:decoded) if !mail.html_part.blank?
|
201
241
|
|
242
|
+
# try message `body` as fallback if no content found
|
243
|
+
unless content[:text_part] || content[:html_part] || mail.body.try(:raw_source).empty?
|
244
|
+
content[mail.content_type.try(:include?,'text/html') ? :html_part : :text_part] = mail.body.raw_source
|
245
|
+
end
|
246
|
+
|
202
247
|
# Formatting attachments (inline + regular)
|
203
248
|
unless mail.attachments.empty?
|
204
249
|
content[:attachments] = []
|
@@ -252,7 +297,7 @@ class Mailjet::APIMailer
|
|
252
297
|
payload.merge(content)
|
253
298
|
.merge(base_from)
|
254
299
|
.merge(@delivery_method_options_v3_0)
|
255
|
-
|
300
|
+
end
|
256
301
|
end
|
257
302
|
|
258
303
|
ActionMailer::Base.add_delivery_method :mailjet_api, Mailjet::APIMailer
|