challengepost-postmark 0.7.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.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/.rake_tasks +22 -0
- data/LICENSE +20 -0
- data/README.rdoc +83 -0
- data/Rakefile +69 -0
- data/VERSION +1 -0
- data/features/postmark.feature +15 -0
- data/features/step_definitions/postmark_steps.rb +0 -0
- data/features/support/env.rb +4 -0
- data/lib/postmark/bounce.rb +56 -0
- data/lib/postmark/http_client.rb +77 -0
- data/lib/postmark/json.rb +22 -0
- data/lib/postmark/mail_message_extension.rb +11 -0
- data/lib/postmark/response_parsers/active_support.rb +10 -0
- data/lib/postmark/response_parsers/json.rb +10 -0
- data/lib/postmark/response_parsers/yajl.rb +11 -0
- data/lib/postmark.rb +155 -0
- data/spec/bounce_spec.rb +51 -0
- data/spec/postmark_spec.rb +141 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +23 -0
- metadata +121 -0
data/.document
ADDED
data/.rake_tasks
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
build
|
2
|
+
check_dependencies
|
3
|
+
check_dependencies:development
|
4
|
+
check_dependencies:runtime
|
5
|
+
clobber_rcov
|
6
|
+
clobber_rdoc
|
7
|
+
features
|
8
|
+
gemcutter:release
|
9
|
+
gemspec
|
10
|
+
gemspec:generate
|
11
|
+
gemspec:validate
|
12
|
+
install
|
13
|
+
rcov
|
14
|
+
rdoc
|
15
|
+
release
|
16
|
+
rerdoc
|
17
|
+
spec
|
18
|
+
version
|
19
|
+
version:bump:major
|
20
|
+
version:bump:minor
|
21
|
+
version:bump:patch
|
22
|
+
version:write
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Petyo Ivanov
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
= Postmark gem
|
2
|
+
|
3
|
+
Ruby gem for sending emails through http://postmarkapp.com HTTP API
|
4
|
+
|
5
|
+
== Install
|
6
|
+
|
7
|
+
The gem is hosted at gemcutter. If you don't have it, install it first:
|
8
|
+
|
9
|
+
sudo gem install gemcutter
|
10
|
+
Then
|
11
|
+
sudo gem install postmark
|
12
|
+
|
13
|
+
== Example
|
14
|
+
|
15
|
+
#!/usr/bin/env ruby
|
16
|
+
|
17
|
+
require 'rubygems'
|
18
|
+
require 'postmark'
|
19
|
+
require 'tmail'
|
20
|
+
|
21
|
+
Postmark.api_key = "your-api-key"
|
22
|
+
|
23
|
+
message = TMail::Mail.new
|
24
|
+
# make sure you have a sender signature with that email
|
25
|
+
# from and to also accept arrays of emails.
|
26
|
+
message.from = "leonard@bigbangtheory.com"
|
27
|
+
message.to = "Sheldon Cooper <sheldon@bigbangtheory.com>"
|
28
|
+
message.subject = "Hi Sheldon!"
|
29
|
+
message.content_type = "text/html"
|
30
|
+
message.body = "Hello my friend!"
|
31
|
+
# set custom headers at will.
|
32
|
+
message["CUSTOM-HEADER"] = "my custom header value"
|
33
|
+
# tag message
|
34
|
+
message.tag = "my-tracking-tag"
|
35
|
+
# set reply to if you need; also, you can pass array of emails.
|
36
|
+
message.reply_to = "penny@bigbangtheory.com"
|
37
|
+
|
38
|
+
p Postmark.send_through_postmark(message).body
|
39
|
+
|
40
|
+
You can retrieve various information about your server state using the Public bounces API - http://developer.postmarkapp.com/bounces
|
41
|
+
|
42
|
+
# get delivery stats
|
43
|
+
Postmark.delivery_stats # json
|
44
|
+
Postmark::Bounce.all # [ bounce, bounce ... ]
|
45
|
+
bounce = Postmark::Bounce.find(bounce_id) # Bounce
|
46
|
+
bounce.dump # string, containing raw SMTP data
|
47
|
+
bounce.reactivate # reactivate hard bounce
|
48
|
+
|
49
|
+
== Encryption
|
50
|
+
|
51
|
+
To use SSL encryption when sending email configure the library as follows:
|
52
|
+
|
53
|
+
Postmark.secure = true
|
54
|
+
|
55
|
+
|
56
|
+
== Requirements
|
57
|
+
|
58
|
+
The gem relies on TMail for building the message. You will also need postmark account, server and sender signature set up to use it.
|
59
|
+
If you plan using it in a rails project, check out the postmark-rails gem, which is meant to integrate with ActionMailer.
|
60
|
+
|
61
|
+
The plugin will try to use ActiveSupport Json if it is already included. If not, it will attempt using the built-in ruby Json library.
|
62
|
+
You can also explicitly specify which one to be used, using
|
63
|
+
|
64
|
+
Postmark.response_parser_class = :Json # :ActiveSupport or :Yajl is also supported.
|
65
|
+
|
66
|
+
== Limitations
|
67
|
+
|
68
|
+
Currently postmark API does not support attachments. For more information, check the docs at:
|
69
|
+
|
70
|
+
http://developer.postmarkapp.com
|
71
|
+
|
72
|
+
== Note on Patches/Pull Requests
|
73
|
+
|
74
|
+
* Fork the project.
|
75
|
+
* Make your feature addition or bug fix.
|
76
|
+
* Add tests for it. This is important so I don't break it in a
|
77
|
+
future version unintentionally.
|
78
|
+
* Commit, do not mess with rakefile, version, or history.
|
79
|
+
* Send me a pull request. Bonus points for topic branches.
|
80
|
+
|
81
|
+
== Copyright
|
82
|
+
|
83
|
+
Copyright (c) 2009 Wildbit LLC. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "challengepost-postmark"
|
8
|
+
gem.summary = %Q{Ruby gem for sending emails through http://postmarkapp.com HTTP API}
|
9
|
+
gem.description = %Q{Ruby gem for sending emails through http://postmarkapp.com HTTP API. It relieas on the mail gem for message construction.}
|
10
|
+
gem.email = "underlog@gmail.com"
|
11
|
+
gem.homepage = "http://postmarkapp.com"
|
12
|
+
gem.authors = ["Petyo Ivanov"]
|
13
|
+
gem.add_development_dependency "rspec"
|
14
|
+
gem.add_development_dependency "cucumber"
|
15
|
+
gem.add_dependency "mail"
|
16
|
+
gem.files << "lib/postmark/mail_message_extension.rb"
|
17
|
+
gem.post_install_message = %q[
|
18
|
+
==================
|
19
|
+
Thanks for installing the postmark gem. If you don't have an account, please sign up at http://postmarkapp.com/.
|
20
|
+
Review the README.rdoc for implementation details and examples.
|
21
|
+
==================
|
22
|
+
]
|
23
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
24
|
+
end
|
25
|
+
Jeweler::GemcutterTasks.new
|
26
|
+
rescue LoadError
|
27
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
28
|
+
end
|
29
|
+
|
30
|
+
require 'spec/rake/spectask'
|
31
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
32
|
+
spec.libs << 'lib' << 'spec'
|
33
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
34
|
+
end
|
35
|
+
|
36
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
37
|
+
spec.libs << 'lib' << 'spec'
|
38
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
39
|
+
spec.rcov = true
|
40
|
+
end
|
41
|
+
|
42
|
+
task :spec => :check_dependencies
|
43
|
+
|
44
|
+
begin
|
45
|
+
require 'cucumber/rake/task'
|
46
|
+
Cucumber::Rake::Task.new(:features)
|
47
|
+
|
48
|
+
task :features => :check_dependencies
|
49
|
+
rescue LoadError
|
50
|
+
task :features do
|
51
|
+
abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
task :default => :spec
|
56
|
+
|
57
|
+
require 'rake/rdoctask'
|
58
|
+
Rake::RDocTask.new do |rdoc|
|
59
|
+
if File.exist?('VERSION')
|
60
|
+
version = File.read('VERSION')
|
61
|
+
else
|
62
|
+
version = ""
|
63
|
+
end
|
64
|
+
|
65
|
+
rdoc.rdoc_dir = 'rdoc'
|
66
|
+
rdoc.title = "postmark #{version}"
|
67
|
+
rdoc.rdoc_files.include('README*')
|
68
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
69
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.7.1
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Feature: sending emails through Postmark API
|
2
|
+
In order to communicate effectively with the world
|
3
|
+
My application should be able to deliver emails through the postmark API
|
4
|
+
|
5
|
+
Background:
|
6
|
+
Given the service listens to "http://postmarkapp.local"
|
7
|
+
And I have an account with api key "mykey"
|
8
|
+
|
9
|
+
Scenario: Sending simple email
|
10
|
+
Given I send the following email:
|
11
|
+
| From | leonard@bigbangtheory.com |
|
12
|
+
| To | sheldon@bigbangtheory.com |
|
13
|
+
| Subject | About that last night |
|
14
|
+
| Body | Sorry for not coming |
|
15
|
+
Then the service should receive an email on behalf of "mykey" for "sheldon@bigbangtheory.com"
|
File without changes
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Postmark
|
2
|
+
class Bounce
|
3
|
+
|
4
|
+
attr_reader :email, :bounced_at, :type, :details, :name, :id, :server_id, :tag
|
5
|
+
|
6
|
+
def initialize(values = {})
|
7
|
+
@id = values["ID"]
|
8
|
+
@email = values["Email"]
|
9
|
+
@bounced_at = Time.parse(values["BouncedAt"])
|
10
|
+
@type = values["Type"]
|
11
|
+
@name = values["Name"]
|
12
|
+
@details = values["Details"]
|
13
|
+
@tag = values["Tag"]
|
14
|
+
@dump_available = values["DumpAvailable"]
|
15
|
+
@inactive = values["Inactive"]
|
16
|
+
@can_activate = values["CanActivate"]
|
17
|
+
end
|
18
|
+
|
19
|
+
def inactive?
|
20
|
+
!!@inactive
|
21
|
+
end
|
22
|
+
|
23
|
+
def can_activate?
|
24
|
+
!!@can_activate
|
25
|
+
end
|
26
|
+
|
27
|
+
def dump
|
28
|
+
Postmark::HttpClient.get("bounces/#{id}/dump")["Body"]
|
29
|
+
end
|
30
|
+
|
31
|
+
def activate
|
32
|
+
Bounce.new(Postmark::HttpClient.put("bounces/#{id}/activate")["Bounce"])
|
33
|
+
end
|
34
|
+
|
35
|
+
def dump_available?
|
36
|
+
!!@dump_available
|
37
|
+
end
|
38
|
+
|
39
|
+
class << self
|
40
|
+
def find(id)
|
41
|
+
Bounce.new(Postmark::HttpClient.get("bounces/#{id}"))
|
42
|
+
end
|
43
|
+
|
44
|
+
def all(options = {})
|
45
|
+
options[:count] ||= 30
|
46
|
+
options[:offset] ||= 0
|
47
|
+
Postmark::HttpClient.get("bounces", options).map { |bounce_json| Bounce.new(bounce_json) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def tags
|
51
|
+
Postmark::HttpClient.get("bounces/tags")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module Postmark
|
4
|
+
module HttpClient
|
5
|
+
class << self
|
6
|
+
def post(path, data = '')
|
7
|
+
handle_response(http.post(url_path(path), data, headers))
|
8
|
+
end
|
9
|
+
|
10
|
+
def put(path, data = '')
|
11
|
+
handle_response(http.put(url_path(path), data, headers))
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(path, query = {})
|
15
|
+
handle_response(http.get(url_path(path + to_query_string(query)), headers))
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def to_query_string(hash)
|
21
|
+
return "" if hash.empty?
|
22
|
+
"?" + hash.map { |key, value| "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}" }.join("&")
|
23
|
+
end
|
24
|
+
|
25
|
+
def protocol
|
26
|
+
Postmark.secure ? "https" : "http"
|
27
|
+
end
|
28
|
+
|
29
|
+
def url
|
30
|
+
URI.parse("#{protocol}://#{Postmark.host}:#{Postmark.port}/")
|
31
|
+
end
|
32
|
+
|
33
|
+
def handle_response(response)
|
34
|
+
case response.code.to_i
|
35
|
+
when 200
|
36
|
+
return Postmark::Json.decode(response.body)
|
37
|
+
when 401
|
38
|
+
raise InvalidApiKeyError, error_message(response.body)
|
39
|
+
when 422
|
40
|
+
raise InvalidMessageError, error_message(response.body)
|
41
|
+
when 500
|
42
|
+
raise InternalServerError, response.body
|
43
|
+
else
|
44
|
+
raise UnknownError, response
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def headers
|
49
|
+
@headers ||= HEADERS.merge({ "X-Postmark-Server-Token" => Postmark.api_key.to_s })
|
50
|
+
end
|
51
|
+
|
52
|
+
def url_path(path)
|
53
|
+
Postmark.path_prefix + path
|
54
|
+
end
|
55
|
+
|
56
|
+
def http
|
57
|
+
@http ||= build_http
|
58
|
+
end
|
59
|
+
|
60
|
+
def build_http
|
61
|
+
http = Net::HTTP::Proxy(Postmark.proxy_host,
|
62
|
+
Postmark.proxy_port,
|
63
|
+
Postmark.proxy_user,
|
64
|
+
Postmark.proxy_pass).new(url.host, url.port)
|
65
|
+
|
66
|
+
http.read_timeout = Postmark.http_read_timeout
|
67
|
+
http.open_timeout = Postmark.http_open_timeout
|
68
|
+
http.use_ssl = !!Postmark.secure
|
69
|
+
http
|
70
|
+
end
|
71
|
+
|
72
|
+
def error_message(response_body)
|
73
|
+
Postmark::Json.decode(response_body)["Message"]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Postmark
|
2
|
+
module Json
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def encode(data)
|
6
|
+
json_parser
|
7
|
+
data.to_json
|
8
|
+
end
|
9
|
+
|
10
|
+
def decode(data)
|
11
|
+
json_parser.decode(data)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def json_parser
|
17
|
+
ResponseParsers.const_get(Postmark.response_parser_class)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/postmark.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'yaml'
|
5
|
+
require 'mail'
|
6
|
+
require 'postmark/bounce'
|
7
|
+
require 'postmark/json'
|
8
|
+
require 'postmark/http_client'
|
9
|
+
require 'postmark/mail_message_extension'
|
10
|
+
|
11
|
+
module Postmark
|
12
|
+
|
13
|
+
class InvalidApiKeyError < StandardError; end
|
14
|
+
class UnknownError < StandardError; end
|
15
|
+
class InvalidMessageError < StandardError; end
|
16
|
+
class InternalServerError < StandardError; end
|
17
|
+
|
18
|
+
module ResponseParsers
|
19
|
+
autoload :Json, 'postmark/response_parsers/json'
|
20
|
+
autoload :ActiveSupport, 'postmark/response_parsers/active_support'
|
21
|
+
autoload :Yajl, 'postmark/response_parsers/yajl'
|
22
|
+
end
|
23
|
+
|
24
|
+
HEADERS = {
|
25
|
+
'Content-type' => 'application/json',
|
26
|
+
'Accept' => 'application/json'
|
27
|
+
}
|
28
|
+
|
29
|
+
MAX_RETRIES = 2
|
30
|
+
|
31
|
+
class << self
|
32
|
+
attr_accessor :host, :path_prefix, :port, :secure, :api_key, :http_open_timeout, :http_read_timeout,
|
33
|
+
:proxy_host, :proxy_port, :proxy_user, :proxy_pass, :max_retries, :sleep_between_retries
|
34
|
+
|
35
|
+
attr_writer :response_parser_class
|
36
|
+
|
37
|
+
def response_parser_class
|
38
|
+
@response_parser_class ||= Object.const_defined?(:ActiveSupport) ? :ActiveSupport : :Json
|
39
|
+
end
|
40
|
+
|
41
|
+
# The port on which your Postmark server runs.
|
42
|
+
def port
|
43
|
+
@port || (secure ? 443 : 80)
|
44
|
+
end
|
45
|
+
|
46
|
+
# The host to connect to.
|
47
|
+
def host
|
48
|
+
@host ||= 'api.postmarkapp.com'
|
49
|
+
end
|
50
|
+
|
51
|
+
# The path of the listener
|
52
|
+
def path_prefix
|
53
|
+
@path_prefix ||= '/'
|
54
|
+
end
|
55
|
+
|
56
|
+
def http_open_timeout
|
57
|
+
@http_open_timeout ||= 5
|
58
|
+
end
|
59
|
+
|
60
|
+
def http_read_timeout
|
61
|
+
@http_read_timeout ||= 15
|
62
|
+
end
|
63
|
+
|
64
|
+
def max_retries
|
65
|
+
@max_retries ||= 3
|
66
|
+
end
|
67
|
+
|
68
|
+
def sleep_between_retries
|
69
|
+
@sleep_between_retries ||= 10
|
70
|
+
end
|
71
|
+
|
72
|
+
def configure
|
73
|
+
yield self
|
74
|
+
end
|
75
|
+
|
76
|
+
def send_through_postmark(message) #:nodoc:
|
77
|
+
@retries = 0
|
78
|
+
begin
|
79
|
+
HttpClient.post("email", Postmark::Json.encode(convert_mail(message)))
|
80
|
+
rescue Exception => e
|
81
|
+
if @retries < max_retries
|
82
|
+
@retries += 1
|
83
|
+
retry
|
84
|
+
else
|
85
|
+
raise
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def delivery_stats
|
91
|
+
HttpClient.get("deliverystats")
|
92
|
+
end
|
93
|
+
|
94
|
+
protected
|
95
|
+
|
96
|
+
def convert_mail(message)
|
97
|
+
options = { "From" => message['from'].to_s, "To" => message['to'].to_s, "Subject" => message.subject }
|
98
|
+
|
99
|
+
headers = extract_headers(message)
|
100
|
+
options["Headers"] = headers unless headers.length == 0
|
101
|
+
|
102
|
+
options["Tag"] = message['tag'].to_s unless message.tag.nil?
|
103
|
+
|
104
|
+
options["Cc"] = message['cc'].to_s unless message.cc.nil?
|
105
|
+
|
106
|
+
options["Bcc"] = message['bcc'].to_s unless message.bcc.nil?
|
107
|
+
|
108
|
+
if reply_to = message['reply-to']
|
109
|
+
options["ReplyTo"] = reply_to.to_s
|
110
|
+
end
|
111
|
+
|
112
|
+
if message.multipart?
|
113
|
+
options["HtmlBody"] = message.html_part
|
114
|
+
options["TextBody"] = message.text_part
|
115
|
+
elsif message.content_type == "text/html"
|
116
|
+
options["HtmlBody"] = message.body
|
117
|
+
else
|
118
|
+
options["TextBody"] = message.body
|
119
|
+
end
|
120
|
+
options
|
121
|
+
end
|
122
|
+
|
123
|
+
def extract_headers(message)
|
124
|
+
headers = []
|
125
|
+
message.header.fields.each do |field|
|
126
|
+
name = field.name.downcase
|
127
|
+
next if bogus_headers.include? name
|
128
|
+
name = name.split(/-/).map {|i| i.capitalize }.join('-')
|
129
|
+
headers << { "Name" => name, "Value" => field.value }
|
130
|
+
end
|
131
|
+
headers
|
132
|
+
end
|
133
|
+
|
134
|
+
def bogus_headers
|
135
|
+
%q[
|
136
|
+
return-path
|
137
|
+
x-pm-rcpt
|
138
|
+
from
|
139
|
+
reply-to
|
140
|
+
sender
|
141
|
+
received
|
142
|
+
date
|
143
|
+
content-type
|
144
|
+
cc
|
145
|
+
bcc
|
146
|
+
subject
|
147
|
+
tag
|
148
|
+
]
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
self.response_parser_class = nil
|
154
|
+
|
155
|
+
end
|
data/spec/bounce_spec.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Bounce" do
|
4
|
+
let(:bounce_json) { %{{"Type":"HardBounce","TypeCode":1,"Details":"test bounce","Email":"jim@test.com","BouncedAt":"#{Time.now.to_s}","DumpAvailable":true,"Inactive":false,"CanActivate":true,"ID":12}} }
|
5
|
+
let(:bounces_json) { "[#{bounce_json},#{bounce_json}]" }
|
6
|
+
|
7
|
+
context "single bounce" do
|
8
|
+
let(:bounce) { Postmark::Bounce.find(12) }
|
9
|
+
|
10
|
+
before do
|
11
|
+
Timecop.freeze
|
12
|
+
FakeWeb.register_uri(:get, "http://api.postmarkapp.com/bounces/12", { :body => bounce_json })
|
13
|
+
end
|
14
|
+
|
15
|
+
after do
|
16
|
+
Timecop.return
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should retrieve and parce bounce correctly" do
|
20
|
+
bounce.type.should == "HardBounce"
|
21
|
+
bounce.bounced_at.should == Time.now
|
22
|
+
bounce.details.should == "test bounce"
|
23
|
+
bounce.email.should == "jim@test.com"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should retrieve bounce dump" do
|
27
|
+
FakeWeb.register_uri(:get, "http://api.postmarkapp.com/bounces/12/dump", { :body => %{{"Body": "Some SMTP gibberish"}} } )
|
28
|
+
bounce.dump.should == "Some SMTP gibberish"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should activate inactive bounce" do
|
32
|
+
FakeWeb.register_uri(:put, "http://api.postmarkapp.com/bounces/12/activate", { :body => %{{"Message":"OK","Bounce":#{bounce_json}}} } )
|
33
|
+
bounce.activate.should be_a(Postmark::Bounce)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should retrieve bounces" do
|
39
|
+
FakeWeb.register_uri(:get, "http://api.postmarkapp.com/bounces?count=30&offset=0", { :body => bounces_json })
|
40
|
+
bounces = Postmark::Bounce.all
|
41
|
+
bounces.should have(2).entries
|
42
|
+
bounces[0].should be_a(Postmark::Bounce)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should retrieve bounce tags" do
|
46
|
+
FakeWeb.register_uri(:get, "http://api.postmarkapp.com/bounces/tags", { :body => '["Signup","Commit Notification"]' })
|
47
|
+
tags = Postmark::Bounce.tags
|
48
|
+
tags.should have(2).entries
|
49
|
+
tags.first.should == "Signup"
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Postmark" do
|
4
|
+
|
5
|
+
let :message do
|
6
|
+
Mail.new.tap do |mail|
|
7
|
+
mail.from = "sheldon@bigbangtheory.com"
|
8
|
+
mail.to = "lenard@bigbangtheory.com"
|
9
|
+
mail.subject = "Hello!"
|
10
|
+
mail.body = "Hello Sheldon!"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let :html_message do
|
15
|
+
Mail.new.tap do |mail|
|
16
|
+
mail.from = "sheldon@bigbangtheory.com"
|
17
|
+
mail.to = "lenard@bigbangtheory.com"
|
18
|
+
mail.subject = "Hello!"
|
19
|
+
mail.body = "<b>Hello Sheldon!</b>"
|
20
|
+
mail.content_type = "text/html"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "service call" do
|
25
|
+
|
26
|
+
before(:all) do
|
27
|
+
Postmark.sleep_between_retries = 0
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should send email successfully" do
|
31
|
+
FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email", {})
|
32
|
+
Postmark.send_through_postmark(message)
|
33
|
+
FakeWeb.should have_requested(:post, "http://api.postmarkapp.com/email")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should warn when header is invalid" do
|
37
|
+
FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email", {:status => [ "401", "Unauthorized" ], :body => "Missing API token"})
|
38
|
+
lambda { Postmark.send_through_postmark(message) }.should raise_error(Postmark::InvalidApiKeyError)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should warn when json is not ok" do
|
42
|
+
FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email", {:status => [ "422", "Invalid" ], :body => "Invalid JSON"})
|
43
|
+
lambda { Postmark.send_through_postmark(message) }.should raise_error(Postmark::InvalidMessageError)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should warn when server fails" do
|
47
|
+
FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email", {:status => [ "500", "Internal Server Error" ]})
|
48
|
+
lambda { Postmark.send_through_postmark(message) }.should raise_error(Postmark::InternalServerError)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should warn when unknown stuff fails" do
|
52
|
+
FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email", {:status => [ "485", "Custom HTTP response status" ]})
|
53
|
+
lambda { Postmark.send_through_postmark(message) }.should raise_error(Postmark::UnknownError)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should retry 3 times" do
|
57
|
+
FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email",
|
58
|
+
[ { :status => [ 500, "Internal Server Error" ] },
|
59
|
+
{ :status => [ 500, "Internal Server Error" ] },
|
60
|
+
{ } ]
|
61
|
+
)
|
62
|
+
lambda { Postmark.send_through_postmark(message) }.should_not raise_error
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "delivery stats" do
|
67
|
+
let(:response_body) { %{{"InactiveMails":1,"Bounces":[{"TypeCode":0,"Name":"All","Count":2},{"Type":"HardBounce","TypeCode":1,"Name":"Hard bounce","Count":1},{"Type":"SoftBounce","TypeCode":4096,"Name":"Soft bounce","Count":1}]}} }
|
68
|
+
|
69
|
+
it "should query the service for delivery stats" do
|
70
|
+
FakeWeb.register_uri(:get, "http://api.postmarkapp.com/deliverystats", { :body => response_body })
|
71
|
+
results = Postmark.delivery_stats
|
72
|
+
results["InactiveMails"].should == 1
|
73
|
+
results["Bounces"].should be_an(Array)
|
74
|
+
results["Bounces"].should have(3).entries
|
75
|
+
FakeWeb.should have_requested(:get, "http://api.postmarkapp.com/deliverystats")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "mail parse" do
|
80
|
+
it "should set text body for plain message" do
|
81
|
+
Postmark.send(:convert_mail, message)['TextBody'].should_not be_nil
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should set html body for html message" do
|
85
|
+
Postmark.send(:convert_mail, html_message)['HtmlBody'].should_not be_nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def be_serialized_to(json)
|
90
|
+
simple_matcher "be serialized to #{json}" do |message|
|
91
|
+
Postmark.send(:convert_mail, message).should == JSON.parse(json)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should encode custom headers headers properly" do
|
96
|
+
message["CUSTOM-HEADER"] = "header"
|
97
|
+
message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!", "Headers":[{"Name":"Custom-Header", "Value":"header"}]}]
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should encode reply to" do
|
101
|
+
message.reply_to = ['a@a.com', 'b@b.com']
|
102
|
+
message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "ReplyTo":"a@a.com, b@b.com", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}]
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should encode tag" do
|
106
|
+
message.tag = "invite"
|
107
|
+
message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "Tag":"invite", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}]
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should encode multiple recepients (TO)" do
|
111
|
+
message.to = ['a@a.com', 'b@b.com']
|
112
|
+
message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"a@a.com, b@b.com", "TextBody":"Hello Sheldon!"}]
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should encode multiple recepients (CC)" do
|
116
|
+
message.cc = ['a@a.com', 'b@b.com']
|
117
|
+
message.should be_serialized_to %q[{"Cc":"a@a.com, b@b.com", "Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}]
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should encode multiple recepients (BCC)" do
|
121
|
+
message.bcc = ['a@a.com', 'b@b.com']
|
122
|
+
message.should be_serialized_to %q[{"Bcc":"a@a.com, b@b.com", "Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}]
|
123
|
+
end
|
124
|
+
|
125
|
+
context "JSON library support" do
|
126
|
+
[:Json, :ActiveSupport, :Yajl].each do |lib|
|
127
|
+
begin
|
128
|
+
original_parser_class = Postmark.response_parser_class
|
129
|
+
|
130
|
+
it "decodes json with #{lib}" do
|
131
|
+
Postmark.response_parser_class = lib
|
132
|
+
Postmark::Json.decode(%({"Message":"OK"})).should == { "Message" => "OK" }
|
133
|
+
end
|
134
|
+
|
135
|
+
Postmark.response_parser_class = original_parser_class
|
136
|
+
rescue LoadError # No ActiveSupport or Yajl :(
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'postmark'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'active_support'
|
6
|
+
require 'json'
|
7
|
+
require 'ruby-debug'
|
8
|
+
require 'fakeweb'
|
9
|
+
require 'fakeweb_matcher'
|
10
|
+
require 'timecop'
|
11
|
+
require 'spec'
|
12
|
+
require 'spec/autorun'
|
13
|
+
require 'yaml'
|
14
|
+
|
15
|
+
if ENV['JSONGEM']
|
16
|
+
# `JSONGEM=Yajl rake spec`
|
17
|
+
Postmark.response_parser_class = ENV['JSONGEM'].to_sym
|
18
|
+
puts "Setting ResponseParser class to #{Postmark::ResponseParsers.const_get Postmark.response_parser_class}"
|
19
|
+
end
|
20
|
+
|
21
|
+
Spec::Runner.configure do |config|
|
22
|
+
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: challengepost-postmark
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 7
|
8
|
+
- 1
|
9
|
+
version: 0.7.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Petyo Ivanov
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-06-07 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :development
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: cucumber
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
version: "0"
|
42
|
+
type: :development
|
43
|
+
version_requirements: *id002
|
44
|
+
- !ruby/object:Gem::Dependency
|
45
|
+
name: mail
|
46
|
+
prerelease: false
|
47
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
segments:
|
52
|
+
- 0
|
53
|
+
version: "0"
|
54
|
+
type: :runtime
|
55
|
+
version_requirements: *id003
|
56
|
+
description: Ruby gem for sending emails through http://postmarkapp.com HTTP API. It relieas on the mail gem for message construction.
|
57
|
+
email: underlog@gmail.com
|
58
|
+
executables: []
|
59
|
+
|
60
|
+
extensions: []
|
61
|
+
|
62
|
+
extra_rdoc_files:
|
63
|
+
- LICENSE
|
64
|
+
- README.rdoc
|
65
|
+
files:
|
66
|
+
- .document
|
67
|
+
- .gitignore
|
68
|
+
- .rake_tasks
|
69
|
+
- LICENSE
|
70
|
+
- README.rdoc
|
71
|
+
- Rakefile
|
72
|
+
- VERSION
|
73
|
+
- features/postmark.feature
|
74
|
+
- features/step_definitions/postmark_steps.rb
|
75
|
+
- features/support/env.rb
|
76
|
+
- lib/postmark.rb
|
77
|
+
- lib/postmark/bounce.rb
|
78
|
+
- lib/postmark/http_client.rb
|
79
|
+
- lib/postmark/json.rb
|
80
|
+
- lib/postmark/mail_message_extension.rb
|
81
|
+
- lib/postmark/response_parsers/active_support.rb
|
82
|
+
- lib/postmark/response_parsers/json.rb
|
83
|
+
- lib/postmark/response_parsers/yajl.rb
|
84
|
+
- spec/bounce_spec.rb
|
85
|
+
- spec/postmark_spec.rb
|
86
|
+
- spec/spec.opts
|
87
|
+
- spec/spec_helper.rb
|
88
|
+
has_rdoc: true
|
89
|
+
homepage: http://postmarkapp.com
|
90
|
+
licenses: []
|
91
|
+
|
92
|
+
post_install_message: "\n ==================\n Thanks for installing the postmark gem. If you don't have an account, please sign up at http://postmarkapp.com/.\n Review the README.rdoc for implementation details and examples.\n ==================\n "
|
93
|
+
rdoc_options:
|
94
|
+
- --charset=UTF-8
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
segments:
|
109
|
+
- 0
|
110
|
+
version: "0"
|
111
|
+
requirements: []
|
112
|
+
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 1.3.6
|
115
|
+
signing_key:
|
116
|
+
specification_version: 3
|
117
|
+
summary: Ruby gem for sending emails through http://postmarkapp.com HTTP API
|
118
|
+
test_files:
|
119
|
+
- spec/bounce_spec.rb
|
120
|
+
- spec/postmark_spec.rb
|
121
|
+
- spec/spec_helper.rb
|