sparkpost_rails_eu 1.5.1
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 +7 -0
- data/LICENSE +22 -0
- data/README.md +230 -0
- data/lib/sparkpost_rails.rb +64 -0
- data/lib/sparkpost_rails/data_options.rb +25 -0
- data/lib/sparkpost_rails/delivery_method.rb +402 -0
- data/lib/sparkpost_rails/exceptions.rb +17 -0
- data/lib/sparkpost_rails/railtie.rb +15 -0
- data/lib/sparkpost_rails/version.rb +4 -0
- data/spec/attachments_spec.rb +56 -0
- data/spec/bcc_recipients_spec.rb +102 -0
- data/spec/campaign_id_spec.rb +64 -0
- data/spec/cc_recipients_spec.rb +119 -0
- data/spec/click_tracking_spec.rb +54 -0
- data/spec/configuration_spec.rb +12 -0
- data/spec/delivery_schedule_spec.rb +46 -0
- data/spec/description_spec.rb +39 -0
- data/spec/exceptions_spec.rb +65 -0
- data/spec/from_spec.rb +24 -0
- data/spec/headers_spec.rb +33 -0
- data/spec/inline_content_spec.rb +63 -0
- data/spec/inline_css_spec.rb +49 -0
- data/spec/ip_pool_spec.rb +50 -0
- data/spec/metadata_spec.rb +44 -0
- data/spec/open_tracking_spec.rb +54 -0
- data/spec/recipient_specific_data_spec.rb +30 -0
- data/spec/recipients_list_spec.rb +34 -0
- data/spec/recipients_spec.rb +62 -0
- data/spec/reply_to_spec.rb +25 -0
- data/spec/response_spec.rb +26 -0
- data/spec/return_path_spec.rb +50 -0
- data/spec/sandbox_mode_spec.rb +53 -0
- data/spec/skip_suppression_spec.rb +27 -0
- data/spec/spec_helper.rb +91 -0
- data/spec/subaccount_api_spec.rb +75 -0
- data/spec/substitution_data_spec.rb +40 -0
- data/spec/template_spec.rb +36 -0
- data/spec/transactional_spec.rb +50 -0
- metadata +161 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 389e52647f591e59d115e55cf157c1bcb65a44edaf351d027e69f6526d3d3c98
|
4
|
+
data.tar.gz: 8ca903cf8d6f5bc6b82c95d7b54844c497588e3ada33ffa512901a4b5abb7b37
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5e79ba82eb5d9cd19cd8bd4b37ef86d48128c8c6b6e99239def3e889a68c38e070107d4b6855076da07a519581fbcd099628b7ff47316ed0e4144b37f1f0e797
|
7
|
+
data.tar.gz: e875f7e1ca1c8b755cd28a00f6e54fbcd55aa32ae15b83d5144f23e3937d8c24f4593fc6c8043d3f4d4e9287bc5dbbd25ab904cd1ae810bd26b35d6a858d5b88
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Kevin Kimball
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
[](https://badge.fury.io/rb/sparkpost_rails)
|
2
|
+
[](https://travis-ci.org/the-refinery/sparkpost_rails)
|
3
|
+
|
4
|
+
SparkPost Rails
|
5
|
+
===============
|
6
|
+
|
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.
|
8
|
+
|
9
|
+
Getting Started
|
10
|
+
---------------
|
11
|
+
|
12
|
+
Add the gem to your Gemfile
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'sparkpost_rails'
|
16
|
+
```
|
17
|
+
|
18
|
+
Then run the bundle command to install it.
|
19
|
+
|
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`):
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
SparkPostRails.configure do |c|
|
24
|
+
c.api_key = 'YOUR API KEY'
|
25
|
+
end
|
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.
|
28
|
+
|
29
|
+
In each environment configuration file from which you want to send emails via Sparkpost, (i.e. `config/environments/production.rb`) add
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
config.action_mailer.delivery_method = :sparkpost
|
33
|
+
```
|
34
|
+
|
35
|
+
Additional Configuration
|
36
|
+
------------------------
|
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.
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
SparkPostRails.configure do |c|
|
41
|
+
c.sandbox = true # default: false
|
42
|
+
c.track_opens = true # default: false
|
43
|
+
c.track_clicks = true # default: false
|
44
|
+
c.return_path = 'BOUNCE-EMAIL@YOUR-DOMAIN.COM' # default: nil
|
45
|
+
c.campaign_id = 'YOUR-CAMPAIGN' # default: nil
|
46
|
+
c.transactional = true # default: false
|
47
|
+
c.ip_pool = "MY-POOL" # default: nil
|
48
|
+
c.inline_css = true # default: false
|
49
|
+
c.html_content_only = true # default: false
|
50
|
+
c.subaccount = "123" # default: nil
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
Usage
|
55
|
+
-----
|
56
|
+
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.
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
result = MyMailer.welcome_message(user).deliver!
|
60
|
+
```
|
61
|
+
|
62
|
+
Example:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
{
|
66
|
+
"total_rejected_recipients" => 0,
|
67
|
+
"total_accepted_recipients" => 1,
|
68
|
+
"id" => "00000000000000"
|
69
|
+
}
|
70
|
+
```
|
71
|
+
|
72
|
+
If the SparkPost API reponds with an error condition, SparkPostRails will raise a `SparkPostRails::DeliveryException`, which will include all the message data returned by the API.
|
73
|
+
|
74
|
+
SparkPostRails will support multiple recipients, multilple CC, multiple BCC, ReplyTo address, file attachments, inline images, multi-part (HTML and plaintext) messages - all utilizing the standard `ActionMailer` methodologies.
|
75
|
+
|
76
|
+
Handling Errors
|
77
|
+
---------------
|
78
|
+
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:
|
79
|
+
|
80
|
+
`config/initializers/action_mailer.rb`
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
ActionMailer::DeliveryJob.rescue_from(SparkPostRails::DeliveryException) do |exception|
|
84
|
+
# do something special with the error
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
SparkPost-Specific Features
|
89
|
+
---------------------------
|
90
|
+
|
91
|
+
### Configuration Settings
|
92
|
+
You can specifiy 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`:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
data = {
|
96
|
+
track_opens: true,
|
97
|
+
track_clicks: false,
|
98
|
+
campaign_id: "My Campaign",
|
99
|
+
transactional: true,
|
100
|
+
ip_pool = "SPECIAL_POOL",
|
101
|
+
api_key = "MESSAGE_SPECIFIC_API_KEY"
|
102
|
+
subaccount = "123"
|
103
|
+
}
|
104
|
+
|
105
|
+
mail(to: to_email, subject: "Test", body: "test", sparkpost_data: data)
|
106
|
+
```
|
107
|
+
|
108
|
+
Additionally, `return_path` can be overriden on a specific email by setting that field on the mail message itself:
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
mail(to: to_email, subject: "Test", body: "test", return_path: "bounces@example.com")
|
112
|
+
```
|
113
|
+
|
114
|
+
### Transmission Specific Settings
|
115
|
+
|
116
|
+
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:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
data = { skip_suppression: true }
|
120
|
+
|
121
|
+
mail(to: to_email, subject: "Test", body: "test", sparkpost_data: data)
|
122
|
+
```
|
123
|
+
|
124
|
+
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.
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
start_time = DateTime.now + 4.hours
|
128
|
+
|
129
|
+
mail(to: to_email, subject: "Test", body: "test", date: start_time)
|
130
|
+
```
|
131
|
+
|
132
|
+
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.
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
data = { description: "My Important Message" }
|
136
|
+
|
137
|
+
mail(to: to_email, subject: "Test", body: "test", sparkpost_data: data)
|
138
|
+
```
|
139
|
+
|
140
|
+
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.
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
data = { html_content_only: true }
|
144
|
+
|
145
|
+
mail(to: to_email, subject: "Test", body: "<h1>test</h1>", sparkpost_data: data)
|
146
|
+
```
|
147
|
+
|
148
|
+
### Subaccounts
|
149
|
+
|
150
|
+
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.
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
data = { api_key: "SUBACCOUNT_API_KEY" }
|
154
|
+
|
155
|
+
mail(subject: "Test", body: "test", sparkpost_data: data)
|
156
|
+
```
|
157
|
+
|
158
|
+
Subaccounts can also be leveraged using the subaccount ID with the master API key.
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
data = { subaccount: "123" }
|
162
|
+
|
163
|
+
mail(subject: "Test", body: "test", sparkpost_data: data)
|
164
|
+
```
|
165
|
+
|
166
|
+
### Recipient Lists
|
167
|
+
SparkPostRails supports using SparkPost stored recipient lists. Simply add the `list_id` to the `sparkpost_data` hash on the mail message:
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
data = { recipient_list_id: "MY-LIST"}
|
171
|
+
|
172
|
+
mail(subject: "Test", body: "test", sparkpost_data: data)
|
173
|
+
```
|
174
|
+
|
175
|
+
**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.
|
176
|
+
|
177
|
+
|
178
|
+
### Substitution Data
|
179
|
+
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`.
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
sub_data = {
|
183
|
+
first_name: "Sam",
|
184
|
+
last_name: "Test
|
185
|
+
}
|
186
|
+
|
187
|
+
data = { substitution_data: sub_data }
|
188
|
+
|
189
|
+
mail(to: to_email, subject: "Test", body: "test", sparkpost_data: data)
|
190
|
+
```
|
191
|
+
|
192
|
+
### Recipient-Specific Data
|
193
|
+
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`).
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
recipients = ['recipient1@email.com', 'recipient2@email.com']
|
197
|
+
sparkpost_data = {
|
198
|
+
recipients: [
|
199
|
+
{ substitution_data: { name: 'Recipient1' } },
|
200
|
+
{ substitution_data: { name: 'Recipient2' } }
|
201
|
+
]
|
202
|
+
}
|
203
|
+
mail(to: recipients, sparkpost_data: sparkpost_data)
|
204
|
+
```
|
205
|
+
|
206
|
+
|
207
|
+
### Using SparkPost Templates
|
208
|
+
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:
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
data = { template_id: "MY-TEMPLATE" }
|
212
|
+
|
213
|
+
mail(to: to_email, sparkpost_data: data) do |format|
|
214
|
+
format.text { render text: "" }
|
215
|
+
end
|
216
|
+
```
|
217
|
+
|
218
|
+
**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.
|
219
|
+
|
220
|
+
###Other Mail Headers
|
221
|
+
If you need to identify custom mail headers for your messages, use the `ActionMailer` `header[]` method. The gem will pass all approprite headers through to the API. Note, per the SparkPost API documentation
|
222
|
+
|
223
|
+
> Headers such as 'Content-Type' and 'Content-Transfer-Encoding' are not allowed here as they are auto-generated upon construction of the email.
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
headers["Priority"] = "urgent"
|
227
|
+
headers["Sensitivity"] = "private"
|
228
|
+
|
229
|
+
mail(to: to_email, subject: "Test", body: "test")
|
230
|
+
```
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "sparkpost_rails/data_options"
|
2
|
+
require "sparkpost_rails/delivery_method"
|
3
|
+
require "sparkpost_rails/exceptions"
|
4
|
+
require "sparkpost_rails/railtie"
|
5
|
+
|
6
|
+
module SparkPostRails
|
7
|
+
class << self
|
8
|
+
attr_accessor :configuration
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.configuration
|
12
|
+
@configuration ||= Configuration.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.configure
|
16
|
+
self.configuration ||= Configuration.new
|
17
|
+
yield(configuration)
|
18
|
+
end
|
19
|
+
|
20
|
+
class Configuration
|
21
|
+
attr_accessor :api_key
|
22
|
+
attr_accessor :sandbox
|
23
|
+
|
24
|
+
attr_accessor :track_opens
|
25
|
+
attr_accessor :track_clicks
|
26
|
+
|
27
|
+
attr_accessor :campaign_id
|
28
|
+
attr_accessor :return_path
|
29
|
+
|
30
|
+
attr_accessor :transactional
|
31
|
+
attr_accessor :ip_pool
|
32
|
+
attr_accessor :inline_css
|
33
|
+
attr_accessor :html_content_only
|
34
|
+
|
35
|
+
attr_accessor :subaccount
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
set_defaults
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_defaults
|
42
|
+
if ENV.has_key?("SPARKPOST_API_KEY")
|
43
|
+
@api_key = ENV["SPARKPOST_API_KEY"]
|
44
|
+
else
|
45
|
+
@api_key = ""
|
46
|
+
end
|
47
|
+
|
48
|
+
@sandbox = false
|
49
|
+
|
50
|
+
@track_opens = false
|
51
|
+
@track_clicks = false
|
52
|
+
|
53
|
+
@campaign_id = nil
|
54
|
+
@return_path = nil
|
55
|
+
|
56
|
+
@transactional = false
|
57
|
+
@ip_pool = nil
|
58
|
+
@inline_css = false
|
59
|
+
@html_content_only = false
|
60
|
+
|
61
|
+
@subaccount = nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -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
|
@@ -0,0 +1,402 @@
|
|
1
|
+
module SparkPostRails
|
2
|
+
class DeliveryMethod
|
3
|
+
require 'net/http'
|
4
|
+
|
5
|
+
attr_accessor :settings, :data, :response, :headers
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@settings = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def deliver!(mail)
|
12
|
+
@data = {content: {}}
|
13
|
+
|
14
|
+
sparkpost_data = find_sparkpost_data_from mail
|
15
|
+
|
16
|
+
prepare_recipients_from mail, sparkpost_data
|
17
|
+
prepare_recipients_data_from sparkpost_data
|
18
|
+
|
19
|
+
if sparkpost_data.has_key?(:template_id)
|
20
|
+
prepare_template_content_from sparkpost_data
|
21
|
+
else
|
22
|
+
prepare_from_address_from mail
|
23
|
+
prepare_reply_to_address_from mail
|
24
|
+
|
25
|
+
prepare_subject_from mail
|
26
|
+
prepare_cc_headers_from mail, sparkpost_data
|
27
|
+
prepare_inline_content_from mail, sparkpost_data
|
28
|
+
prepare_attachments_from mail
|
29
|
+
end
|
30
|
+
|
31
|
+
prepare_substitution_data_from sparkpost_data
|
32
|
+
prepare_metadata_from sparkpost_data
|
33
|
+
prepare_description_from sparkpost_data
|
34
|
+
prepare_options_from mail, sparkpost_data
|
35
|
+
prepare_additional_mail_headers_from mail
|
36
|
+
|
37
|
+
prepare_api_headers_from sparkpost_data
|
38
|
+
|
39
|
+
result = post_to_api
|
40
|
+
|
41
|
+
process_result result
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def find_sparkpost_data_from mail
|
46
|
+
mail.sparkpost_data
|
47
|
+
end
|
48
|
+
|
49
|
+
def prepare_recipients_from mail, sparkpost_data
|
50
|
+
if sparkpost_data.has_key?(:recipient_list_id)
|
51
|
+
@data[:recipients] = {list_id: sparkpost_data[:recipient_list_id]}
|
52
|
+
else
|
53
|
+
@data[:recipients] = prepare_addresses(mail.to, mail[:to].display_names)
|
54
|
+
|
55
|
+
if !mail.cc.nil?
|
56
|
+
@data[:recipients] += prepare_copy_addresses(mail.cc, mail[:cc].display_names, mail.to.first).flatten
|
57
|
+
end
|
58
|
+
|
59
|
+
if !mail.bcc.nil?
|
60
|
+
@data[:recipients] += prepare_copy_addresses(mail.bcc, mail[:bcc].display_names, mail.to.first).flatten
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
def prepare_addresses emails, names
|
67
|
+
emails = [emails] unless emails.is_a?(Array)
|
68
|
+
header_to = emails.join(",")
|
69
|
+
emails.each_with_index.map {|email, index| prepare_address(email, index, names, header_to) }
|
70
|
+
end
|
71
|
+
|
72
|
+
def prepare_address email, index, names, header_to
|
73
|
+
if !names[index].nil?
|
74
|
+
{ address: { email: email, name: names[index], header_to: header_to } }
|
75
|
+
else
|
76
|
+
{ address: { email: email, header_to: header_to } }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def prepare_copy_addresses emails, names, header_to
|
81
|
+
emails = [emails] unless emails.is_a?(Array)
|
82
|
+
emails.each_with_index.map {|email, index| prepare_copy_address(email, index, names, header_to) }
|
83
|
+
end
|
84
|
+
|
85
|
+
def prepare_copy_address email, index, names, header_to
|
86
|
+
if !names[index].nil? && !header_to.nil?
|
87
|
+
{ address: { email: email, name: names[index], header_to: header_to } }
|
88
|
+
elsif !names[index].nil?
|
89
|
+
{ address: { email: email, name: names[index] } }
|
90
|
+
elsif !header_to.nil?
|
91
|
+
{ address: { email: email, header_to: header_to } }
|
92
|
+
else
|
93
|
+
{ address: { email: email } }
|
94
|
+
end
|
95
|
+
end
|
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
|
+
|
108
|
+
def prepare_template_content_from sparkpost_data
|
109
|
+
@data[:content][:template_id] = sparkpost_data[:template_id]
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
def prepare_substitution_data_from sparkpost_data
|
114
|
+
if sparkpost_data[:substitution_data]
|
115
|
+
@data[:substitution_data] = sparkpost_data[:substitution_data]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def prepare_metadata_from sparkpost_data
|
120
|
+
if sparkpost_data[:metadata]
|
121
|
+
@data[:metadata] = sparkpost_data[:metadata]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def prepare_from_address_from mail
|
126
|
+
if !mail[:from].display_names.first.nil?
|
127
|
+
from = { email: mail.from.first, name: mail[:from].display_names.first }
|
128
|
+
else
|
129
|
+
from = { email: mail.from.first }
|
130
|
+
end
|
131
|
+
|
132
|
+
@data[:content][:from] = from
|
133
|
+
end
|
134
|
+
|
135
|
+
def prepare_reply_to_address_from mail
|
136
|
+
unless mail.reply_to.nil?
|
137
|
+
@data[:content][:reply_to] = mail.reply_to.first
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def prepare_subject_from mail
|
142
|
+
@data[:content][:subject] = mail.subject
|
143
|
+
end
|
144
|
+
|
145
|
+
def prepare_cc_headers_from mail, sparkpost_data
|
146
|
+
if !mail[:cc].nil? && !sparkpost_data.has_key?(:recipient_list_id)
|
147
|
+
copies = prepare_addresses(mail.cc, mail[:cc].display_names)
|
148
|
+
emails = []
|
149
|
+
|
150
|
+
copies.each do |copy|
|
151
|
+
emails << copy[:address][:email]
|
152
|
+
end
|
153
|
+
|
154
|
+
@data[:content][:headers] = { cc: emails.join(",") }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def prepare_inline_content_from mail, sparkpost_data
|
159
|
+
if mail.multipart?
|
160
|
+
if mail.html_part
|
161
|
+
@data[:content][:html] = cleanse_encoding(mail.html_part.body.to_s)
|
162
|
+
end
|
163
|
+
|
164
|
+
if mail.text_part
|
165
|
+
@data[:content][:text] = cleanse_encoding(mail.text_part.body.to_s)
|
166
|
+
end
|
167
|
+
else
|
168
|
+
if SparkPostRails.configuration.html_content_only || sparkpost_data[:html_content_only]
|
169
|
+
@data[:content][:html] = cleanse_encoding(mail.body.to_s)
|
170
|
+
else
|
171
|
+
@data[:content][:text] = cleanse_encoding(mail.body.to_s)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def cleanse_encoding content
|
177
|
+
::JSON.parse({c: content}.to_json)["c"]
|
178
|
+
end
|
179
|
+
|
180
|
+
def prepare_attachments_from mail
|
181
|
+
attachments = Array.new
|
182
|
+
inline_images = Array.new
|
183
|
+
|
184
|
+
mail.attachments.each do |attachment|
|
185
|
+
#We decode and reencode here to ensure that attachments are
|
186
|
+
#Base64 encoded without line breaks as required by the API.
|
187
|
+
attachment_data = { name: attachment.inline? ? attachment.cid : attachment.filename,
|
188
|
+
type: attachment.content_type,
|
189
|
+
data: Base64.strict_encode64(attachment.body.decoded) }
|
190
|
+
|
191
|
+
if attachment.inline?
|
192
|
+
inline_images << attachment_data
|
193
|
+
else
|
194
|
+
attachments << attachment_data
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
if attachments.count > 0
|
199
|
+
@data[:content][:attachments] = attachments
|
200
|
+
end
|
201
|
+
|
202
|
+
if inline_images.count > 0
|
203
|
+
@data[:content][:inline_images] = inline_images
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def prepare_options_from mail, sparkpost_data
|
208
|
+
@data[:options] = Hash.new
|
209
|
+
|
210
|
+
prepare_sandbox_mode_from sparkpost_data
|
211
|
+
prepare_open_tracking_from sparkpost_data
|
212
|
+
prepare_click_tracking_from sparkpost_data
|
213
|
+
prepare_campaign_id_from sparkpost_data
|
214
|
+
prepare_return_path_from mail
|
215
|
+
prepare_transactional_from sparkpost_data
|
216
|
+
prepare_skip_suppression_from sparkpost_data
|
217
|
+
prepare_ip_pool_from sparkpost_data
|
218
|
+
prepare_inline_css_from sparkpost_data
|
219
|
+
prepare_delivery_schedule_from mail
|
220
|
+
end
|
221
|
+
|
222
|
+
def prepare_sandbox_mode_from sparkpost_data
|
223
|
+
if SparkPostRails.configuration.sandbox
|
224
|
+
@data[:options][:sandbox] = true
|
225
|
+
end
|
226
|
+
|
227
|
+
if sparkpost_data.has_key?(:sandbox)
|
228
|
+
if sparkpost_data[:sandbox]
|
229
|
+
@data[:options][:sandbox] = sparkpost_data[:sandbox]
|
230
|
+
else
|
231
|
+
@data[:options].delete(:sandbox)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def prepare_open_tracking_from sparkpost_data
|
237
|
+
@data[:options][:open_tracking] = SparkPostRails.configuration.track_opens
|
238
|
+
|
239
|
+
if sparkpost_data.has_key?(:track_opens)
|
240
|
+
@data[:options][:open_tracking] = sparkpost_data[:track_opens]
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def prepare_click_tracking_from sparkpost_data
|
245
|
+
@data[:options][:click_tracking] = SparkPostRails.configuration.track_clicks
|
246
|
+
|
247
|
+
if sparkpost_data.has_key?(:track_clicks)
|
248
|
+
@data[:options][:click_tracking] = sparkpost_data[:track_clicks]
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def prepare_campaign_id_from sparkpost_data
|
253
|
+
campaign_id = SparkPostRails.configuration.campaign_id
|
254
|
+
|
255
|
+
if sparkpost_data.has_key?(:campaign_id)
|
256
|
+
campaign_id = sparkpost_data[:campaign_id]
|
257
|
+
end
|
258
|
+
|
259
|
+
if campaign_id
|
260
|
+
@data[:campaign_id] = campaign_id
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def prepare_return_path_from mail
|
265
|
+
return_path = SparkPostRails.configuration.return_path
|
266
|
+
|
267
|
+
unless mail.return_path.nil?
|
268
|
+
return_path = mail.return_path
|
269
|
+
end
|
270
|
+
|
271
|
+
if return_path
|
272
|
+
@data[:return_path] = return_path
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def prepare_transactional_from sparkpost_data
|
277
|
+
@data[:options][:transactional] = SparkPostRails.configuration.transactional
|
278
|
+
|
279
|
+
if sparkpost_data.has_key?(:transactional)
|
280
|
+
@data[:options][:transactional] = sparkpost_data[:transactional]
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
|
285
|
+
def prepare_skip_suppression_from sparkpost_data
|
286
|
+
if sparkpost_data[:skip_suppression]
|
287
|
+
@data[:options][:skip_suppression] = sparkpost_data[:skip_suppression]
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def prepare_description_from sparkpost_data
|
292
|
+
if sparkpost_data[:description]
|
293
|
+
@data[:description] = sparkpost_data[:description].truncate(1024)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def prepare_ip_pool_from sparkpost_data
|
298
|
+
ip_pool = SparkPostRails.configuration.ip_pool
|
299
|
+
|
300
|
+
if sparkpost_data.has_key?(:ip_pool)
|
301
|
+
ip_pool = sparkpost_data[:ip_pool]
|
302
|
+
end
|
303
|
+
|
304
|
+
if ip_pool
|
305
|
+
@data[:options][:ip_pool] = ip_pool
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def prepare_inline_css_from sparkpost_data
|
310
|
+
@data[:options][:inline_css] = SparkPostRails.configuration.inline_css
|
311
|
+
|
312
|
+
if sparkpost_data.has_key?(:inline_css)
|
313
|
+
@data[:options][:inline_css] = sparkpost_data[:inline_css]
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def prepare_delivery_schedule_from mail
|
318
|
+
# Format YYYY-MM-DDTHH:MM:SS+-HH:MM or "now". Example: '2015-02-11T08:00:00-04:00'. -From SparkPost API Docs
|
319
|
+
if mail.date && (mail.date > DateTime.now) && (mail.date < (DateTime.now + 1.year))
|
320
|
+
@data[:options][:start_time] = mail.date.strftime("%Y-%m-%dT%H:%M:%S%:z")
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
def prepare_additional_mail_headers_from mail
|
325
|
+
valid_headers = Hash.new
|
326
|
+
|
327
|
+
invalid_names = ["sparkpost-data",
|
328
|
+
"from",
|
329
|
+
"to",
|
330
|
+
"cc",
|
331
|
+
"bcc",
|
332
|
+
"subject",
|
333
|
+
"reply-to",
|
334
|
+
"return-path",
|
335
|
+
"date",
|
336
|
+
"mime-version",
|
337
|
+
"content-type",
|
338
|
+
"content-transfer-encoding",
|
339
|
+
"text-part"]
|
340
|
+
|
341
|
+
mail.header.fields.each do |field|
|
342
|
+
unless invalid_names.include?(field.name.downcase)
|
343
|
+
valid_headers[field.name] = field.value
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
if valid_headers.count > 0
|
348
|
+
unless @data[:content].has_key?(:headers)
|
349
|
+
@data[:content][:headers] = Hash.new
|
350
|
+
end
|
351
|
+
|
352
|
+
@data[:content][:headers].merge!(valid_headers)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
def prepare_api_headers_from sparkpost_data
|
357
|
+
if sparkpost_data.has_key?(:api_key)
|
358
|
+
api_key = sparkpost_data[:api_key]
|
359
|
+
else
|
360
|
+
api_key = SparkPostRails.configuration.api_key
|
361
|
+
end
|
362
|
+
|
363
|
+
@headers = {
|
364
|
+
"Authorization" => api_key,
|
365
|
+
"Content-Type" => "application/json"
|
366
|
+
}
|
367
|
+
|
368
|
+
if sparkpost_data.has_key?(:subaccount)
|
369
|
+
subaccount = sparkpost_data[:subaccount]
|
370
|
+
else
|
371
|
+
subaccount = SparkPostRails.configuration.subaccount
|
372
|
+
end
|
373
|
+
|
374
|
+
if subaccount
|
375
|
+
@headers["X-MSYS-SUBACCOUNT"] = subaccount.to_s
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
def post_to_api
|
380
|
+
url = "https://api.eu.sparkpost.com/api/v1/transmissions"
|
381
|
+
|
382
|
+
uri = URI.parse(url)
|
383
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
384
|
+
http.use_ssl = true
|
385
|
+
|
386
|
+
request = Net::HTTP::Post.new(uri.path, @headers)
|
387
|
+
request.body = JSON.generate(@data)
|
388
|
+
http.request(request)
|
389
|
+
end
|
390
|
+
|
391
|
+
def process_result result
|
392
|
+
result_data = JSON.parse(result.body)
|
393
|
+
|
394
|
+
if result_data["errors"]
|
395
|
+
@response = result_data["errors"]
|
396
|
+
raise SparkPostRails::DeliveryException, @response
|
397
|
+
else
|
398
|
+
@response = result_data["results"]
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
402
|
+
end
|