sparkpost_rails 1.3.0 → 1.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +103 -93
- data/lib/sparkpost_rails.rb +8 -0
- data/lib/sparkpost_rails/data_options.rb +25 -0
- data/lib/sparkpost_rails/delivery_method.rb +29 -15
- data/lib/sparkpost_rails/exceptions.rb +13 -0
- data/lib/sparkpost_rails/railtie.rb +6 -0
- data/lib/sparkpost_rails/version.rb +1 -1
- data/spec/attachments_spec.rb +1 -1
- data/spec/bcc_recipients_spec.rb +32 -11
- data/spec/cc_recipients_spec.rb +41 -20
- data/spec/configuration_spec.rb +12 -0
- data/spec/exceptions_spec.rb +65 -0
- data/spec/headers_spec.rb +1 -1
- data/spec/metadata_spec.rb +44 -0
- data/spec/recipient_specific_data_spec.rb +30 -0
- data/spec/recipients_spec.rb +17 -5
- data/spec/response_spec.rb +2 -1
- data/spec/spec_helper.rb +15 -9
- metadata +38 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bd9cf39367215bffb11029280b7526e52553b8484767d2823fc59448b9352c80
|
4
|
+
data.tar.gz: 662a03effd5a0332400c843e9a047f65dc34e823baabfa63ce3366867a3f4b69
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c62bb17ff1685deb530b442e4c022bf4eb88208d8ea988ad6b7baa4c9ee79d0e24180ae099db14d3635e9973367de9ce402dbd3f93418ba1c085a2732e828f4
|
7
|
+
data.tar.gz: 202997cc9e44bc84bfc4412b1d5f2f0e6b09246089cf3372506fe3606ce3ea383aec09ae99fbc7db31af27e96cd314f6555894cef2d19317d7cf0ad668519b4d
|
data/README.md
CHANGED
@@ -4,159 +4,154 @@
|
|
4
4
|
SparkPost Rails
|
5
5
|
===============
|
6
6
|
|
7
|
-
This gem provides seamless integration of SparkPost with ActionMailer. It provides a delivery_method based upon the SparkPost API, and
|
8
|
-
makes getting setup and sending email via SparkPost in a Rails app pretty painless.
|
7
|
+
This gem provides seamless integration of SparkPost with ActionMailer. It provides a `delivery_method` based upon the SparkPost API, and makes getting setup and sending email via SparkPost in a Rails app pretty painless.
|
9
8
|
|
10
9
|
Getting Started
|
11
10
|
---------------
|
12
11
|
|
13
12
|
Add the gem to your Gemfile
|
14
13
|
|
15
|
-
```
|
14
|
+
```ruby
|
16
15
|
gem 'sparkpost_rails'
|
17
16
|
```
|
18
17
|
|
19
18
|
Then run the bundle command to install it.
|
20
19
|
|
21
|
-
By default, the gem will look for your SparkPost API key in your environment, with the key
|
22
|
-
setting by identifying a different key in the initializer (`config/initializers/sparkpost_rails.rb`):
|
20
|
+
By default, the gem will look for your SparkPost API key in your environment, with the key `SPARKPOST_API_KEY`. You can override this setting by identifying a different key in the initializer (`config/initializers/sparkpost_rails.rb`):
|
23
21
|
|
24
|
-
```
|
22
|
+
```ruby
|
25
23
|
SparkPostRails.configure do |c|
|
26
24
|
c.api_key = 'YOUR API KEY'
|
27
25
|
end
|
28
26
|
```
|
27
|
+
Note that an initializer file is not required to use this gem. If an initializer is not provided, default values will be used. See ["Additional Configuration"](#additional-configuration) below for a list of all the default settings.
|
29
28
|
|
30
29
|
In each environment configuration file from which you want to send emails via Sparkpost, (i.e. `config/environments/production.rb`) add
|
31
30
|
|
32
|
-
```
|
31
|
+
```ruby
|
33
32
|
config.action_mailer.delivery_method = :sparkpost
|
34
33
|
```
|
35
34
|
|
36
35
|
Additional Configuration
|
37
36
|
------------------------
|
38
|
-
You can establish values for a number of SparkPost settings in the initializer.
|
39
|
-
from your application. You can override these settings on individual messages.
|
37
|
+
You can establish values for a number of SparkPost settings in the initializer. These values will be used for every message sent from your application. You can override these settings on individual messages.
|
40
38
|
|
41
|
-
```
|
39
|
+
```ruby
|
42
40
|
SparkPostRails.configure do |c|
|
43
|
-
c.
|
44
|
-
c.
|
45
|
-
c.
|
46
|
-
c.
|
47
|
-
c.
|
48
|
-
c.
|
49
|
-
c.
|
50
|
-
c.
|
51
|
-
c.
|
52
|
-
c.
|
41
|
+
c.api_endpoint = "https://api.eu.sparkpost.com/api/" # default: "https://api.sparkpost.com/api/"
|
42
|
+
c.sandbox = true # default: false
|
43
|
+
c.track_opens = true # default: false
|
44
|
+
c.track_clicks = true # default: false
|
45
|
+
c.return_path = 'BOUNCE-EMAIL@YOUR-DOMAIN.COM' # default: nil
|
46
|
+
c.campaign_id = 'YOUR-CAMPAIGN' # default: nil
|
47
|
+
c.transactional = true # default: false
|
48
|
+
c.ip_pool = "MY-POOL" # default: nil
|
49
|
+
c.inline_css = true # default: false
|
50
|
+
c.html_content_only = true # default: false
|
51
|
+
c.subaccount = "123" # default: nil
|
53
52
|
end
|
54
53
|
```
|
55
54
|
|
56
|
-
The default values for these optional configuration settings are:
|
57
|
-
|
58
|
-
```
|
59
|
-
sandbox = false
|
60
|
-
track_opens = false
|
61
|
-
track_clicks = false
|
62
|
-
return_path = nil
|
63
|
-
campaign_id = nil
|
64
|
-
transactional = false
|
65
|
-
ip_pool = nil
|
66
|
-
inline_css = false
|
67
|
-
html_content_only = false
|
68
|
-
subaccount = nil
|
69
|
-
```
|
70
|
-
|
71
55
|
Usage
|
72
56
|
-----
|
73
|
-
When calling the deliver
|
74
|
-
from SparkPost as a hash.
|
57
|
+
When calling the `deliver!` method on the mail object returned from your mailer, `SparkPostRails` provides the response data directly back from SparkPost as a hash.
|
75
58
|
|
76
|
-
```
|
59
|
+
```ruby
|
77
60
|
result = MyMailer.welcome_message(user).deliver!
|
78
61
|
```
|
79
62
|
|
80
63
|
Example:
|
81
64
|
|
65
|
+
```ruby
|
66
|
+
{
|
67
|
+
"total_rejected_recipients" => 0,
|
68
|
+
"total_accepted_recipients" => 1,
|
69
|
+
"id" => "00000000000000"
|
70
|
+
}
|
82
71
|
```
|
83
|
-
{"total_rejected_recipients"=>0, "total_accepted_recipients"=>1, "id"=>"00000000000000"}
|
84
|
-
```
|
85
72
|
|
86
|
-
If the SparkPost API
|
87
|
-
|
73
|
+
If the SparkPost API responds with an error condition, SparkPostRails will raise a `SparkPostRails::DeliveryException`, which will include all the message data returned by the API.
|
74
|
+
|
75
|
+
SparkPostRails will support multiple recipients, multiple CC, multiple BCC, ReplyTo address, file attachments, inline images, multi-part (HTML and plaintext) messages - all utilizing the standard `ActionMailer` methodologies.
|
88
76
|
|
89
|
-
|
90
|
-
|
77
|
+
Handling Errors
|
78
|
+
---------------
|
79
|
+
If you are using `ActiveJob` and wish to do something special when the SparkPost API responds with an error condition you can do so by rescuing these exceptions via `ActionMailer::DeliveryJob`. Simply add an initializer:
|
91
80
|
|
81
|
+
`config/initializers/action_mailer.rb`
|
92
82
|
|
93
|
-
|
83
|
+
```ruby
|
84
|
+
ActionMailer::DeliveryJob.rescue_from(SparkPostRails::DeliveryException) do |exception|
|
85
|
+
# do something special with the error
|
86
|
+
# do something special with the error
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
SparkPost-Specific Features
|
94
91
|
---------------------------
|
95
92
|
|
96
93
|
### Configuration Settings
|
97
|
-
You can
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
94
|
+
You can specify values for any or all of the configuration settings listed above on an individual message. Simply add a hash of these values to the mail message in a field named `sparkpost_data`:
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
data = {
|
98
|
+
track_opens: true,
|
99
|
+
track_clicks: false,
|
100
|
+
campaign_id: 'My Campaign',
|
101
|
+
transactional: true,
|
102
|
+
ip_pool: 'SPECIAL_POOL',
|
103
|
+
api_key: 'MESSAGE_SPECIFIC_API_KEY',
|
104
|
+
subaccount: '123'
|
105
|
+
}
|
109
106
|
|
110
107
|
mail(to: to_email, subject: "Test", body: "test", sparkpost_data: data)
|
111
108
|
```
|
112
109
|
|
113
|
-
Additionally, return_path can be
|
110
|
+
Additionally, `return_path` can be overridden on a specific email by setting that field on the mail message itself:
|
114
111
|
|
115
|
-
```
|
112
|
+
```ruby
|
116
113
|
mail(to: to_email, subject: "Test", body: "test", return_path: "bounces@example.com")
|
117
114
|
```
|
118
115
|
|
119
116
|
### Transmission Specific Settings
|
120
117
|
|
121
|
-
For an individual transmisison you can specifiy that SparkPost should ignore customer supression rules - if your SparkPost account allows for this
|
122
|
-
feature. Simply include the flag in the "sparkpost_data" field on the message:
|
118
|
+
For an individual transmisison you can specifiy that SparkPost should ignore customer supression rules - if your SparkPost account allows for this feature. Simply include the flag in the `sparkpost_data` field on the message:
|
123
119
|
|
124
|
-
```
|
120
|
+
```ruby
|
125
121
|
data = { skip_suppression: true }
|
126
122
|
|
127
123
|
mail(to: to_email, subject: "Test", body: "test", sparkpost_data: data)
|
128
124
|
```
|
129
125
|
|
130
|
-
To schedule the generation of messages for a future date and time, specify a start time in the date parameter of the mail.
|
126
|
+
To schedule the generation of messages for a future date and time, specify a start time in the `date` parameter of the mail. The `date` must be in the future and less than 1 year from today. If `date` is in the past or too far in the future, no date will be passed, and no delivery schedule will be set.
|
131
127
|
|
132
|
-
```
|
133
|
-
start_time = DateTime.now + 4.hours
|
128
|
+
```ruby
|
129
|
+
start_time = DateTime.now + 4.hours
|
134
130
|
|
135
131
|
mail(to: to_email, subject: "Test", body: "test", date: start_time)
|
136
132
|
```
|
137
133
|
|
138
|
-
You can set a description for a transmission via the
|
139
|
-
longer than the maxium will be truncated.
|
134
|
+
You can set a `description` for a transmission via the `sparkpost_data` as well. The maximum length of the `decription` is 1024 characters - values longer than the maxium will be truncated.
|
140
135
|
|
141
|
-
```
|
136
|
+
```ruby
|
142
137
|
data = { description: "My Important Message" }
|
143
138
|
|
144
139
|
mail(to: to_email, subject: "Test", body: "test", sparkpost_data: data)
|
145
140
|
```
|
146
141
|
|
147
|
-
By default, content from single-part messages is sent at plain-text.
|
148
|
-
as shown below. You can also set this in the configuration to ensure that all single-part emails are sent as html.
|
142
|
+
By default, content from single-part messages is sent at plain-text. If you are only intending to send HTML email, with no plain-text part, you can specify this as shown below. You can also set this in the configuration to ensure that all single-part emails are sent as HTML.
|
149
143
|
|
150
|
-
```
|
144
|
+
```ruby
|
151
145
|
data = { html_content_only: true }
|
152
146
|
|
153
147
|
mail(to: to_email, subject: "Test", body: "<h1>test</h1>", sparkpost_data: data)
|
154
148
|
```
|
155
149
|
|
156
150
|
### Subaccounts
|
151
|
+
|
157
152
|
SparkPostRails supports sending messages via subaccounts in two ways. The default API key set in the configuration can be overriden on a message-by-message basis with a subaccount API key.
|
158
153
|
|
159
|
-
```
|
154
|
+
```ruby
|
160
155
|
data = { api_key: "SUBACCOUNT_API_KEY" }
|
161
156
|
|
162
157
|
mail(subject: "Test", body: "test", sparkpost_data: data)
|
@@ -164,57 +159,72 @@ mail(subject: "Test", body: "test", sparkpost_data: data)
|
|
164
159
|
|
165
160
|
Subaccounts can also be leveraged using the subaccount ID with the master API key.
|
166
161
|
|
167
|
-
```
|
162
|
+
```ruby
|
168
163
|
data = { subaccount: "123" }
|
169
164
|
|
170
165
|
mail(subject: "Test", body: "test", sparkpost_data: data)
|
171
166
|
```
|
172
167
|
|
173
168
|
### Recipient Lists
|
174
|
-
SparkPostRails supports using SparkPost stored recipient lists.
|
169
|
+
SparkPostRails supports using SparkPost stored recipient lists. Simply add the `list_id` to the `sparkpost_data` hash on the mail message:
|
175
170
|
|
176
|
-
```
|
177
|
-
data = {
|
171
|
+
```ruby
|
172
|
+
data = { recipient_list_id: "MY-LIST"}
|
178
173
|
|
179
174
|
mail(subject: "Test", body: "test", sparkpost_data: data)
|
180
175
|
```
|
181
176
|
|
182
|
-
**NOTE**: If you supply a recipient
|
183
|
-
not support utilizing both a recipient list and inline recipients.
|
177
|
+
**NOTE**: If you supply a recipient `list_id`, all `To:`, `CC:`, and `BCC:` data specified on the mail message will be ignored. The SparkPost API does not support utilizing both a recipient list and inline recipients.
|
184
178
|
|
185
179
|
|
186
180
|
### Substitution Data
|
187
|
-
You can leverage SparkPost's substitution engine through the gem as well.
|
188
|
-
to your sparkpost_data hash, with the key :substitution_data.
|
181
|
+
You can leverage SparkPost's substitution engine through the gem as well. To supply substitution data, simply add your hash of substitution data to your `sparkpost_data` hash, with the key `substitution_data`.
|
189
182
|
|
190
|
-
```
|
191
|
-
sub_data = {
|
192
|
-
|
183
|
+
```ruby
|
184
|
+
sub_data = {
|
185
|
+
first_name: "Sam",
|
186
|
+
last_name: "Test
|
187
|
+
}
|
193
188
|
|
194
189
|
data = { substitution_data: sub_data }
|
195
190
|
|
196
191
|
mail(to: to_email, subject: "Test", body: "test", sparkpost_data: data)
|
197
192
|
```
|
198
193
|
|
199
|
-
###
|
200
|
-
|
201
|
-
add your template id to the sparkpost_data hash:
|
194
|
+
### Recipient-Specific Data
|
195
|
+
When sending to multiple recipients, you can pass an array of data to complement each recipient. Simply pass an array called `recipients` containing an array of the additional data (e.g. `substitution_data`).
|
202
196
|
|
197
|
+
```ruby
|
198
|
+
recipients = ['recipient1@email.com', 'recipient2@email.com']
|
199
|
+
sparkpost_data = {
|
200
|
+
recipients: [
|
201
|
+
{ substitution_data: { name: 'Recipient1' } },
|
202
|
+
{ substitution_data: { name: 'Recipient2' } }
|
203
|
+
]
|
204
|
+
}
|
205
|
+
mail(to: recipients, sparkpost_data: sparkpost_data)
|
203
206
|
```
|
207
|
+
|
208
|
+
|
209
|
+
### Using SparkPost Templates
|
210
|
+
You can leverage SparkPost's powerful templates rather than building ActionMailer views using SparkPostRails. Add your `template_id` to the `sparkpost_data` hash. By default, `ActionMailer` finds a template to use within views. A workaround to prevent this default action is to explicitly pass a block with an empty `text` part:
|
211
|
+
|
212
|
+
```ruby
|
204
213
|
data = { template_id: "MY-TEMPLATE" }
|
205
214
|
|
206
|
-
mail(to: to_email, sparkpost_data: data)
|
215
|
+
mail(to: to_email, sparkpost_data: data) do |format|
|
216
|
+
format.text { render text: "" }
|
217
|
+
end
|
207
218
|
```
|
208
219
|
|
209
|
-
**NOTE**: All inline-content that may exist in your mail message will be ignored, as the SparkPost API does not accept that data when a template id is
|
210
|
-
supplied. This includes Subject, From, ReplyTo, Attachments, and Inline Images.
|
220
|
+
**NOTE**: All inline-content that may exist in your mail message will be ignored, as the SparkPost API does not accept that data when a template id is supplied. This includes `Subject`, `From`, `ReplyTo`, Attachments, and Inline Images.
|
211
221
|
|
212
222
|
###Other Mail Headers
|
213
|
-
If you need to identify custom mail headers for your messages,
|
214
|
-
to the api. Note, per the SparkPost API documentation, "Headers such as 'Content-Type' and 'Content-Transfer-Encoding' are not allowed here as they are auto
|
215
|
-
generated upon construction of the email."
|
223
|
+
If you need to identify custom mail headers for your messages, use the `ActionMailer` `header[]` method. The gem will pass all appropriate headers through to the API. Note, per the SparkPost API documentation
|
216
224
|
|
217
|
-
|
225
|
+
> Headers such as 'Content-Type' and 'Content-Transfer-Encoding' are not allowed here as they are auto-generated upon construction of the email.
|
226
|
+
|
227
|
+
```ruby
|
218
228
|
headers["Priority"] = "urgent"
|
219
229
|
headers["Sensitivity"] = "private"
|
220
230
|
|
data/lib/sparkpost_rails.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "sparkpost_rails/data_options"
|
1
2
|
require "sparkpost_rails/delivery_method"
|
2
3
|
require "sparkpost_rails/exceptions"
|
3
4
|
require "sparkpost_rails/railtie"
|
@@ -7,6 +8,10 @@ module SparkPostRails
|
|
7
8
|
attr_accessor :configuration
|
8
9
|
end
|
9
10
|
|
11
|
+
def self.configuration
|
12
|
+
@configuration ||= Configuration.new
|
13
|
+
end
|
14
|
+
|
10
15
|
def self.configure
|
11
16
|
self.configuration ||= Configuration.new
|
12
17
|
yield(configuration)
|
@@ -14,6 +19,7 @@ module SparkPostRails
|
|
14
19
|
|
15
20
|
class Configuration
|
16
21
|
attr_accessor :api_key
|
22
|
+
attr_accessor :api_endpoint
|
17
23
|
attr_accessor :sandbox
|
18
24
|
|
19
25
|
attr_accessor :track_opens
|
@@ -40,6 +46,8 @@ module SparkPostRails
|
|
40
46
|
@api_key = ""
|
41
47
|
end
|
42
48
|
|
49
|
+
@api_endpoint = "https://api.sparkpost.com/api/"
|
50
|
+
|
43
51
|
@sandbox = false
|
44
52
|
|
45
53
|
@track_opens = false
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module SparkPostRails
|
2
|
+
module DataOptions
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
prepend InstanceMethods
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
|
12
|
+
def mail(headers={}, &block)
|
13
|
+
headers = headers.clone
|
14
|
+
sparkpost_data = headers.delete(:sparkpost_data)
|
15
|
+
sparkpost_data ||= {}
|
16
|
+
super(headers, &block).tap do |message|
|
17
|
+
message.singleton_class.class_eval { attr_accessor "sparkpost_data" }
|
18
|
+
message.sparkpost_data = sparkpost_data
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -14,6 +14,7 @@ module SparkPostRails
|
|
14
14
|
sparkpost_data = find_sparkpost_data_from mail
|
15
15
|
|
16
16
|
prepare_recipients_from mail, sparkpost_data
|
17
|
+
prepare_recipients_data_from sparkpost_data
|
17
18
|
|
18
19
|
if sparkpost_data.has_key?(:template_id)
|
19
20
|
prepare_template_content_from sparkpost_data
|
@@ -28,6 +29,7 @@ module SparkPostRails
|
|
28
29
|
end
|
29
30
|
|
30
31
|
prepare_substitution_data_from sparkpost_data
|
32
|
+
prepare_metadata_from sparkpost_data
|
31
33
|
prepare_description_from sparkpost_data
|
32
34
|
prepare_options_from mail, sparkpost_data
|
33
35
|
prepare_additional_mail_headers_from mail
|
@@ -41,11 +43,7 @@ module SparkPostRails
|
|
41
43
|
|
42
44
|
private
|
43
45
|
def find_sparkpost_data_from mail
|
44
|
-
|
45
|
-
eval(mail[:sparkpost_data].value)
|
46
|
-
else
|
47
|
-
Hash.new
|
48
|
-
end
|
46
|
+
mail.sparkpost_data
|
49
47
|
end
|
50
48
|
|
51
49
|
def prepare_recipients_from mail, sparkpost_data
|
@@ -67,14 +65,15 @@ module SparkPostRails
|
|
67
65
|
|
68
66
|
def prepare_addresses emails, names
|
69
67
|
emails = [emails] unless emails.is_a?(Array)
|
70
|
-
emails.
|
68
|
+
header_to = emails.join(",")
|
69
|
+
emails.each_with_index.map {|email, index| prepare_address(email, index, names, header_to) }
|
71
70
|
end
|
72
71
|
|
73
|
-
def prepare_address email, index, names
|
72
|
+
def prepare_address email, index, names, header_to
|
74
73
|
if !names[index].nil?
|
75
|
-
{ address: { email: email, name: names[index] } }
|
74
|
+
{ address: { email: email, name: names[index], header_to: header_to } }
|
76
75
|
else
|
77
|
-
{ address: { email: email } }
|
76
|
+
{ address: { email: email, header_to: header_to } }
|
78
77
|
end
|
79
78
|
end
|
80
79
|
|
@@ -95,6 +94,17 @@ module SparkPostRails
|
|
95
94
|
end
|
96
95
|
end
|
97
96
|
|
97
|
+
# See https://developers.sparkpost.com/api/#/introduction/substitutions-reference/links-and-substitution-expressions-within-substitution-values
|
98
|
+
def prepare_recipients_data_from sparkpost_data
|
99
|
+
if (recipients_data = sparkpost_data[:recipients])
|
100
|
+
@data[:recipients].each_with_index do |recipient, index|
|
101
|
+
if (recipient_data = recipients_data[index])
|
102
|
+
recipient.merge!(recipient_data)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
98
108
|
def prepare_template_content_from sparkpost_data
|
99
109
|
@data[:content][:template_id] = sparkpost_data[:template_id]
|
100
110
|
|
@@ -106,6 +116,12 @@ module SparkPostRails
|
|
106
116
|
end
|
107
117
|
end
|
108
118
|
|
119
|
+
def prepare_metadata_from sparkpost_data
|
120
|
+
if sparkpost_data[:metadata]
|
121
|
+
@data[:metadata] = sparkpost_data[:metadata]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
109
125
|
def prepare_from_address_from mail
|
110
126
|
if !mail[:from].display_names.first.nil?
|
111
127
|
from = { email: mail.from.first, name: mail[:from].display_names.first }
|
@@ -135,7 +151,7 @@ module SparkPostRails
|
|
135
151
|
emails << copy[:address][:email]
|
136
152
|
end
|
137
153
|
|
138
|
-
@data[:content][:headers] = { cc: emails }
|
154
|
+
@data[:content][:headers] = { cc: emails.join(",") }
|
139
155
|
end
|
140
156
|
end
|
141
157
|
|
@@ -166,9 +182,9 @@ module SparkPostRails
|
|
166
182
|
inline_images = Array.new
|
167
183
|
|
168
184
|
mail.attachments.each do |attachment|
|
169
|
-
#We decode and reencode here to ensure that attachments are
|
185
|
+
#We decode and reencode here to ensure that attachments are
|
170
186
|
#Base64 encoded without line breaks as required by the API.
|
171
|
-
attachment_data = { name: attachment.inline? ? attachment.
|
187
|
+
attachment_data = { name: attachment.inline? ? attachment.cid : attachment.filename,
|
172
188
|
type: attachment.content_type,
|
173
189
|
data: Base64.strict_encode64(attachment.body.decoded) }
|
174
190
|
|
@@ -361,15 +377,13 @@ module SparkPostRails
|
|
361
377
|
end
|
362
378
|
|
363
379
|
def post_to_api
|
364
|
-
|
380
|
+
uri = URI.join(SparkPostRails.configuration.api_endpoint, 'v1/transmissions')
|
365
381
|
|
366
|
-
uri = URI.parse(url)
|
367
382
|
http = Net::HTTP.new(uri.host, uri.port)
|
368
383
|
http.use_ssl = true
|
369
384
|
|
370
385
|
request = Net::HTTP::Post.new(uri.path, @headers)
|
371
386
|
request.body = JSON.generate(@data)
|
372
|
-
|
373
387
|
http.request(request)
|
374
388
|
end
|
375
389
|
|
@@ -1,4 +1,17 @@
|
|
1
1
|
module SparkPostRails
|
2
2
|
class DeliveryException < StandardError
|
3
|
+
attr_reader :service_message, :service_description, :service_code
|
4
|
+
|
5
|
+
def initialize(message)
|
6
|
+
errors = [*message].first
|
7
|
+
|
8
|
+
if errors.is_a?(Hash)
|
9
|
+
@service_message = errors['message']
|
10
|
+
@service_description = errors['description']
|
11
|
+
@service_code = errors['code']
|
12
|
+
end
|
13
|
+
|
14
|
+
super(message)
|
15
|
+
end
|
3
16
|
end
|
4
17
|
end
|
@@ -5,5 +5,11 @@ module SparkPostRails
|
|
5
5
|
ActionMailer::Base.add_delivery_method :sparkpost, SparkPostRails::DeliveryMethod, return_response: true
|
6
6
|
end
|
7
7
|
end
|
8
|
+
|
9
|
+
initializer "sparkpost_rails.extend_with_data_options" do
|
10
|
+
ActiveSupport.on_load :action_mailer do
|
11
|
+
ActionMailer::Base.send :include, SparkPostRails::DataOptions
|
12
|
+
end
|
13
|
+
end
|
8
14
|
end
|
9
15
|
end
|
data/spec/attachments_spec.rb
CHANGED
@@ -48,7 +48,7 @@ describe SparkPostRails::DeliveryMethod do
|
|
48
48
|
expect(images.length).to eq(1)
|
49
49
|
image = images[0]
|
50
50
|
expect(image).to include(:name, :type=>"image/png; filename=image_0.png", :data=>"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")
|
51
|
-
expect(image[:name]).to match(
|
51
|
+
expect(image[:name]).to match(/.*@.*/)
|
52
52
|
end
|
53
53
|
|
54
54
|
end
|
data/spec/bcc_recipients_spec.rb
CHANGED
@@ -13,7 +13,8 @@ describe SparkPostRails::DeliveryMethod do
|
|
13
13
|
test_email = Mailer.test_email bcc: "bcc@example.com"
|
14
14
|
@delivery_method.deliver!(test_email)
|
15
15
|
|
16
|
-
expect(@delivery_method.data[:recipients]).to
|
16
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", header_to: anything}}),
|
17
|
+
{address: {email: "bcc@example.com", header_to: "to@example.com"}}])
|
17
18
|
expect(@delivery_method.data[:content]).not_to include(:headers)
|
18
19
|
end
|
19
20
|
|
@@ -21,7 +22,9 @@ describe SparkPostRails::DeliveryMethod do
|
|
21
22
|
test_email = Mailer.test_email to: "Joe Test <to1@example.com>, Sam Test <to2@example.com>", bcc: "Brock Test <bcc@example.com>"
|
22
23
|
@delivery_method.deliver!(test_email)
|
23
24
|
|
24
|
-
expect(@delivery_method.data[:recipients]).to
|
25
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", name: "Joe Test", header_to: anything}}),
|
26
|
+
a_hash_including({address: {email: "to2@example.com", name: "Sam Test", header_to: anything}}),
|
27
|
+
{address: {email: "bcc@example.com", name: "Brock Test", header_to: "to1@example.com"}}])
|
25
28
|
expect(@delivery_method.data[:content]).not_to include(:headers)
|
26
29
|
end
|
27
30
|
end
|
@@ -31,7 +34,10 @@ describe SparkPostRails::DeliveryMethod do
|
|
31
34
|
test_email = Mailer.test_email bcc: "bcc1@example.com, bcc2@example.com"
|
32
35
|
@delivery_method.deliver!(test_email)
|
33
36
|
|
34
|
-
expect(@delivery_method.data[:recipients]).to
|
37
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", header_to: anything}}),
|
38
|
+
{address: {email: "bcc1@example.com", header_to: "to@example.com"}},
|
39
|
+
{address: {email: "bcc2@example.com", header_to: "to@example.com"}}])
|
40
|
+
|
35
41
|
expect(@delivery_method.data[:content]).not_to include(:headers)
|
36
42
|
end
|
37
43
|
|
@@ -39,7 +45,9 @@ describe SparkPostRails::DeliveryMethod do
|
|
39
45
|
test_email = Mailer.test_email to: "Joe Test <to@example.com>", bcc: "Brock Test <bcc1@example.com>, Brack Test <bcc2@example.com>"
|
40
46
|
@delivery_method.deliver!(test_email)
|
41
47
|
|
42
|
-
expect(@delivery_method.data[:recipients]).to
|
48
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", name: "Joe Test", header_to: anything}}),
|
49
|
+
{address: {email: "bcc1@example.com", name: "Brock Test", header_to: "to@example.com"}},
|
50
|
+
{address: {email: "bcc2@example.com", name: "Brack Test", header_to: "to@example.com"}}])
|
43
51
|
expect(@delivery_method.data[:content]).not_to include(:headers)
|
44
52
|
end
|
45
53
|
|
@@ -47,7 +55,9 @@ describe SparkPostRails::DeliveryMethod do
|
|
47
55
|
test_email = Mailer.test_email to: "Joe Test <to@example.com>", bcc: "Brock Test <bcc1@example.com>, bcc2@example.com"
|
48
56
|
@delivery_method.deliver!(test_email)
|
49
57
|
|
50
|
-
expect(@delivery_method.data[:recipients]).to
|
58
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", name: "Joe Test", header_to: anything}}),
|
59
|
+
{address: {email: "bcc1@example.com", name: "Brock Test", header_to: "to@example.com"}},
|
60
|
+
{address: {email: "bcc2@example.com", header_to: "to@example.com"}}])
|
51
61
|
expect(@delivery_method.data[:content]).not_to include(:headers)
|
52
62
|
end
|
53
63
|
end
|
@@ -57,24 +67,35 @@ describe SparkPostRails::DeliveryMethod do
|
|
57
67
|
test_email = Mailer.test_email to: "to1@example.com, to2@example.com", cc: "cc@example.com", bcc: "bcc@example.com"
|
58
68
|
@delivery_method.deliver!(test_email)
|
59
69
|
|
60
|
-
expect(@delivery_method.data[:recipients]).to
|
61
|
-
|
70
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", header_to: anything}}),
|
71
|
+
a_hash_including({address: {email: "to2@example.com", header_to: anything}}),
|
72
|
+
{address: {email: "cc@example.com", header_to: "to1@example.com"}},
|
73
|
+
{address: {email: "bcc@example.com", header_to: "to1@example.com"}}])
|
74
|
+
expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc@example.com"})
|
62
75
|
end
|
63
76
|
|
64
77
|
it "handles name and email" do
|
65
78
|
test_email = Mailer.test_email to: "Joe Test <to1@example.com>, Sam Test <to2@example.com>", cc: "Carl Test <cc@example.com>", bcc: "Brock Test <bcc@example.com>"
|
66
79
|
@delivery_method.deliver!(test_email)
|
67
80
|
|
68
|
-
expect(@delivery_method.data[:recipients]).to
|
69
|
-
|
81
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", name: "Joe Test", header_to: anything}}),
|
82
|
+
a_hash_including({address: {email: "to2@example.com", name: "Sam Test", header_to: anything}}),
|
83
|
+
{address: {email: "cc@example.com", name: "Carl Test", header_to: "to1@example.com"}},
|
84
|
+
{address: {email: "bcc@example.com", name: "Brock Test", header_to: "to1@example.com"}}])
|
85
|
+
expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc@example.com"})
|
70
86
|
end
|
71
87
|
|
72
88
|
it "handles mix of email only and name/email" do
|
73
89
|
test_email = Mailer.test_email to: "Joe Test <to1@example.com>, to2@example.com", cc: "cc1@example.com, Chris Test <cc2@example.com>", bcc: "Brock Test <bcc1@example.com>, bcc2@example.com"
|
74
90
|
@delivery_method.deliver!(test_email)
|
75
91
|
|
76
|
-
expect(@delivery_method.data[:recipients]).to
|
77
|
-
|
92
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", name: "Joe Test", header_to: anything}}),
|
93
|
+
a_hash_including({address: {email: "to2@example.com", header_to: anything}}),
|
94
|
+
{address: {email: "cc1@example.com", header_to: "to1@example.com"}},
|
95
|
+
{address: {email: "cc2@example.com", name: "Chris Test", header_to: "to1@example.com"}},
|
96
|
+
{address: {email: "bcc1@example.com", name: "Brock Test", header_to: "to1@example.com"}},
|
97
|
+
{address: {email: "bcc2@example.com", header_to: "to1@example.com"}}])
|
98
|
+
expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc1@example.com,cc2@example.com"})
|
78
99
|
end
|
79
100
|
end
|
80
101
|
end
|
data/spec/cc_recipients_spec.rb
CHANGED
@@ -12,16 +12,18 @@ describe SparkPostRails::DeliveryMethod do
|
|
12
12
|
test_email = Mailer.test_email cc: "cc@example.com"
|
13
13
|
@delivery_method.deliver!(test_email)
|
14
14
|
|
15
|
-
expect(@delivery_method.data[:recipients]).to
|
16
|
-
|
15
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", header_to: anything}}),
|
16
|
+
{address: {email: "cc@example.com", header_to: "to@example.com"}}])
|
17
|
+
expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc@example.com"})
|
17
18
|
end
|
18
19
|
|
19
20
|
it "handles name and email" do
|
20
21
|
test_email = Mailer.test_email to: "Joe Test <to@example.com>", cc: "Carl Test <cc@example.com>"
|
21
22
|
@delivery_method.deliver!(test_email)
|
22
23
|
|
23
|
-
expect(@delivery_method.data[:recipients]).to
|
24
|
-
|
24
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", name: "Joe Test", header_to: anything}}),
|
25
|
+
{address: {email: "cc@example.com", name: "Carl Test", header_to: "to@example.com"}}])
|
26
|
+
expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc@example.com"})
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
@@ -30,24 +32,30 @@ describe SparkPostRails::DeliveryMethod do
|
|
30
32
|
test_email = Mailer.test_email cc: "cc1@example.com, cc2@example.com"
|
31
33
|
@delivery_method.deliver!(test_email)
|
32
34
|
|
33
|
-
expect(@delivery_method.data[:recipients]).to
|
34
|
-
|
35
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", header_to: anything}}),
|
36
|
+
{address: {email: "cc1@example.com", header_to: "to@example.com"}},
|
37
|
+
{address: {email: "cc2@example.com", header_to: "to@example.com"}}])
|
38
|
+
expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc1@example.com,cc2@example.com"})
|
35
39
|
end
|
36
40
|
|
37
41
|
it "handles name and email" do
|
38
42
|
test_email = Mailer.test_email to: "Joe Test <to@example.com>", cc: "Carl Test <cc1@example.com>, Chris Test <cc2@example.com>"
|
39
43
|
@delivery_method.deliver!(test_email)
|
40
44
|
|
41
|
-
expect(@delivery_method.data[:recipients]).to
|
42
|
-
|
45
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", name: "Joe Test", header_to: anything}}),
|
46
|
+
{address: {email: "cc1@example.com", name: "Carl Test", header_to: "to@example.com"}},
|
47
|
+
{address: {email: "cc2@example.com", name: "Chris Test", header_to: "to@example.com"}}])
|
48
|
+
expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc1@example.com,cc2@example.com"})
|
43
49
|
end
|
44
50
|
|
45
51
|
it "handles mix of email only and name/email" do
|
46
52
|
test_email = Mailer.test_email to: "Joe Test <to@example.com>", cc: "Carl Test <cc1@example.com>, cc2@example.com"
|
47
53
|
@delivery_method.deliver!(test_email)
|
48
54
|
|
49
|
-
expect(@delivery_method.data[:recipients]).to
|
50
|
-
|
55
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", name: "Joe Test", header_to: anything}}),
|
56
|
+
{address: {email: "cc1@example.com", name: "Carl Test", header_to: "to@example.com"}},
|
57
|
+
{address: {email: "cc2@example.com", header_to: "to@example.com"}}])
|
58
|
+
expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc1@example.com,cc2@example.com"})
|
51
59
|
end
|
52
60
|
end
|
53
61
|
|
@@ -56,16 +64,20 @@ describe SparkPostRails::DeliveryMethod do
|
|
56
64
|
test_email = Mailer.test_email to: "to1@example.com, to2@example.com", cc: "cc@example.com"
|
57
65
|
@delivery_method.deliver!(test_email)
|
58
66
|
|
59
|
-
expect(@delivery_method.data[:recipients]).to
|
60
|
-
|
67
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", header_to: anything}}),
|
68
|
+
a_hash_including({address: {email: "to2@example.com", header_to: anything}}),
|
69
|
+
{address: {email: "cc@example.com", header_to: "to1@example.com"}}])
|
70
|
+
expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc@example.com"})
|
61
71
|
end
|
62
72
|
|
63
73
|
it "handles name and email" do
|
64
74
|
test_email = Mailer.test_email to: "Joe Test <to1@example.com>, Sam Test <to2@example.com>", cc: "Carl Test <cc@example.com>"
|
65
75
|
@delivery_method.deliver!(test_email)
|
66
76
|
|
67
|
-
expect(@delivery_method.data[:recipients]).to
|
68
|
-
|
77
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", name: "Joe Test", header_to: anything}}),
|
78
|
+
a_hash_including({address: {email: "to2@example.com", name: "Sam Test", header_to: anything}}),
|
79
|
+
{address: {email: "cc@example.com", name: "Carl Test", header_to: "to1@example.com"}}])
|
80
|
+
expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc@example.com"})
|
69
81
|
end
|
70
82
|
end
|
71
83
|
|
@@ -74,24 +86,33 @@ describe SparkPostRails::DeliveryMethod do
|
|
74
86
|
test_email = Mailer.test_email to: "to1@example.com, to2@example.com", cc: "cc1@example.com, cc2@example.com"
|
75
87
|
@delivery_method.deliver!(test_email)
|
76
88
|
|
77
|
-
expect(@delivery_method.data[:recipients]).to
|
78
|
-
|
89
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", header_to: anything}}),
|
90
|
+
a_hash_including({address: {email: "to2@example.com", header_to: anything}}),
|
91
|
+
{address: {email: "cc1@example.com", header_to: "to1@example.com"}},
|
92
|
+
{address: {email: "cc2@example.com", header_to: "to1@example.com"}}])
|
93
|
+
expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc1@example.com,cc2@example.com"})
|
79
94
|
end
|
80
95
|
|
81
96
|
it "handles name and email" do
|
82
97
|
test_email = Mailer.test_email to: "Joe Test <to1@example.com>, Sam Test <to2@example.com>", cc: "Carl Test <cc1@example.com>, Chris Test <cc2@example.com>"
|
83
98
|
@delivery_method.deliver!(test_email)
|
84
99
|
|
85
|
-
expect(@delivery_method.data[:recipients]).to
|
86
|
-
|
100
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", name: "Joe Test", header_to: anything}}),
|
101
|
+
a_hash_including({address: {email: "to2@example.com", name: "Sam Test", header_to: anything}}),
|
102
|
+
{address: {email: "cc1@example.com", name: "Carl Test", header_to: "to1@example.com"}},
|
103
|
+
{address: {email: "cc2@example.com", name: "Chris Test", header_to: "to1@example.com"}}])
|
104
|
+
expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc1@example.com,cc2@example.com"})
|
87
105
|
end
|
88
106
|
|
89
107
|
it "handles mix of email only and name/email for to recipients" do
|
90
108
|
test_email = Mailer.test_email to: "Joe Test <to1@example.com>, to2@example.com", cc: "cc1@example.com, Chris Test <cc2@example.com>"
|
91
109
|
@delivery_method.deliver!(test_email)
|
92
110
|
|
93
|
-
expect(@delivery_method.data[:recipients]).to
|
94
|
-
|
111
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", name: "Joe Test", header_to: anything}}),
|
112
|
+
a_hash_including({address: {email: "to2@example.com", header_to: anything}}),
|
113
|
+
{address: {email: "cc1@example.com", header_to: "to1@example.com"}},
|
114
|
+
{address: {email: "cc2@example.com", name: "Chris Test", header_to: "to1@example.com"}}])
|
115
|
+
expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc1@example.com,cc2@example.com"})
|
95
116
|
end
|
96
117
|
end
|
97
118
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "SparkPostRails configuration" do
|
4
|
+
let(:delivery_method) { SparkPostRails::DeliveryMethod.new }
|
5
|
+
|
6
|
+
describe "#configuration" do
|
7
|
+
it "creates a new configuration + defaults if #configure is never called", skip_configure: true do
|
8
|
+
config = SparkPostRails.configuration
|
9
|
+
expect(config).to_not be_nil
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SparkPostRails::DeliveryException do
|
4
|
+
subject { SparkPostRails::DeliveryException.new(error) }
|
5
|
+
|
6
|
+
describe 'string' do
|
7
|
+
let(:error) { 'Some delivery error' }
|
8
|
+
|
9
|
+
it 'preserves original message' do
|
10
|
+
begin
|
11
|
+
raise subject
|
12
|
+
rescue SparkPostRails::DeliveryException => err
|
13
|
+
expect(error).to eq(err.message)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'array with error details' do
|
19
|
+
let(:error) { [{ 'message' => 'Message generation rejected', 'description' => 'recipient address suppressed due to customer policy', 'code' => '1902' }] }
|
20
|
+
|
21
|
+
it 'assigns message' do
|
22
|
+
expect(subject.service_message).to eq('Message generation rejected')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'assigns description' do
|
26
|
+
expect(subject.service_description).to eq('recipient address suppressed due to customer policy')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'assigns code' do
|
30
|
+
expect(subject.service_code).to eq('1902')
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'preserves original message' do
|
34
|
+
begin
|
35
|
+
raise subject
|
36
|
+
rescue SparkPostRails::DeliveryException => err
|
37
|
+
expect(error.to_s).to eq(err.message)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'array with partial details' do
|
43
|
+
let(:error) { [{ 'message' => 'end of world' }] }
|
44
|
+
|
45
|
+
it 'assigns message' do
|
46
|
+
expect(subject.service_message).to eq('end of world')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'assigns description' do
|
50
|
+
expect(subject.service_description).to be nil
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'assigns code' do
|
54
|
+
expect(subject.service_code).to be nil
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'preserves original message' do
|
58
|
+
begin
|
59
|
+
raise subject
|
60
|
+
rescue SparkPostRails::DeliveryException => err
|
61
|
+
expect(error.to_s).to eq(err.message)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/spec/headers_spec.rb
CHANGED
@@ -18,7 +18,7 @@ describe SparkPostRails::DeliveryMethod do
|
|
18
18
|
test_email = Mailer.test_email cc: "Carl Test <cc@example.com>", headers: {"Priority" => "urgent", "Sensitivity" => "private"}
|
19
19
|
@delivery_method.deliver!(test_email)
|
20
20
|
|
21
|
-
expect(@delivery_method.data[:content][:headers]).to eq({cc:
|
21
|
+
expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc@example.com", "Priority" => "urgent", "Sensitivity" => "private"})
|
22
22
|
end
|
23
23
|
|
24
24
|
it "does not pass inappropriate headers through to the API" do
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SparkPostRails::DeliveryMethod do
|
4
|
+
subject { described_class.new }
|
5
|
+
let(:metadata) { {item_1: 'test data 1', item_2: 'test data 2'} }
|
6
|
+
|
7
|
+
describe 'Metadata' do
|
8
|
+
context 'template-based message' do
|
9
|
+
context 'when metadata is passed' do
|
10
|
+
it 'includes metadata' do
|
11
|
+
test_email = Mailer.test_email sparkpost_data: { template_id: 'test_template', metadata: metadata }
|
12
|
+
subject.deliver!(test_email)
|
13
|
+
expect(subject.data[:metadata]).to eq(metadata)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when metadata isn't passed" do
|
18
|
+
it "doesn't include metadata" do
|
19
|
+
test_email = Mailer.test_email sparkpost_data: { template_id: 'test_template' }
|
20
|
+
subject.deliver!(test_email)
|
21
|
+
expect(subject.data).to_not have_key(:metadata)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'inline-content message' do
|
27
|
+
context 'when metadata is passed' do
|
28
|
+
it 'includes metadata' do
|
29
|
+
test_email = Mailer.test_email sparkpost_data: { metadata: metadata }
|
30
|
+
subject.deliver!(test_email)
|
31
|
+
expect(subject.data[:metadata]).to eq(metadata)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when metadata isn't passed" do
|
36
|
+
it "doesn't include metadata" do
|
37
|
+
test_email = Mailer.test_email sparkpost_data: { metadata: nil }
|
38
|
+
subject.deliver!(test_email)
|
39
|
+
expect(subject.data).to_not have_key(:metadata)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SparkPostRails::DeliveryMethod do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@delivery_method = SparkPostRails::DeliveryMethod.new
|
7
|
+
end
|
8
|
+
|
9
|
+
context "Recipient Data" do
|
10
|
+
|
11
|
+
it "accepts data matching all recipients" do
|
12
|
+
recipients = ['recipient1@email.com', 'recipient2@email.com']
|
13
|
+
recipients_data = [
|
14
|
+
{substitution_data: {name: "Recipient1"}},
|
15
|
+
{substitution_data: {name: "Recipient2"}}
|
16
|
+
]
|
17
|
+
|
18
|
+
test_email = Mailer.test_email to: recipients, sparkpost_data: {recipients: recipients_data}
|
19
|
+
|
20
|
+
@delivery_method.deliver!(test_email)
|
21
|
+
|
22
|
+
actual_recipients = @delivery_method.data[:recipients]
|
23
|
+
expect(actual_recipients.length).to eq(recipients.length)
|
24
|
+
expect(actual_recipients).to match(recipients.each_with_index.map { |recipient, index| recipients_data[index].merge(address: {email: recipient, header_to: anything}) })
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
data/spec/recipients_spec.rb
CHANGED
@@ -13,14 +13,14 @@ describe SparkPostRails::DeliveryMethod do
|
|
13
13
|
test_email = Mailer.test_email
|
14
14
|
@delivery_method.deliver!(test_email)
|
15
15
|
|
16
|
-
expect(@delivery_method.data[:recipients]).to
|
16
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", header_to: anything}})])
|
17
17
|
end
|
18
18
|
|
19
19
|
it "handles name and email" do
|
20
20
|
test_email = Mailer.test_email to: "Joe Test <to@example.com>"
|
21
21
|
@delivery_method.deliver!(test_email)
|
22
22
|
|
23
|
-
expect(@delivery_method.data[:recipients]).to
|
23
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", name: "Joe Test", header_to: anything}})])
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -29,21 +29,33 @@ describe SparkPostRails::DeliveryMethod do
|
|
29
29
|
test_email = Mailer.test_email to: "to1@example.com, to2@example.com"
|
30
30
|
@delivery_method.deliver!(test_email)
|
31
31
|
|
32
|
-
expect(@delivery_method.data[:recipients]).to
|
32
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", header_to: anything}}),
|
33
|
+
a_hash_including({address: {email: "to2@example.com", header_to: anything}})])
|
33
34
|
end
|
34
35
|
|
35
36
|
it "handles name and email" do
|
36
37
|
test_email = Mailer.test_email to: "Sam Test <to1@example.com>, Joe Test <to2@example.com>"
|
37
38
|
@delivery_method.deliver!(test_email)
|
38
39
|
|
39
|
-
expect(@delivery_method.data[:recipients]).to
|
40
|
+
expect(@delivery_method.data[:recipients]).to match([a_hash_including({:address=>{:email=>"to1@example.com", :name=>"Sam Test", header_to: anything}}),
|
41
|
+
a_hash_including({:address=>{:email=>"to2@example.com", :name=>"Joe Test", header_to: anything}})])
|
40
42
|
end
|
41
43
|
|
42
44
|
it "handles mix of email only and name/email" do
|
43
45
|
test_email = Mailer.test_email to: "Sam Test <to1@example.com>, to2@example.com"
|
44
46
|
@delivery_method.deliver!(test_email)
|
45
47
|
|
46
|
-
expect(@delivery_method.data[:recipients]).to
|
48
|
+
expect(@delivery_method.data[:recipients]).to match_array([a_hash_including({:address=>{:email=>"to1@example.com", :name=>"Sam Test", header_to: anything}}),
|
49
|
+
a_hash_including({:address=>{:email=>"to2@example.com", header_to: anything}})])
|
50
|
+
end
|
51
|
+
|
52
|
+
it "compiles list of email addresses to populate :header_to for each recipient" do
|
53
|
+
expected_header_to = "a@a.com,b@b.com"
|
54
|
+
test_email = Mailer.test_email to: "a <a@a.com>, b@b.com"
|
55
|
+
@delivery_method.deliver!(test_email)
|
56
|
+
|
57
|
+
expect(@delivery_method.data[:recipients].first[:address][:header_to]).to eql(expected_header_to)
|
58
|
+
expect(@delivery_method.data[:recipients].second[:address][:header_to]).to eql(expected_header_to)
|
47
59
|
end
|
48
60
|
end
|
49
61
|
end
|
data/spec/response_spec.rb
CHANGED
@@ -15,7 +15,8 @@ describe SparkPostRails::DeliveryMethod do
|
|
15
15
|
end
|
16
16
|
|
17
17
|
it "raises exception on error" do
|
18
|
-
|
18
|
+
uri = URI.join(SparkPostRails.configuration.api_endpoint, 'v1/transmissions')
|
19
|
+
stub_request(:any, uri.to_s).
|
19
20
|
to_return(body: "{\"errors\":[{\"message\":\"required field is missing\",\"description\":\"recipients or list_id required\",\"code\":\"1400\"}]}", status: 403)
|
20
21
|
|
21
22
|
test_email = Mailer.test_email
|
data/spec/spec_helper.rb
CHANGED
@@ -6,13 +6,19 @@ require "sparkpost_rails"
|
|
6
6
|
RSpec.configure do |config|
|
7
7
|
|
8
8
|
config.before(:all) do
|
9
|
-
|
10
|
-
c.api_key = "TESTKEY1234"
|
11
|
-
end
|
9
|
+
ActionMailer::Base.send :include, SparkPostRails::DataOptions
|
12
10
|
end
|
13
11
|
|
14
|
-
config.before(:each) do
|
15
|
-
|
12
|
+
config.before(:each) do |example|
|
13
|
+
if example.metadata[:skip_configure]
|
14
|
+
SparkPostRails.configuration = nil # Reset configuration
|
15
|
+
else
|
16
|
+
SparkPostRails.configure do |c|
|
17
|
+
c.api_key = "TESTKEY1234"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
uri = URI.join(SparkPostRails.configuration.api_endpoint, 'v1/transmissions')
|
21
|
+
stub_request(:any, uri.to_s).
|
16
22
|
to_return(body: "{\"results\":{\"total_rejected_recipients\":0,\"total_accepted_recipients\":1,\"id\":\"00000000000000000\"}}", status: 200)
|
17
23
|
end
|
18
24
|
|
@@ -23,7 +29,7 @@ class Mailer < ActionMailer::Base
|
|
23
29
|
def test_email options = {}
|
24
30
|
data = {
|
25
31
|
from: "from@example.com",
|
26
|
-
to: "to@example.com",
|
32
|
+
to: options[:to] || "to@example.com",
|
27
33
|
subject: "Test Email",
|
28
34
|
text_part: "Hello, Testing!"
|
29
35
|
}
|
@@ -65,14 +71,14 @@ class Mailer < ActionMailer::Base
|
|
65
71
|
if data.has_key?(:html_part)
|
66
72
|
|
67
73
|
mail(data) do |format|
|
68
|
-
format.text {render
|
69
|
-
format.html {render
|
74
|
+
format.text {render plain: data[:text_part]}
|
75
|
+
format.html {render plain: data[:html_part]}
|
70
76
|
end
|
71
77
|
|
72
78
|
else
|
73
79
|
|
74
80
|
mail(data) do |format|
|
75
|
-
format.text {render
|
81
|
+
format.text {render plain: data[:text_part]}
|
76
82
|
end
|
77
83
|
|
78
84
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sparkpost_rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3
|
4
|
+
version: 1.5.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Kimball
|
@@ -9,22 +9,28 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2020-10-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - "
|
18
|
+
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '4.0'
|
21
|
+
- - "<"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '6.1'
|
21
24
|
type: :runtime
|
22
25
|
prerelease: false
|
23
26
|
version_requirements: !ruby/object:Gem::Requirement
|
24
27
|
requirements:
|
25
|
-
- - "
|
28
|
+
- - ">="
|
26
29
|
- !ruby/object:Gem::Version
|
27
30
|
version: '4.0'
|
31
|
+
- - "<"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '6.1'
|
28
34
|
- !ruby/object:Gem::Dependency
|
29
35
|
name: rspec
|
30
36
|
requirement: !ruby/object:Gem::Requirement
|
@@ -65,6 +71,7 @@ files:
|
|
65
71
|
- LICENSE
|
66
72
|
- README.md
|
67
73
|
- lib/sparkpost_rails.rb
|
74
|
+
- lib/sparkpost_rails/data_options.rb
|
68
75
|
- lib/sparkpost_rails/delivery_method.rb
|
69
76
|
- lib/sparkpost_rails/exceptions.rb
|
70
77
|
- lib/sparkpost_rails/railtie.rb
|
@@ -74,14 +81,18 @@ files:
|
|
74
81
|
- spec/campaign_id_spec.rb
|
75
82
|
- spec/cc_recipients_spec.rb
|
76
83
|
- spec/click_tracking_spec.rb
|
84
|
+
- spec/configuration_spec.rb
|
77
85
|
- spec/delivery_schedule_spec.rb
|
78
86
|
- spec/description_spec.rb
|
87
|
+
- spec/exceptions_spec.rb
|
79
88
|
- spec/from_spec.rb
|
80
89
|
- spec/headers_spec.rb
|
81
90
|
- spec/inline_content_spec.rb
|
82
91
|
- spec/inline_css_spec.rb
|
83
92
|
- spec/ip_pool_spec.rb
|
93
|
+
- spec/metadata_spec.rb
|
84
94
|
- spec/open_tracking_spec.rb
|
95
|
+
- spec/recipient_specific_data_spec.rb
|
85
96
|
- spec/recipients_list_spec.rb
|
86
97
|
- spec/recipients_spec.rb
|
87
98
|
- spec/reply_to_spec.rb
|
@@ -113,34 +124,37 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
124
|
- !ruby/object:Gem::Version
|
114
125
|
version: '0'
|
115
126
|
requirements: []
|
116
|
-
|
117
|
-
rubygems_version: 2.2.2
|
127
|
+
rubygems_version: 3.1.4
|
118
128
|
signing_key:
|
119
129
|
specification_version: 4
|
120
130
|
summary: SparkPost for Rails
|
121
131
|
test_files:
|
122
|
-
- spec/
|
132
|
+
- spec/reply_to_spec.rb
|
133
|
+
- spec/open_tracking_spec.rb
|
134
|
+
- spec/recipient_specific_data_spec.rb
|
135
|
+
- spec/response_spec.rb
|
136
|
+
- spec/click_tracking_spec.rb
|
137
|
+
- spec/substitution_data_spec.rb
|
138
|
+
- spec/spec_helper.rb
|
123
139
|
- spec/from_spec.rb
|
140
|
+
- spec/ip_pool_spec.rb
|
141
|
+
- spec/metadata_spec.rb
|
124
142
|
- spec/subaccount_api_spec.rb
|
143
|
+
- spec/return_path_spec.rb
|
144
|
+
- spec/campaign_id_spec.rb
|
145
|
+
- spec/inline_content_spec.rb
|
146
|
+
- spec/skip_suppression_spec.rb
|
125
147
|
- spec/recipients_spec.rb
|
148
|
+
- spec/description_spec.rb
|
149
|
+
- spec/inline_css_spec.rb
|
150
|
+
- spec/exceptions_spec.rb
|
151
|
+
- spec/delivery_schedule_spec.rb
|
152
|
+
- spec/attachments_spec.rb
|
126
153
|
- spec/headers_spec.rb
|
127
|
-
- spec/transactional_spec.rb
|
128
154
|
- spec/bcc_recipients_spec.rb
|
129
|
-
- spec/ip_pool_spec.rb
|
130
|
-
- spec/attachments_spec.rb
|
131
|
-
- spec/cc_recipients_spec.rb
|
132
155
|
- spec/recipients_list_spec.rb
|
133
|
-
- spec/
|
134
|
-
- spec/
|
135
|
-
- spec/reply_to_spec.rb
|
136
|
-
- spec/spec_helper.rb
|
137
|
-
- spec/description_spec.rb
|
138
|
-
- spec/click_tracking_spec.rb
|
139
|
-
- spec/skip_suppression_spec.rb
|
140
|
-
- spec/substitution_data_spec.rb
|
141
|
-
- spec/inline_css_spec.rb
|
142
|
-
- spec/response_spec.rb
|
143
|
-
- spec/open_tracking_spec.rb
|
156
|
+
- spec/transactional_spec.rb
|
157
|
+
- spec/cc_recipients_spec.rb
|
144
158
|
- spec/sandbox_mode_spec.rb
|
145
|
-
- spec/
|
146
|
-
- spec/
|
159
|
+
- spec/template_spec.rb
|
160
|
+
- spec/configuration_spec.rb
|