sparkpost_rails 1.3.0 → 1.5.3
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/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
|