vf-email-handler 0.0.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 +20 -0
- data/README.md +37 -0
- data/bin/vf-email-handler +61 -0
- data/lib/initializers/aws.rb +14 -0
- data/lib/initializers/logger.rb +11 -0
- data/lib/interfaces/ses.rb +63 -0
- data/lib/interfaces/sqs.rb +42 -0
- data/lib/logger/logger.rb +32 -0
- data/lib/mailhandler.rb +56 -0
- data/lib/meta/console_strings.rb +58 -0
- data/lib/meta/sample-config.yml +14 -0
- data/lib/meta/version.rb +4 -0
- data/lib/models/email.rb +44 -0
- data/lib/workers/threaded_worker.rb +31 -0
- data/spec/integration_spec.rb +0 -0
- data/spec/interfaces/ses_spec.rb +42 -0
- data/spec/interfaces/sqs_spec.rb +90 -0
- data/spec/mail_handler_spec.rb +44 -0
- data/spec/spec_helper.rb +20 -0
- data/vf-email-handler.gemspec +43 -0
- metadata +143 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5d3dfd6e8bd814eb464375f2310eabcb1d2dbeb7
|
4
|
+
data.tar.gz: 35d8181ce32c6fe61c5a1959df0ee244512992a7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 31d8c82a3921232dd1479af035159aa6a0e21a9ce921bcadc9ce5744dbac24193463b6fc7ff668dd67320ca64a0fda1ac7fcce10b28082d4f807b32ef97a513f
|
7
|
+
data.tar.gz: ac941615464f4665fced5f48a7c8e9befb7bf6d2c1647e001ba47c6b21fa2d8e5a02d41655f7f5f16fa64b584bdfad2a3b751c3405480976946fcfe029431e41
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Videofy
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
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, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
Videofy Email Handler
|
2
|
+
========
|
3
|
+
|
4
|
+
Logic that binds the Amazon SQS (Simple Queue Service) to the Amazon SES (Simple Email Service)
|
5
|
+
|
6
|
+
The handler will poll a specified SQS Queue and translate the messages to somethign that SES can understand and then push them to SES; effectively sending the emails.
|
7
|
+
|
8
|
+
####Get up and running with the mail handler
|
9
|
+
To start with there are some dependencies that must be satisfied.
|
10
|
+
* Ruby ( >= 2.1.2) must be installed on the host computer
|
11
|
+
* Rubygems (>= 1.8) to install the gem
|
12
|
+
|
13
|
+
To install just type `gem install vf-email-handler` and you're set to go.
|
14
|
+
|
15
|
+
Then there is just the matter of writing the config, which of course the handler can do for you; at least get you started.
|
16
|
+
Type the following:
|
17
|
+
|
18
|
+
```bash
|
19
|
+
Config create call here
|
20
|
+
```
|
21
|
+
|
22
|
+
and it will generate a config-skeleton for you. Looking inside `default.h.cfg` we se this:
|
23
|
+
|
24
|
+
```bash
|
25
|
+
Put sample config here
|
26
|
+
```
|
27
|
+
|
28
|
+
Fill in the blanks and then start the handler by running.
|
29
|
+
|
30
|
+
```bash
|
31
|
+
Put startup command here
|
32
|
+
```
|
33
|
+
|
34
|
+
#####Daemonize
|
35
|
+
There is an init.d script in the opt folder of the repo.
|
36
|
+
|
37
|
+
Happy hacking!
|
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'mailhandler'
|
3
|
+
require 'meta/console_strings'
|
4
|
+
require 'meta/version'
|
5
|
+
require 'initializers/aws'
|
6
|
+
require 'initializers/logger'
|
7
|
+
require 'aws/ses'
|
8
|
+
require 'aws/sqs'
|
9
|
+
|
10
|
+
include ConsoleStrings
|
11
|
+
|
12
|
+
def handle_path(path)
|
13
|
+
path[0] == '/' ? path : "#{Dir.pwd}/#{path}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def start(settings)
|
17
|
+
MailHandler::AWS.setup settings
|
18
|
+
MailHandler::Logger.setup settings
|
19
|
+
MailHandler::Handler.begin_work_loop
|
20
|
+
end
|
21
|
+
|
22
|
+
def copy_config
|
23
|
+
FileUtils.copy(
|
24
|
+
File.expand_path('../../lib/meta/sample-config.yml', __FILE__),
|
25
|
+
"#{Dir.pwd}/mailer_config.yml"
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
if ARGV.empty?
|
30
|
+
puts WELCOME
|
31
|
+
elsif ARGV.include?('--version') || ARGV.include?('-v')
|
32
|
+
puts VERSION
|
33
|
+
elsif ARGV.include?('--help') || ARGV.include?('-h')
|
34
|
+
puts HELP
|
35
|
+
elsif ARGV[0] == 'create-config'
|
36
|
+
copy_config
|
37
|
+
puts CONFIG_COPY_COMPLETE
|
38
|
+
elsif ARGV[0] == 'run'
|
39
|
+
puts STARTING
|
40
|
+
if ARGV[1]
|
41
|
+
begin
|
42
|
+
start YAML.load_file(handle_path(ARGV[1]))
|
43
|
+
puts STARTED
|
44
|
+
rescue Errno::ENOENT
|
45
|
+
puts MISSING_CONFIG
|
46
|
+
rescue AWS::SES::ResponseError, AWS::SQS::Errors::IncompleteSignature
|
47
|
+
puts BAD_AWS_CREDENTIALS
|
48
|
+
rescue AWS::SQS::Errors::NonExistentQueue
|
49
|
+
puts NO_SUCH_QUEUE
|
50
|
+
rescue StandardError
|
51
|
+
puts BAD_CONFIG
|
52
|
+
rescue SystemExit, Interrupt
|
53
|
+
puts 'Exiting.. '
|
54
|
+
exit 1
|
55
|
+
end
|
56
|
+
else
|
57
|
+
puts MISSING_CONFIG
|
58
|
+
end
|
59
|
+
else
|
60
|
+
puts UNRECOGNIZED % ARGV.join(' ')
|
61
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module MailHandler
|
2
|
+
# Initializes SES and SQS interfaces
|
3
|
+
module AWS
|
4
|
+
class << self
|
5
|
+
def setup(config)
|
6
|
+
require 'interfaces/sqs'
|
7
|
+
require 'interfaces/ses'
|
8
|
+
|
9
|
+
SES.setup config[:aws][:ses]
|
10
|
+
SQS.setup config[:aws][:sqs]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'aws/ses'
|
2
|
+
|
3
|
+
module MailHandler
|
4
|
+
# Containing everything related to SES comunication
|
5
|
+
module SES
|
6
|
+
# Setup and cofig the SES
|
7
|
+
class << self
|
8
|
+
attr_reader :mailer
|
9
|
+
|
10
|
+
def setup(config)
|
11
|
+
@mailer = Mailer.new config
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Yields an interface to SES
|
16
|
+
class Mailer
|
17
|
+
def initialize(config)
|
18
|
+
@ses = ::AWS::SES::Base.new(
|
19
|
+
access_key_id: config[:access_key_id],
|
20
|
+
secret_access_key: config[:secret_access_key],
|
21
|
+
server: config[:ses_server]
|
22
|
+
)
|
23
|
+
check_connection
|
24
|
+
end
|
25
|
+
|
26
|
+
def send_mail(email)
|
27
|
+
response = @ses.send_email email.to_h
|
28
|
+
log email, response
|
29
|
+
response
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def log(email, response)
|
35
|
+
if response.error?
|
36
|
+
log_failure email, response
|
37
|
+
else
|
38
|
+
log_success email
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def log_failure(email, response)
|
43
|
+
Logger.error(
|
44
|
+
subject: email,
|
45
|
+
error_code: response.code,
|
46
|
+
error_type: 'SES ERROR',
|
47
|
+
trace: response.error.to_s
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def log_success(email)
|
52
|
+
Logger.success(
|
53
|
+
subject: email,
|
54
|
+
message: 'successfully sent'
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
def check_connection
|
59
|
+
@ses.quota
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
|
3
|
+
module MailHandler
|
4
|
+
# Contains everything rellated to SQS communication
|
5
|
+
module SQS
|
6
|
+
# Setup and cofig the sqs queue
|
7
|
+
class << self
|
8
|
+
attr_reader :client
|
9
|
+
|
10
|
+
def setup(config)
|
11
|
+
@client = Client.new config
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Yields an interface to a specific sqs queue
|
16
|
+
class Client
|
17
|
+
attr_accessor :queues
|
18
|
+
|
19
|
+
def initialize(config)
|
20
|
+
all_queues = ::AWS::SQS.new(
|
21
|
+
access_key_id: config[:access_key_id],
|
22
|
+
secret_access_key: config[:secret_access_key],
|
23
|
+
region: config[:aws_region]
|
24
|
+
).queues
|
25
|
+
|
26
|
+
@queues = config[:sqs_queues].map do |queue_name|
|
27
|
+
all_queues.named queue_name
|
28
|
+
end
|
29
|
+
|
30
|
+
@batch_size = config[:batch_size] || 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def fetch_messages
|
34
|
+
[
|
35
|
+
@queues.map do |queue|
|
36
|
+
queue.receive_messages(limit: @batch_size)
|
37
|
+
end
|
38
|
+
].flatten.compact
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module MailHandler
|
2
|
+
# Custom logger that handles loging through STOUT
|
3
|
+
# or to a specified logfile
|
4
|
+
module Logger
|
5
|
+
# Setup and config the Logger
|
6
|
+
class << self
|
7
|
+
def setup(config)
|
8
|
+
if config
|
9
|
+
@logfile = File.open(config[:log_file], 'a') if config[:log_file]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def success(args)
|
14
|
+
#log "SUCCESS: #{args[:subject]}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def error(args)
|
18
|
+
str = "\n"
|
19
|
+
str << "ERROR: #{args[:error_type]}\n--\n" if args[:error_type]
|
20
|
+
str << "Error code: #{args[:error_code]}\n--\n" if args[:error_code]
|
21
|
+
str << "Subject: #{args[:subject]}\n--\n" if args[:subject]
|
22
|
+
str << "Trace: #{args[:trace]}\n--\n" if args[:trace]
|
23
|
+
str << "==\n"
|
24
|
+
log str
|
25
|
+
end
|
26
|
+
|
27
|
+
def log(string)
|
28
|
+
@logfile ? @logfile.puts(string) : puts(string)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/mailhandler.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'workers/threaded_worker'
|
2
|
+
require 'models/email'
|
3
|
+
|
4
|
+
module MailHandler
|
5
|
+
# Base handler that does it all
|
6
|
+
class Handler
|
7
|
+
def self.begin_work_loop
|
8
|
+
loop do
|
9
|
+
begin
|
10
|
+
new.do_work
|
11
|
+
rescue StandardError => e
|
12
|
+
Logger.error(
|
13
|
+
subject: 'Main loop',
|
14
|
+
error_type: e.class.to_s,
|
15
|
+
trace: e.to_s
|
16
|
+
)
|
17
|
+
raise e
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def do_work
|
23
|
+
if messages.any?
|
24
|
+
worker = ThreadedWorker.new(messages)
|
25
|
+
worker.do_work do |message|
|
26
|
+
unless send(email_from(message)).error?
|
27
|
+
message.delete
|
28
|
+
end
|
29
|
+
end
|
30
|
+
wait_for worker
|
31
|
+
else
|
32
|
+
sleep 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def messages
|
39
|
+
@messages ||= SQS.client.fetch_messages
|
40
|
+
end
|
41
|
+
|
42
|
+
def email_from(message)
|
43
|
+
MailHandler::Models::Email.new(
|
44
|
+
JSON.parse(message.body, symbolize_names: true)
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def send(email)
|
49
|
+
SES.mailer.send_mail email
|
50
|
+
end
|
51
|
+
|
52
|
+
def wait_for(worker)
|
53
|
+
sleep(0.01) until worker.done?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'meta/version'
|
2
|
+
|
3
|
+
# Strings used by the console executable
|
4
|
+
# gathered in one place for less clutter
|
5
|
+
module ConsoleStrings
|
6
|
+
HELP = 'Usage: mail-handler [-v] [-h] [<args>]
|
7
|
+
|
8
|
+
-v, --version Print the version and exit.
|
9
|
+
-h, --help Print this help.
|
10
|
+
|
11
|
+
If no flags are specified <args> should be one of the following:
|
12
|
+
|
13
|
+
create-config Generates a config boilerplate
|
14
|
+
in current folder.
|
15
|
+
|
16
|
+
run [config-file] Run the handler, suplement path
|
17
|
+
to the config file.
|
18
|
+
|
19
|
+
'
|
20
|
+
WELCOME = 'Welcome to the Videofy email handeler!
|
21
|
+
|
22
|
+
One instance of the mail-handler will poll AWS SQS for messages
|
23
|
+
and then forward them to AWS SES.
|
24
|
+
|
25
|
+
Append -h flag for usage information.
|
26
|
+
|
27
|
+
'
|
28
|
+
VERSION = "Version : #{MailHandler::VERSION}"
|
29
|
+
|
30
|
+
UNRECOGNIZED = 'Did not understand argument: "%s"
|
31
|
+
Run me with -h to see what you can do.
|
32
|
+
'
|
33
|
+
|
34
|
+
STARTING = '
|
35
|
+
Handler is starting...
|
36
|
+
'
|
37
|
+
|
38
|
+
STARTED = 'Started
|
39
|
+
'
|
40
|
+
|
41
|
+
BAD_CONFIG = 'Something went wrong when parsing the config provided.
|
42
|
+
Make sure the config is valid YAML.
|
43
|
+
|
44
|
+
Run with --help for more information
|
45
|
+
'
|
46
|
+
|
47
|
+
NO_SUCH_QUEUE= ' One or more queues in your config could not be found.
|
48
|
+
'
|
49
|
+
|
50
|
+
MISSING_CONFIG = HELP
|
51
|
+
|
52
|
+
CONFIG_COPY_COMPLETE = '
|
53
|
+
mailer_config.yml generated in current folder.
|
54
|
+
'
|
55
|
+
BAD_AWS_CREDENTIALS = '
|
56
|
+
Could not connect to AWS, please ensure that your credentials are valid.
|
57
|
+
'
|
58
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
:aws:
|
2
|
+
:ses:
|
3
|
+
:access_key_id: <YOUR AWS ACCESS_KEY_ID>
|
4
|
+
:secret_access_key: <YOUR AWS SECRET_ACCESS_KEY>
|
5
|
+
:ses_server: email.eu-west-1.amazonaws.com
|
6
|
+
:sqs:
|
7
|
+
:access_key_id: <YOUR AWS ACCESS_KEY_ID>
|
8
|
+
:secret_access_key: <YOUR AWS SECRET_ACCESS_KEY>
|
9
|
+
:sqs_queues: [<NAMES OF YOUR AWS SQS QUEUES>]
|
10
|
+
:aws_region: eu-west-1
|
11
|
+
:batch_size: <OPTIONAL DEFAULT 1 MAX 10>
|
12
|
+
|
13
|
+
:logger:
|
14
|
+
:log_file: <PATH TO LOGFILE HERE; Null if STOUT>
|
data/lib/meta/version.rb
ADDED
data/lib/models/email.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module MailHandler
|
2
|
+
module Models
|
3
|
+
# PORO for mutating email information
|
4
|
+
class Email
|
5
|
+
attr_reader :from, :to, :cc, :bcc, :subject, :html,
|
6
|
+
:text, :reply_to, :bounce_to
|
7
|
+
|
8
|
+
def initialize(email_hash = {})
|
9
|
+
@from = email_hash[:from]
|
10
|
+
|
11
|
+
@to = email_hash[:to]
|
12
|
+
@cc = email_hash[:cc]
|
13
|
+
@bcc = email_hash[:bcc]
|
14
|
+
|
15
|
+
@subject = email_hash[:subject]
|
16
|
+
@text = email_hash[:text]
|
17
|
+
@html = email_hash[:html]
|
18
|
+
|
19
|
+
@reply_to = email_hash[:reply_to]
|
20
|
+
@bounce_to = email_hash[:bounce_to]
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_h
|
24
|
+
{ from: from,
|
25
|
+
to: to,
|
26
|
+
cc: cc,
|
27
|
+
bcc: bcc,
|
28
|
+
subject: subject,
|
29
|
+
html_body: html,
|
30
|
+
text_body: text,
|
31
|
+
return_path: bounce_to,
|
32
|
+
reply: reply_to }.delete_if { |_, v| v.nil? }
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
{
|
37
|
+
from: from,
|
38
|
+
to: to,
|
39
|
+
subject: subject
|
40
|
+
}.to_s
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module MailHandler
|
2
|
+
# Generic threaded worker
|
3
|
+
class ThreadedWorker
|
4
|
+
WORKER_SIZE = 10
|
5
|
+
|
6
|
+
def initialize(task_list)
|
7
|
+
@tasks = Queue.new
|
8
|
+
populate_que task_list
|
9
|
+
end
|
10
|
+
|
11
|
+
def do_work
|
12
|
+
WORKER_SIZE.times do
|
13
|
+
Thread.new do
|
14
|
+
while (task = @tasks.pop)
|
15
|
+
yield task
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def done?
|
22
|
+
@tasks.size.zero?
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def populate_que(task_list)
|
28
|
+
task_list.each { |task| @tasks << task }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
File without changes
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'interfaces/ses'
|
3
|
+
require 'models/email'
|
4
|
+
require 'logger/logger'
|
5
|
+
|
6
|
+
describe 'SES' do
|
7
|
+
|
8
|
+
let(:email) { MailHandler::Models::Email.new({}) }
|
9
|
+
|
10
|
+
before :each do
|
11
|
+
@ses = double
|
12
|
+
|
13
|
+
allow(AWS::SES::Base).to receive(:new).and_return(@ses)
|
14
|
+
allow(@ses).to receive(:quota).and_return(true)
|
15
|
+
|
16
|
+
MailHandler::SES.setup({})
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#send_mail' do
|
20
|
+
subject { MailHandler::SES.mailer }
|
21
|
+
|
22
|
+
let(:success) { double(error?: false) }
|
23
|
+
|
24
|
+
let(:failure) { double(error?: true, error: 'Error String', code: 400) }
|
25
|
+
|
26
|
+
it 'handles successfully sent mail' do
|
27
|
+
allow(@ses).to receive(:send_email)
|
28
|
+
.with(email.to_h).and_return(success)
|
29
|
+
|
30
|
+
expect(MailHandler::Logger).to receive(:success).once
|
31
|
+
expect(subject.send_mail(email).error?).to eq false
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'handles failures while sending mail' do
|
35
|
+
allow(@ses).to receive(:send_email)
|
36
|
+
.with(email.to_h).and_return(failure)
|
37
|
+
|
38
|
+
expect(MailHandler::Logger).to receive(:error).once
|
39
|
+
expect(subject.send_mail(email).error?).to eq true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'interfaces/sqs'
|
3
|
+
|
4
|
+
describe 'SQS', :sqs do
|
5
|
+
|
6
|
+
SQS_CONFIG = {
|
7
|
+
access_key_id: 'fake access key',
|
8
|
+
secret_access_key: 'fake secret key',
|
9
|
+
sqs_queues: %w(q1 q2),
|
10
|
+
batch_size: 3
|
11
|
+
}
|
12
|
+
|
13
|
+
before :all do
|
14
|
+
@sqs = AWS::SQS.new(SQS_CONFIG)
|
15
|
+
|
16
|
+
@q1 = @sqs.queues.create('q1')
|
17
|
+
@q2 = @sqs.queues.create('q2')
|
18
|
+
|
19
|
+
MailHandler::SQS.setup(SQS_CONFIG)
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:email) do
|
23
|
+
{
|
24
|
+
from: 'email@email.com',
|
25
|
+
to: 'email2@email.com',
|
26
|
+
subject: 'testing',
|
27
|
+
text: 'testing text',
|
28
|
+
reply_to: 'noreply@email.com'
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#fetch_messages' do
|
33
|
+
subject do
|
34
|
+
MailHandler::SQS.client.fetch_messages.map do |msg|
|
35
|
+
JSON.parse(msg.body, symbolize_names: true)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when no messages' do
|
40
|
+
it { is_expected.to be_empty }
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when one message' do
|
44
|
+
describe 'from one queue' do
|
45
|
+
before { @q1.send_message(JSON.dump(email)) }
|
46
|
+
|
47
|
+
it { is_expected.to eq [email] }
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'from multiple queues' do
|
51
|
+
before { [@q1, @q2].each { |q| q.send_message(JSON.dump email) } }
|
52
|
+
|
53
|
+
it { is_expected.to eq [email, email] }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when multiple messages' do
|
58
|
+
describe 'from one queue' do
|
59
|
+
before { 4.times { @q1.send_message(JSON.dump email) } }
|
60
|
+
|
61
|
+
it { is_expected.to eq 3.times.map { email } }
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'from multiple queues' do
|
65
|
+
before do
|
66
|
+
[@q1, @q2].each { |q| 4.times { q.send_message(JSON.dump email) } }
|
67
|
+
end
|
68
|
+
|
69
|
+
it { is_expected.to eq 6.times.map { email } }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'deleting messages' do
|
75
|
+
subject { MailHandler::SQS.client.fetch_messages }
|
76
|
+
|
77
|
+
before { @q1.send_message(JSON.dump email) }
|
78
|
+
|
79
|
+
context 'when not yet deleted' do
|
80
|
+
it { is_expected.not_to be_empty }
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when deleted' do
|
84
|
+
before { MailHandler::SQS.client.fetch_messages.first.delete }
|
85
|
+
|
86
|
+
it { is_expected.to be_empty }
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'mailhandler'
|
3
|
+
|
4
|
+
describe 'MailHandler' do
|
5
|
+
before { @handler = MailHandler::Handler.new }
|
6
|
+
|
7
|
+
describe '#do_work' do
|
8
|
+
context 'when no messages' do
|
9
|
+
before(:each) { allow(@handler).to receive(:messages) { [] } }
|
10
|
+
|
11
|
+
it 'should go straight for sleep' do
|
12
|
+
expect(@handler).to receive(:sleep).with(1) { true }
|
13
|
+
@handler.do_work
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when messages' do
|
18
|
+
let(:message) { double }
|
19
|
+
let(:email) { double }
|
20
|
+
|
21
|
+
before(:each) do
|
22
|
+
allow(@handler).to receive(:messages) { [message] }
|
23
|
+
allow(@handler).to receive(:email_from).with(message) { email }
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should try to send the messages' do
|
27
|
+
expect(@handler).to receive(:send).with(email)
|
28
|
+
@handler.do_work
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should delete the messages unless errors' do
|
32
|
+
expect(@handler).to receive(:send) { double(:error? => false) }
|
33
|
+
expect(message).to receive(:delete)
|
34
|
+
@handler.do_work
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should not delete the messages if errors' do
|
38
|
+
expect(@handler).to receive(:send) { double(:error? => true) }
|
39
|
+
expect(message).not_to receive(:delete)
|
40
|
+
@handler.do_work
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require 'fake_sqs/test_integration'
|
3
|
+
|
4
|
+
AWS.config(
|
5
|
+
use_ssl: false,
|
6
|
+
sqs_endpoint: 'localhost',
|
7
|
+
sqs_port: 4568,
|
8
|
+
access_key_id: 'fake access key',
|
9
|
+
secret_access_key: 'fake secret key'
|
10
|
+
)
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.before(:suite) do
|
14
|
+
FAKE_SQS = FakeSQS::TestIntegration.new(database: ':memory:')
|
15
|
+
end
|
16
|
+
|
17
|
+
config.before(:all, :sqs) { FAKE_SQS.start }
|
18
|
+
|
19
|
+
config.after(:all, :sqs) { FAKE_SQS.stop }
|
20
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require 'meta/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = 'vf-email-handler'
|
9
|
+
s.version = MailHandler::VERSION
|
10
|
+
s.date = Date.today.to_s
|
11
|
+
s.authors = ['Videofy', 'Karl Litterfeldt']
|
12
|
+
s.email = 'karl.litterfeldt@videofy.me'
|
13
|
+
s.license = 'MIT-LICENSE'
|
14
|
+
|
15
|
+
s.required_ruby_version = '>=2.1.2'
|
16
|
+
|
17
|
+
s.homepage = 'http://www.videofy.me'
|
18
|
+
|
19
|
+
s.summary = 'Integrates AWS SQS with SES letting you' \
|
20
|
+
'seemlessly queue emails for sending'
|
21
|
+
|
22
|
+
s.description = 'Use this handler when you want to be able' \
|
23
|
+
'to queue up emails via Amazons Simple Queue' \
|
24
|
+
'Service, enabeling you to generate' \
|
25
|
+
'tons of emails and send them as fast as Amazons' \
|
26
|
+
'Simple Email Service will let you. Check out the' \
|
27
|
+
'readme on Github for more information.'
|
28
|
+
|
29
|
+
s.files = Dir.glob('lib/**/*') +
|
30
|
+
Dir.glob('bin/**/*') +
|
31
|
+
%w(vf-email-handler.gemspec LICENSE README.md)
|
32
|
+
|
33
|
+
s.test_files = Dir.glob('spec/**/*')
|
34
|
+
s.executables = %w(vf-email-handler)
|
35
|
+
s.require_paths = ['lib']
|
36
|
+
|
37
|
+
s.add_dependency 'aws-ses', '~> 0.5'
|
38
|
+
s.add_dependency 'aws-sdk', '~> 1.49.0'
|
39
|
+
|
40
|
+
s.add_development_dependency 'rspec', '~> 3'
|
41
|
+
s.add_development_dependency 'cane', '~> 2.6'
|
42
|
+
s.add_development_dependency 'fake_sqs', '~> 0.1'
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vf-email-handler
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Videofy
|
8
|
+
- Karl Litterfeldt
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-08-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: aws-ses
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0.5'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0.5'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: aws-sdk
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.49.0
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 1.49.0
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rspec
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '3'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '3'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: cane
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '2.6'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '2.6'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: fake_sqs
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0.1'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0.1'
|
84
|
+
description: Use this handler when you want to be ableto queue up emails via Amazons
|
85
|
+
Simple QueueService, enabeling you to generatetons of emails and send them as fast
|
86
|
+
as AmazonsSimple Email Service will let you. Check out thereadme on Github for more
|
87
|
+
information.
|
88
|
+
email: karl.litterfeldt@videofy.me
|
89
|
+
executables:
|
90
|
+
- vf-email-handler
|
91
|
+
extensions: []
|
92
|
+
extra_rdoc_files: []
|
93
|
+
files:
|
94
|
+
- LICENSE
|
95
|
+
- README.md
|
96
|
+
- bin/vf-email-handler
|
97
|
+
- lib/initializers/aws.rb
|
98
|
+
- lib/initializers/logger.rb
|
99
|
+
- lib/interfaces/ses.rb
|
100
|
+
- lib/interfaces/sqs.rb
|
101
|
+
- lib/logger/logger.rb
|
102
|
+
- lib/mailhandler.rb
|
103
|
+
- lib/meta/console_strings.rb
|
104
|
+
- lib/meta/sample-config.yml
|
105
|
+
- lib/meta/version.rb
|
106
|
+
- lib/models/email.rb
|
107
|
+
- lib/workers/threaded_worker.rb
|
108
|
+
- spec/integration_spec.rb
|
109
|
+
- spec/interfaces/ses_spec.rb
|
110
|
+
- spec/interfaces/sqs_spec.rb
|
111
|
+
- spec/mail_handler_spec.rb
|
112
|
+
- spec/spec_helper.rb
|
113
|
+
- vf-email-handler.gemspec
|
114
|
+
homepage: http://www.videofy.me
|
115
|
+
licenses:
|
116
|
+
- MIT-LICENSE
|
117
|
+
metadata: {}
|
118
|
+
post_install_message:
|
119
|
+
rdoc_options: []
|
120
|
+
require_paths:
|
121
|
+
- lib
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: 2.1.2
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
requirements: []
|
133
|
+
rubyforge_project:
|
134
|
+
rubygems_version: 2.2.2
|
135
|
+
signing_key:
|
136
|
+
specification_version: 4
|
137
|
+
summary: Integrates AWS SQS with SES letting youseemlessly queue emails for sending
|
138
|
+
test_files:
|
139
|
+
- spec/integration_spec.rb
|
140
|
+
- spec/interfaces/ses_spec.rb
|
141
|
+
- spec/interfaces/sqs_spec.rb
|
142
|
+
- spec/mail_handler_spec.rb
|
143
|
+
- spec/spec_helper.rb
|