emarsys-broadcast 0.1.0
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.
- data/.gitignore +18 -0
- data/.rspec +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +284 -0
- data/Rakefile +6 -0
- data/emarsys-broadcast.gemspec +30 -0
- data/lib/emarsys/broadcast/api.rb +78 -0
- data/lib/emarsys/broadcast/batch.rb +25 -0
- data/lib/emarsys/broadcast/batch_xml_builder.rb +34 -0
- data/lib/emarsys/broadcast/configuration.rb +43 -0
- data/lib/emarsys/broadcast/email.rb +11 -0
- data/lib/emarsys/broadcast/http.rb +70 -0
- data/lib/emarsys/broadcast/sender.rb +11 -0
- data/lib/emarsys/broadcast/sftp.rb +31 -0
- data/lib/emarsys/broadcast/validation.rb +14 -0
- data/lib/emarsys/broadcast/validation_error.rb +11 -0
- data/lib/emarsys/broadcast/version.rb +5 -0
- data/lib/emarsys/broadcast/xml_builder.rb +35 -0
- data/lib/emarsys/broadcast.rb +18 -0
- data/spec/api_spec.rb +38 -0
- data/spec/batch_spec.rb +7 -0
- data/spec/batch_xml_builder_spec.rb +29 -0
- data/spec/configuration_spec.rb +215 -0
- data/spec/email_spec.rb +11 -0
- data/spec/fixtures/minimal_batch.xml +2 -0
- data/spec/fixtures/minimal_escaped_batch.xml +2 -0
- data/spec/fixtures/xml_builder_import.xml +4 -0
- data/spec/http_spec.rb +6 -0
- data/spec/sftp_spec.rb +99 -0
- data/spec/spec_helper.rb +65 -0
- data/spec/validation_spec.rb +49 -0
- data/spec/version_spec.rb +7 -0
- data/spec/xml_builder_spec.rb +5 -0
- metadata +224 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Valentin Vasilyev
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,284 @@
|
|
1
|
+
# Emarsys::Broadcast
|
2
|
+
|
3
|
+
Ruby wrapper for Emarsys batch mailing API
|
4
|
+
==========================================
|
5
|
+
[](https://travis-ci.org/Valve/emarsys-broadcast-ruby)
|
6
|
+
[](https://codeclimate.com/github/Valve/emarsys-broadcast-ruby)
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem 'emarsys-broadcast'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install emarsys-broadcast
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
### Complete sending example:
|
25
|
+
|
26
|
+
Minimal configuration is required before usage
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
# if using rails, place this configuration code into initializer
|
30
|
+
Emarsys::Broadcast::configure do |c|
|
31
|
+
c.api_user = your_api_user
|
32
|
+
c.api_password = your_api_password
|
33
|
+
|
34
|
+
c.sftp_user = your_sftp_user
|
35
|
+
c.sftp_password = your_sftp_password
|
36
|
+
|
37
|
+
c.sender_domain = 'mail.your.company.com'
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# create a batch that you want to send
|
42
|
+
batch = Emarsys::Broadcast::Batch.new
|
43
|
+
batch.sender = 'sender_id'
|
44
|
+
batch.name = 'newsletter_2013_06_01'
|
45
|
+
batch.subject = 'June 2013 company news'
|
46
|
+
batch.body_html = '<h1>Dear 朋友!</h1>'
|
47
|
+
batch.recipients_path = '/path/to/your/csv/with/emails'
|
48
|
+
|
49
|
+
# create API client
|
50
|
+
api = Emarsys::Broadcast::API.new
|
51
|
+
|
52
|
+
# now send your batch
|
53
|
+
api.send_batch(batch)
|
54
|
+
```
|
55
|
+
|
56
|
+
This will synchronously send the batch email to all recipients found in CSV file.
|
57
|
+
|
58
|
+
### Moving batch properties to configuration
|
59
|
+
|
60
|
+
If you find yourself using same batch attributes over and over again, for example `recipients_path`,
|
61
|
+
you can move those values into configuration:
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
Emarsys::Broadcast::configure do |c|
|
65
|
+
c.api_user = your_api_user
|
66
|
+
c.api_password = your_api_password
|
67
|
+
|
68
|
+
c.sftp_user = your_sftp_user
|
69
|
+
c.sftp_password = your_sftp_password
|
70
|
+
|
71
|
+
c.sender = 'sender_id'
|
72
|
+
c.sender_domain = 'mail.your.company.com'
|
73
|
+
c.recipients_path = '/path/to/hyour/csv/with/emails'
|
74
|
+
end
|
75
|
+
|
76
|
+
# now you can omit these attributes when constructing a batch:
|
77
|
+
batch = Emarsys::Broadcast::Batch.new
|
78
|
+
batch.name = 'newsletter_2013_06_01'
|
79
|
+
batch.subject = 'June 2013 company news'
|
80
|
+
batch.body_html = '<h1>Dear 朋友!</h1>'
|
81
|
+
|
82
|
+
# send your batch as above, via api
|
83
|
+
```
|
84
|
+
|
85
|
+
|
86
|
+
### Creating batch from hash
|
87
|
+
|
88
|
+
If you like, you can construct your batch from a hash, like follows:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
batch = Emarsys::Broadcast::Batch.new name: 'name', subject: 'subject', body_html: '<h1>html body</h1>'
|
92
|
+
```
|
93
|
+
|
94
|
+
### Batch name requirements
|
95
|
+
|
96
|
+
Batch name must be a valid identifier, i.e. start with a letter and contain letters, digits and underscores.
|
97
|
+
Emarsys requires every batch to have a unique name, but you don't have to maintain the uniqueness, because
|
98
|
+
this library internally appends a timestamp to each batch name before submitting it to Emarsys.
|
99
|
+
|
100
|
+
### Batch subject requirements
|
101
|
+
|
102
|
+
Batch subject must be a string with a maximum length of 255 characters
|
103
|
+
|
104
|
+
### Batch body html
|
105
|
+
|
106
|
+
Batch body html can be any HTML text, no restrictions
|
107
|
+
|
108
|
+
### Batch body in plain text
|
109
|
+
|
110
|
+
It is possible to supply the body contents in plain text, this will broaden compatibility,
|
111
|
+
because some email clients don't support HTML and will download textual version instead.
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
batch = Emarsys::Broadcast::Batch.new
|
115
|
+
batch.name = 'newsletter_2013_06_01'
|
116
|
+
batch.subject = 'June 2013 company news'
|
117
|
+
batch.body_html = '<h1>Dear 朋友!</h1>'
|
118
|
+
batch.body_text = 'Dear 朋友'
|
119
|
+
```
|
120
|
+
|
121
|
+
### Batch validation
|
122
|
+
|
123
|
+
Emarsys::Broadcast uses ActiveModel for validating plain ruby objects, so you have all the methods for
|
124
|
+
validation you're accustomed to:
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
batch = Emarsys::Broadcast::Batch.new
|
128
|
+
batch.name = 'newsletter_2013_06_01'
|
129
|
+
|
130
|
+
batch.valid? # false
|
131
|
+
batch.errors
|
132
|
+
batch.errors.full_messages
|
133
|
+
```
|
134
|
+
|
135
|
+
You can always validate your batch before submitting it.
|
136
|
+
|
137
|
+
Note that calling api#send_batch on an invalid batch will throw ValidationException
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
batch = get_invalid_batch
|
141
|
+
api = Emarsys::Broadcast::API.new
|
142
|
+
begin
|
143
|
+
api.send_batch batch
|
144
|
+
rescue Emarsys::Broadcast::ValidationException => ex
|
145
|
+
# get exception message
|
146
|
+
puts ex.message
|
147
|
+
# get exception errors (ActiveModel compatible)
|
148
|
+
puts ex.errors
|
149
|
+
end
|
150
|
+
```
|
151
|
+
|
152
|
+
### CSV file requirements
|
153
|
+
|
154
|
+
The recipients must be placed in a `UTF-8` CSV file.
|
155
|
+
The file must have at least one column with `EMAIL` header, for example:
|
156
|
+
|
157
|
+
```csv
|
158
|
+
EMAIL
|
159
|
+
john.doe@gmail.com
|
160
|
+
sara.parker@yahoo.com
|
161
|
+
...
|
162
|
+
...
|
163
|
+
```
|
164
|
+
|
165
|
+
If you use additional customization columns, add them to your CSV file:
|
166
|
+
|
167
|
+
```csv
|
168
|
+
EMAIL FIRST_NAME
|
169
|
+
john.doe@gmail.com John
|
170
|
+
sara.parker@yahoo.com Sara
|
171
|
+
...
|
172
|
+
...
|
173
|
+
```
|
174
|
+
Having additional columns allows you to customize your body_html, for example:
|
175
|
+
|
176
|
+
```html
|
177
|
+
<h1>Hi, $$FIRST_NAME$$</h1>
|
178
|
+
```
|
179
|
+
|
180
|
+
### Batch sender_id requirements
|
181
|
+
|
182
|
+
Emarsys requires that API users maintain a list of possible senders, and restricts
|
183
|
+
sending emails from arbitrary sender.
|
184
|
+
|
185
|
+
To use any `sender_id` in your batch, create it first:
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
# assuming you have API configured already
|
189
|
+
api = Emarsys::Broadcast::API.new
|
190
|
+
# sender requires 3 arguments: id, name, email_address
|
191
|
+
sender = Emarsys::Broadcast::Sender.new('primary_newsletter_sender', 'My company', 'news@company.com')
|
192
|
+
api.create_sender sender
|
193
|
+
```
|
194
|
+
|
195
|
+
Once you upload a sender, you can use its ID in any batch:
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
batch.sender_id = 'primary_newsletter_sender'
|
199
|
+
# more attributes
|
200
|
+
```
|
201
|
+
|
202
|
+
### Working with senders
|
203
|
+
|
204
|
+
#### Getting a full list of senders
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
api.get_senders
|
208
|
+
# returns Sender array
|
209
|
+
```
|
210
|
+
|
211
|
+
#### Getting a single sender by email
|
212
|
+
|
213
|
+
```ruby
|
214
|
+
api.get_sender('news@mycompany.ru')
|
215
|
+
```
|
216
|
+
|
217
|
+
#### Find if a sender exists by email
|
218
|
+
|
219
|
+
```ruby
|
220
|
+
api.sender_exists? 'news@mycompany.ru'
|
221
|
+
```
|
222
|
+
|
223
|
+
### Scheduling batches
|
224
|
+
|
225
|
+
By default a new batch is scheduled for immediate sending, but you can set the `send_time`
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
# Assuming using ActiveSupport and want to schedule a batch to be sent in 10 days
|
229
|
+
batch.send_time = Time.zone.now + 10.days
|
230
|
+
# .. more attributes
|
231
|
+
|
232
|
+
api.send_batch batch
|
233
|
+
```
|
234
|
+
|
235
|
+
### Compatibility
|
236
|
+
|
237
|
+
This gem is tested on
|
238
|
+
* MRI `1.9.2`, `1.9.3`, `2.0.0`
|
239
|
+
* JRuby 1.9 mode
|
240
|
+
|
241
|
+
|
242
|
+
### Further plans
|
243
|
+
|
244
|
+
This library does not yet cover all Emarsys functionality, so the plans are to cover 100% of Emarsys features,
|
245
|
+
add async support, more scheduling options etc.
|
246
|
+
|
247
|
+
If you want to help me with this, pull requests are especially welcome :)
|
248
|
+
|
249
|
+
|
250
|
+
## Contributing
|
251
|
+
|
252
|
+
1. Fork it
|
253
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
254
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
255
|
+
4. Run specs (`rspec`)
|
256
|
+
5. Push to the branch (`git push origin my-new-feature`)
|
257
|
+
6. Create new Pull Request
|
258
|
+
|
259
|
+
|
260
|
+
## License
|
261
|
+
|
262
|
+
|
263
|
+
Copyright (c) 2013 Valentin Vasilyev
|
264
|
+
|
265
|
+
MIT License
|
266
|
+
|
267
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
268
|
+
a copy of this software and associated documentation files (the
|
269
|
+
"Software"), to deal in the Software without restriction, including
|
270
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
271
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
272
|
+
permit persons to whom the Software is furnished to do so, subject to
|
273
|
+
the following conditions:
|
274
|
+
|
275
|
+
The above copyright notice and this permission notice shall be
|
276
|
+
included in all copies or substantial portions of the Software.
|
277
|
+
|
278
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
279
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
280
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
281
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
282
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
283
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
284
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'emarsys/broadcast/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "emarsys-broadcast"
|
8
|
+
spec.version = Emarsys::Broadcast::VERSION
|
9
|
+
spec.authors = ["Valentin Vasilyev"]
|
10
|
+
spec.email = ["iamvalentin@gmail.com"]
|
11
|
+
spec.description = %q{Emarsys broadcast API for Ruby}
|
12
|
+
spec.summary = %q{Emarsys broadcast API for Ruby}
|
13
|
+
spec.homepage = "https://github.com/Valve/emarsys-broadcast-ruby"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "net-sftp"
|
22
|
+
spec.add_dependency "nokogiri"
|
23
|
+
spec.add_dependency "activemodel", "~> 3.0"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "rspec", "~> 2.11"
|
28
|
+
spec.add_development_dependency "timecop"
|
29
|
+
spec.add_development_dependency "webmock"
|
30
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Emarsys
|
2
|
+
module Broadcast
|
3
|
+
class API
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@config = Emarsys::Broadcast.configuration
|
7
|
+
@sftp = SFTP.new @config
|
8
|
+
@http = HTTP.new @config
|
9
|
+
@xml_builder = XmlBuilder.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def send_batch(batch)
|
13
|
+
batch = supplement_batch_from_config(batch)
|
14
|
+
validate_batch(batch)
|
15
|
+
validate_sender(batch.sender)
|
16
|
+
create_batch(batch)
|
17
|
+
upload_recipients(batch.recipients_path)
|
18
|
+
trigger_import(batch)
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_batch(batch)
|
22
|
+
emarsys_sender = get_sender(batch.sender)
|
23
|
+
batch.sender_id = emarsys_sender.id
|
24
|
+
batch_xml = BatchXmlBuilder.new.build(batch)
|
25
|
+
@http.post("#{@config.api_base_path}/batches/#{batch.name}", batch_xml)
|
26
|
+
end
|
27
|
+
|
28
|
+
def upload_recipients(recipients_path)
|
29
|
+
@sftp.upload_file(recipients_path, File.basename(recipients_path))
|
30
|
+
end
|
31
|
+
|
32
|
+
def trigger_import(batch)
|
33
|
+
import_xml = XmlBuilder.new.import_xml(File.basename(batch.recipients_path))
|
34
|
+
@http.post("#{@config.api_base_path}/batches/#{batch.name}/import", import_xml)
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_senders
|
38
|
+
response = @http.get("#{@config.api_base_path}/senders")
|
39
|
+
Nokogiri::XML(response).xpath('//sender').map do |node|
|
40
|
+
Sender.new(node.attr('id'), node.xpath('name').text, node.xpath('address').text)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_sender(email)
|
45
|
+
get_senders.find{|s| s.address == email}
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_sender(sender)
|
49
|
+
sender_xml = @xml_builder.sender_xml(sender)
|
50
|
+
@http.put("#{@config.api_base_path}/senders/#{sender.id}", sender_xml)
|
51
|
+
end
|
52
|
+
|
53
|
+
def sender_exists?(email)
|
54
|
+
get_senders.any?{|s|s.address == email}
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def supplement_batch_from_config(batch)
|
60
|
+
batch.recipients_path ||= @config.recipients_path
|
61
|
+
batch.send_time ||= Time.now
|
62
|
+
batch.sender ||= @config.sender
|
63
|
+
batch.sender_domain ||= @config.sender_domain
|
64
|
+
batch
|
65
|
+
end
|
66
|
+
|
67
|
+
def validate_batch(batch)
|
68
|
+
raise ValidationError.new('Batch is invalid', batch.errors.full_messages) unless batch.valid?
|
69
|
+
end
|
70
|
+
|
71
|
+
def validate_sender(email)
|
72
|
+
msg = 'This email is not registered with Emarsys as a sender, register it with `create_sender` api call'
|
73
|
+
raise ValidationError, msg, [msg] unless sender_exists? email
|
74
|
+
end
|
75
|
+
end
|
76
|
+
class ApiError < StandardError; end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'uri'
|
2
|
+
module Emarsys
|
3
|
+
module Broadcast
|
4
|
+
class Batch
|
5
|
+
include ActiveModel::Validations
|
6
|
+
attr_accessor \
|
7
|
+
:name,
|
8
|
+
:subject,
|
9
|
+
:body_html,
|
10
|
+
:body_text,
|
11
|
+
:recipients_path,
|
12
|
+
:send_time,
|
13
|
+
:sender,
|
14
|
+
:sender_domain,
|
15
|
+
:sender_id
|
16
|
+
|
17
|
+
validates :name, :subject, :body_html, :recipients_path, :sender, :sender_domain, presence: true
|
18
|
+
validates :name, format: {with: /^[^\d\W]\w*\z/i, message: 'must start with a letter and contain only letters, numbers and underscores'}
|
19
|
+
validates :subject, length: {maximum: 255}
|
20
|
+
validates :sender, format: {with: /@/, message: 'is not a valid email'}
|
21
|
+
validates :sender_domain, format: {with: URI::REL_URI, message: 'is not valid'}
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
module Emarsys
|
3
|
+
module Broadcast
|
4
|
+
class BatchXmlBuilder
|
5
|
+
|
6
|
+
def build(batch)
|
7
|
+
raise ArgumentError, 'batch is required' unless batch
|
8
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
9
|
+
xml.batch {
|
10
|
+
xml.name batch.name
|
11
|
+
xml.runDate format_time(batch.send_time)
|
12
|
+
xml.properties {
|
13
|
+
xml.property(key: :Sender){xml.text batch.sender_id}
|
14
|
+
xml.property(key: :Language){xml.text 'en'}
|
15
|
+
xml.property(key: :Encoding){xml.text 'UTF-8'}
|
16
|
+
xml.property(key: :Domain){xml.text batch.sender_domain}
|
17
|
+
}
|
18
|
+
xml.subject batch.subject
|
19
|
+
xml.html batch.body_html
|
20
|
+
xml.text batch.body_text
|
21
|
+
}
|
22
|
+
end
|
23
|
+
builder.to_xml
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def format_time(time)
|
30
|
+
time.strftime("%Y-%m-%dT%H:%M:%S%z")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Emarsys
|
2
|
+
module Broadcast
|
3
|
+
class ConfigurationError < StandardError; end
|
4
|
+
|
5
|
+
|
6
|
+
class Configuration
|
7
|
+
attr_accessor \
|
8
|
+
:sftp_host,
|
9
|
+
:sftp_user,
|
10
|
+
:sftp_password,
|
11
|
+
:sftp_port,
|
12
|
+
:api_host,
|
13
|
+
:api_base_path,
|
14
|
+
:api_user,
|
15
|
+
:api_password,
|
16
|
+
:api_port,
|
17
|
+
:api_timeout,
|
18
|
+
:sender,
|
19
|
+
:sender_domain,
|
20
|
+
:recipients_path
|
21
|
+
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@sftp_host = 'e3.emarsys.net'
|
25
|
+
@sftp_port = 22
|
26
|
+
@api_host = 'e3.emarsys.net'
|
27
|
+
@api_base_path = '/bmapi/v2'
|
28
|
+
@api_port = 80
|
29
|
+
@api_timeout = 600 #10 minutes
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
attr_accessor :configuration
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.configure
|
38
|
+
self.configuration ||= Configuration.new
|
39
|
+
yield configuration
|
40
|
+
configuration
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Emarsys
|
2
|
+
module Broadcast
|
3
|
+
module Email
|
4
|
+
REGEX = /\A(|(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6})\z/i
|
5
|
+
|
6
|
+
def self.validate(email)
|
7
|
+
email =~ REGEX
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
module Emarsys
|
3
|
+
module Broadcast
|
4
|
+
class HTTP
|
5
|
+
include Validation
|
6
|
+
|
7
|
+
def initialize(config)
|
8
|
+
validate_config config
|
9
|
+
@config = config
|
10
|
+
end
|
11
|
+
|
12
|
+
def post(path, xml)
|
13
|
+
request(path, xml, :post)
|
14
|
+
end
|
15
|
+
|
16
|
+
def put(path, xml)
|
17
|
+
request(path, xml, :put)
|
18
|
+
end
|
19
|
+
|
20
|
+
def get(path)
|
21
|
+
request(path, nil, :get)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def request(path, data, method)
|
28
|
+
https = Net::HTTP.new(@config.api_host, Net::HTTP.https_default_port)
|
29
|
+
https.read_timeout = @config.api_timeout
|
30
|
+
https.use_ssl = true
|
31
|
+
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
32
|
+
|
33
|
+
https.start do |http|
|
34
|
+
case method.downcase.to_sym
|
35
|
+
when :post
|
36
|
+
req = Net::HTTP::Post.new(path)
|
37
|
+
when :put
|
38
|
+
req = Net::HTTP::Put.new(path)
|
39
|
+
when :get
|
40
|
+
req = Net::HTTP::Get.new(path)
|
41
|
+
end
|
42
|
+
req.basic_auth(@config.api_user, @config.api_password)
|
43
|
+
req.body = data
|
44
|
+
req.content_type = "application/xml"
|
45
|
+
|
46
|
+
res = http.request(req)
|
47
|
+
|
48
|
+
case res
|
49
|
+
when Net::HTTPSuccess
|
50
|
+
puts "OK"
|
51
|
+
return res.body
|
52
|
+
else
|
53
|
+
puts res.body
|
54
|
+
res.error!
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def validate_config(config)
|
60
|
+
raise ConfigurationError, 'configuration is nil, did you forget to configure the gem?' unless config
|
61
|
+
raise ConfigurationError, 'api_host must be configured' unless string_present? config.api_host
|
62
|
+
raise ConfigurationError, 'api_user must be configured' unless string_present? config.api_user
|
63
|
+
raise ConfigurationError, 'api_password must be configured' unless string_present? config.api_password
|
64
|
+
unless within_range? config.api_port, 1..65535
|
65
|
+
raise ConfigurationError, 'api_port must be integer between 1 and 65535'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'net/sftp'
|
2
|
+
module Emarsys
|
3
|
+
module Broadcast
|
4
|
+
class SFTP
|
5
|
+
include Validation
|
6
|
+
|
7
|
+
def initialize(config)
|
8
|
+
validate_config config
|
9
|
+
@config = config
|
10
|
+
end
|
11
|
+
|
12
|
+
def upload_file(local_path, remote_path)
|
13
|
+
Net::SFTP.start(@config.sftp_host, @config.sftp_user, password: @config.sftp_password) do |sftp|
|
14
|
+
sftp.upload!(local_path, remote_path)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def validate_config(config)
|
21
|
+
raise ConfigurationError, 'configuration is nil, did you forget to configure the gem?' unless config
|
22
|
+
raise ConfigurationError, 'sftp_host must be configured' unless string_present? config.sftp_host
|
23
|
+
raise ConfigurationError, 'sftp_user must be configured' unless string_present? config.sftp_user
|
24
|
+
raise ConfigurationError, 'sftp_password must be configured' unless string_present? config.sftp_password
|
25
|
+
unless within_range? config.sftp_port, 1..65535
|
26
|
+
raise ConfigurationError, 'sftp_port must be integer between 1 and 65535'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|