postmark 1.14.0 → 1.15.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.
- checksums.yaml +5 -5
- data/.travis.yml +0 -3
- data/CHANGELOG.rdoc +6 -0
- data/README.md +34 -781
- data/VERSION +1 -1
- data/lib/postmark/api_client.rb +42 -0
- data/lib/postmark/error.rb +10 -0
- data/lib/postmark/handlers/mail.rb +10 -4
- data/lib/postmark/mail_message_converter.rb +15 -5
- data/lib/postmark/message_extensions/mail.rb +45 -0
- data/lib/postmark/version.rb +1 -1
- data/postmark.gemspec +1 -1
- data/postmark.png +0 -0
- data/spec/support/custom_matchers.rb +4 -0
- data/spec/unit/postmark/api_client_spec.rb +103 -23
- data/spec/unit/postmark/error_spec.rb +13 -0
- data/spec/unit/postmark/handlers/mail_spec.rb +59 -27
- data/spec/unit/postmark/mail_message_converter_spec.rb +17 -0
- data/spec/unit/postmark/message_extensions/mail_spec.rb +114 -0
- metadata +8 -7
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.15.0
|
data/lib/postmark/api_client.rb
CHANGED
@@ -27,6 +27,11 @@ module Postmark
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def deliver_message(message)
|
30
|
+
if message.templated?
|
31
|
+
raise ArgumentError,
|
32
|
+
"Please use #{self.class}#deliver_message_with_template to deliver messages with templates."
|
33
|
+
end
|
34
|
+
|
30
35
|
data = serialize(message.to_postmark_hash)
|
31
36
|
|
32
37
|
with_retries do
|
@@ -37,7 +42,26 @@ module Postmark
|
|
37
42
|
end
|
38
43
|
end
|
39
44
|
|
45
|
+
def deliver_message_with_template(message)
|
46
|
+
raise ArgumentError, 'Templated delivery requested, but the template is missing.' unless message.templated?
|
47
|
+
|
48
|
+
data = serialize(message.to_postmark_hash)
|
49
|
+
|
50
|
+
with_retries do
|
51
|
+
response, error = take_response_of { http_client.post("email/withTemplate", data) }
|
52
|
+
update_message(message, response)
|
53
|
+
raise error if error
|
54
|
+
format_response(response, true)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
40
58
|
def deliver_messages(messages)
|
59
|
+
if messages.any? { |m| m.templated? }
|
60
|
+
raise ArgumentError,
|
61
|
+
"Some of the provided messages have templates. Please use " \
|
62
|
+
"#{self.class}#deliver_messages_with_templates to deliver those."
|
63
|
+
end
|
64
|
+
|
41
65
|
in_batches(messages) do |batch, offset|
|
42
66
|
data = serialize(batch.map { |m| m.to_postmark_hash })
|
43
67
|
|
@@ -51,6 +75,24 @@ module Postmark
|
|
51
75
|
end
|
52
76
|
end
|
53
77
|
|
78
|
+
def deliver_messages_with_templates(messages)
|
79
|
+
unless messages.all? { |m| m.templated? }
|
80
|
+
raise ArgumentError, 'Templated delivery requested, but one or more messages lack templates.'
|
81
|
+
end
|
82
|
+
|
83
|
+
in_batches(messages) do |batch, offset|
|
84
|
+
data = serialize(batch.map { |m| m.to_postmark_hash })
|
85
|
+
|
86
|
+
with_retries do
|
87
|
+
http_client.post("email/batchWithTemplates", data).tap do |response|
|
88
|
+
response.each_with_index do |r, i|
|
89
|
+
update_message(messages[offset + i], r)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
54
96
|
def delivery_stats
|
55
97
|
response = format_response(http_client.get("deliverystats"), true)
|
56
98
|
|
data/lib/postmark/error.rb
CHANGED
@@ -99,12 +99,22 @@ module Postmark
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
+
class InvalidTemplateError < Error
|
103
|
+
attr_reader :postmark_response
|
104
|
+
|
105
|
+
def initialize(response)
|
106
|
+
@postmark_response = response
|
107
|
+
super('Failed to render the template. Please check #postmark_response on this error for details.')
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
102
111
|
class TimeoutError < Error
|
103
112
|
def retry?
|
104
113
|
true
|
105
114
|
end
|
106
115
|
end
|
107
116
|
|
117
|
+
class MailAdapterError < Postmark::Error; end
|
108
118
|
class UnknownMessageType < Error; end
|
109
119
|
class InvalidApiKeyError < HttpServerError; end
|
110
120
|
class InternalServerError < HttpServerError; end
|
@@ -8,10 +8,11 @@ module Mail
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def deliver!(mail)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
response = if mail.templated?
|
12
|
+
api_client.deliver_message_with_template(mail)
|
13
|
+
else
|
14
|
+
api_client.deliver_message(mail)
|
15
|
+
end
|
15
16
|
|
16
17
|
if settings[:return_response]
|
17
18
|
response
|
@@ -20,5 +21,10 @@ module Mail
|
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
24
|
+
def api_client
|
25
|
+
settings = self.settings.dup
|
26
|
+
api_token = settings.delete(:api_token) || settings.delete(:api_key)
|
27
|
+
::Postmark::ApiClient.new(api_token, settings)
|
28
|
+
end
|
23
29
|
end
|
24
30
|
end
|
@@ -7,10 +7,10 @@ module Postmark
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def run
|
10
|
-
delete_blank_fields(convert)
|
10
|
+
delete_blank_fields(pick_fields(convert, @message.templated?))
|
11
11
|
end
|
12
12
|
|
13
|
-
|
13
|
+
private
|
14
14
|
|
15
15
|
def convert
|
16
16
|
headers_part.merge(content_part)
|
@@ -32,10 +32,22 @@ module Postmark
|
|
32
32
|
'Tag' => @message.tag.to_s,
|
33
33
|
'TrackOpens' => (cast_to_bool(@message.track_opens) unless @message.track_opens.empty?),
|
34
34
|
'TrackLinks' => (::Postmark::Inflector.to_postmark(@message.track_links) unless @message.track_links.empty?),
|
35
|
-
'Metadata' => @message.metadata
|
35
|
+
'Metadata' => @message.metadata,
|
36
|
+
'TemplateAlias' => @message.template_alias,
|
37
|
+
'TemplateModel' => @message.template_model
|
36
38
|
}
|
37
39
|
end
|
38
40
|
|
41
|
+
def pick_fields(message_hash, templated = false)
|
42
|
+
fields = if templated
|
43
|
+
%w(Subject HtmlBody TextBody)
|
44
|
+
else
|
45
|
+
%w(TemplateAlias TemplateModel)
|
46
|
+
end
|
47
|
+
fields.each { |key| message_hash.delete(key) }
|
48
|
+
message_hash
|
49
|
+
end
|
50
|
+
|
39
51
|
def content_part
|
40
52
|
{
|
41
53
|
'Attachments' => @message.export_attachments,
|
@@ -44,8 +56,6 @@ module Postmark
|
|
44
56
|
}
|
45
57
|
end
|
46
58
|
|
47
|
-
protected
|
48
|
-
|
49
59
|
def cast_to_bool(val)
|
50
60
|
if val.is_a?(TrueClass) || val.is_a?(FalseClass)
|
51
61
|
val
|
@@ -61,6 +61,50 @@ module Mail
|
|
61
61
|
::Postmark::MessageHelper.attachments_to_postmark(@_attachments)
|
62
62
|
end
|
63
63
|
|
64
|
+
def template_alias(val = nil)
|
65
|
+
return self[:postmark_template_alias] && self[:postmark_template_alias].to_s if val.nil?
|
66
|
+
self[:postmark_template_alias] = val
|
67
|
+
end
|
68
|
+
|
69
|
+
attr_writer :template_model
|
70
|
+
def template_model(model = nil)
|
71
|
+
return @template_model if model.nil?
|
72
|
+
@template_model = model
|
73
|
+
end
|
74
|
+
|
75
|
+
def templated?
|
76
|
+
!!template_alias
|
77
|
+
end
|
78
|
+
|
79
|
+
def prerender
|
80
|
+
raise ::Postmark::Error, 'Cannot prerender a message without an associated template alias' unless templated?
|
81
|
+
|
82
|
+
unless delivery_method.is_a?(::Mail::Postmark)
|
83
|
+
raise ::Postmark::MailAdapterError, "Cannot render templates via #{delivery_method.class} adapter."
|
84
|
+
end
|
85
|
+
|
86
|
+
client = delivery_method.api_client
|
87
|
+
template = client.get_template(template_alias)
|
88
|
+
response = client.validate_template(template.merge(:test_render_model => template_model || {}))
|
89
|
+
|
90
|
+
raise ::Postmark::InvalidTemplateError, response unless response[:all_content_is_valid]
|
91
|
+
|
92
|
+
self.body = nil
|
93
|
+
|
94
|
+
subject response[:subject][:rendered_content]
|
95
|
+
|
96
|
+
text_part do
|
97
|
+
body response[:text_body][:rendered_content]
|
98
|
+
end
|
99
|
+
|
100
|
+
html_part do
|
101
|
+
content_type 'text/html; charset=UTF-8'
|
102
|
+
body response[:html_body][:rendered_content]
|
103
|
+
end
|
104
|
+
|
105
|
+
self
|
106
|
+
end
|
107
|
+
|
64
108
|
def text?
|
65
109
|
if defined?(super)
|
66
110
|
super
|
@@ -139,6 +183,7 @@ module Mail
|
|
139
183
|
subject tag
|
140
184
|
attachment to
|
141
185
|
track-opens track-links
|
186
|
+
postmark-template-alias
|
142
187
|
]
|
143
188
|
end
|
144
189
|
|
data/lib/postmark/version.rb
CHANGED
data/postmark.gemspec
CHANGED
data/postmark.png
ADDED
Binary file
|
@@ -10,6 +10,14 @@ describe Postmark::ApiClient do
|
|
10
10
|
delivery_method Mail::Postmark
|
11
11
|
end
|
12
12
|
}
|
13
|
+
let(:templated_message) do
|
14
|
+
Mail.new do
|
15
|
+
from "sheldon@bigbangtheory.com"
|
16
|
+
to "lenard@bigbangtheory.com"
|
17
|
+
template_alias "hello"
|
18
|
+
template_model :name => "Sheldon"
|
19
|
+
end
|
20
|
+
end
|
13
21
|
|
14
22
|
let(:api_client) {Postmark::ApiClient.new(api_token)}
|
15
23
|
subject {api_client}
|
@@ -95,6 +103,11 @@ describe Postmark::ApiClient do
|
|
95
103
|
let(:email_json) {Postmark::Json.encode(email)}
|
96
104
|
let(:http_client) {subject.http_client}
|
97
105
|
|
106
|
+
it 'raises an error when given a templated message' do
|
107
|
+
expect { subject.deliver_message(templated_message) }.
|
108
|
+
to raise_error(ArgumentError, /Please use Postmark::ApiClient\#deliver_message_with_template/)
|
109
|
+
end
|
110
|
+
|
98
111
|
it 'turns message into a JSON document and posts it to /email' do
|
99
112
|
expect(http_client).to receive(:post).with('email', email_json)
|
100
113
|
subject.deliver_message(message)
|
@@ -118,7 +131,41 @@ describe Postmark::ApiClient do
|
|
118
131
|
allow(http_client).to receive(:post).and_raise(Postmark::TimeoutError)
|
119
132
|
expect {subject.deliver_message(message)}.to raise_error(Postmark::TimeoutError)
|
120
133
|
end
|
134
|
+
end
|
121
135
|
|
136
|
+
describe "#deliver_message_with_template" do
|
137
|
+
let(:email) {templated_message.to_postmark_hash}
|
138
|
+
let(:email_json) {Postmark::Json.encode(email)}
|
139
|
+
let(:http_client) {subject.http_client}
|
140
|
+
|
141
|
+
it 'raises an error when given a non-templated message' do
|
142
|
+
expect { subject.deliver_message_with_template(message) }.
|
143
|
+
to raise_error(ArgumentError, 'Templated delivery requested, but the template is missing.')
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'turns message into a JSON document and posts it to /email' do
|
147
|
+
expect(http_client).to receive(:post).with('email/withTemplate', email_json)
|
148
|
+
subject.deliver_message_with_template(templated_message)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "retries 3 times" do
|
152
|
+
2.times do
|
153
|
+
expect(http_client).to receive(:post).and_raise(Postmark::InternalServerError)
|
154
|
+
end
|
155
|
+
expect(http_client).to receive(:post)
|
156
|
+
expect {subject.deliver_message_with_template(templated_message)}.not_to raise_error
|
157
|
+
end
|
158
|
+
|
159
|
+
it "retries on timeout" do
|
160
|
+
expect(http_client).to receive(:post).and_raise(Postmark::TimeoutError)
|
161
|
+
expect(http_client).to receive(:post)
|
162
|
+
expect {subject.deliver_message_with_template(templated_message)}.not_to raise_error
|
163
|
+
end
|
164
|
+
|
165
|
+
it "proxies errors" do
|
166
|
+
allow(http_client).to receive(:post).and_raise(Postmark::TimeoutError)
|
167
|
+
expect {subject.deliver_message_with_template(templated_message)}.to raise_error(Postmark::TimeoutError)
|
168
|
+
end
|
122
169
|
end
|
123
170
|
|
124
171
|
describe "#deliver_messages" do
|
@@ -128,6 +175,11 @@ describe Postmark::ApiClient do
|
|
128
175
|
let(:http_client) {subject.http_client}
|
129
176
|
let(:response) {[{}, {}, {}]}
|
130
177
|
|
178
|
+
it 'raises an error when given a templated message' do
|
179
|
+
expect { subject.deliver_messages([templated_message]) }.
|
180
|
+
to raise_error(ArgumentError, /Please use Postmark::ApiClient\#deliver_messages_with_templates/)
|
181
|
+
end
|
182
|
+
|
131
183
|
it 'turns array of messages into a JSON document and posts it to /email/batch' do
|
132
184
|
expect(http_client).to receive(:post).with('email/batch', emails_json) {response}
|
133
185
|
subject.deliver_messages([message, message, message])
|
@@ -146,7 +198,39 @@ describe Postmark::ApiClient do
|
|
146
198
|
expect(http_client).to receive(:post) {response}
|
147
199
|
expect {subject.deliver_messages([message, message, message])}.not_to raise_error
|
148
200
|
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "#deliver_messages_with_templates" do
|
204
|
+
let(:email) {templated_message.to_postmark_hash}
|
205
|
+
let(:emails) {[email, email, email]}
|
206
|
+
let(:emails_json) {Postmark::Json.encode(emails)}
|
207
|
+
let(:http_client) {subject.http_client}
|
208
|
+
let(:response) {[{}, {}, {}]}
|
209
|
+
let(:messages) { Array.new(3) { templated_message } }
|
149
210
|
|
211
|
+
it 'raises an error when given a templated message' do
|
212
|
+
expect { subject.deliver_messages_with_templates([message]) }.
|
213
|
+
to raise_error(ArgumentError, 'Templated delivery requested, but one or more messages lack templates.')
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'turns array of messages into a JSON document and posts it to /email/batch' do
|
217
|
+
expect(http_client).to receive(:post).with('email/batchWithTemplates', emails_json) {response}
|
218
|
+
subject.deliver_messages_with_templates(messages)
|
219
|
+
end
|
220
|
+
|
221
|
+
it "should retry 3 times" do
|
222
|
+
2.times do
|
223
|
+
expect(http_client).to receive(:post).and_raise(Postmark::InternalServerError)
|
224
|
+
end
|
225
|
+
expect(http_client).to receive(:post) {response}
|
226
|
+
expect {subject.deliver_messages_with_templates(messages)}.not_to raise_error
|
227
|
+
end
|
228
|
+
|
229
|
+
it "should retry on timeout" do
|
230
|
+
expect(http_client).to receive(:post).and_raise(Postmark::TimeoutError)
|
231
|
+
expect(http_client).to receive(:post) {response}
|
232
|
+
expect {subject.deliver_messages_with_templates(messages)}.not_to raise_error
|
233
|
+
end
|
150
234
|
end
|
151
235
|
|
152
236
|
describe "#delivery_stats" do
|
@@ -716,9 +800,9 @@ describe Postmark::ApiClient do
|
|
716
800
|
end
|
717
801
|
|
718
802
|
it 'performs a POST request to /templates with the given attributes' do
|
719
|
-
|
720
|
-
|
721
|
-
|
803
|
+
expect(http_client).to receive(:post).
|
804
|
+
with('templates', json_representation_of('Name' => 'template name')).
|
805
|
+
and_return(response)
|
722
806
|
|
723
807
|
template = subject.create_template(:name => 'template name')
|
724
808
|
|
@@ -738,9 +822,9 @@ describe Postmark::ApiClient do
|
|
738
822
|
end
|
739
823
|
|
740
824
|
it 'performs a PUT request to /templates with the given attributes' do
|
741
|
-
|
742
|
-
|
743
|
-
|
825
|
+
expect(http_client).to receive(:put).
|
826
|
+
with('templates/123', json_representation_of('Name' => 'template name')).
|
827
|
+
and_return(response)
|
744
828
|
|
745
829
|
template = subject.update_template(123, :name => 'template name')
|
746
830
|
|
@@ -796,13 +880,12 @@ describe Postmark::ApiClient do
|
|
796
880
|
end
|
797
881
|
|
798
882
|
it 'performs a POST request and returns unmodified suggested template model' do
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
expect(http_client).to receive(:post).with('templates/validate', expected_template_json).and_return(response)
|
883
|
+
expect(http_client).to receive(:post).
|
884
|
+
with('templates/validate',
|
885
|
+
json_representation_of('HtmlBody' => '{{MyName}}',
|
886
|
+
'TextBody' => '{{MyName}}',
|
887
|
+
'Subject' => '{{MyName}}')).
|
888
|
+
and_return(response)
|
806
889
|
|
807
890
|
resp = subject.validate_template(:html_body => '{{MyName}}',
|
808
891
|
:text_body => '{{MyName}}',
|
@@ -845,13 +928,11 @@ describe Postmark::ApiClient do
|
|
845
928
|
end
|
846
929
|
|
847
930
|
it 'performs a POST request and returns validation errors' do
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
expect(http_client).to receive(:post).with('templates/validate', expected_template_json).and_return(response)
|
931
|
+
expect(http_client).
|
932
|
+
to receive(:post).with('templates/validate',
|
933
|
+
json_representation_of('HtmlBody' => '{{#each}}',
|
934
|
+
'TextBody' => '{{MyName}}',
|
935
|
+
'Subject' => '{{MyName}}')).and_return(response)
|
855
936
|
|
856
937
|
resp = subject.validate_template(:html_body => '{{#each}}',
|
857
938
|
:text_body => '{{MyName}}',
|
@@ -868,12 +949,11 @@ describe Postmark::ApiClient do
|
|
868
949
|
|
869
950
|
describe "#deliver_with_template" do
|
870
951
|
let(:email) {Postmark::MessageHelper.to_postmark(message_hash)}
|
871
|
-
let(:email_json) {Postmark::Json.encode(email)}
|
872
952
|
let(:http_client) {subject.http_client}
|
873
953
|
let(:response) {{"MessageID" => 42}}
|
874
954
|
|
875
955
|
it 'converts message hash to Postmark format and posts it to /email/withTemplate' do
|
876
|
-
expect(http_client).to receive(:post).with('email/withTemplate',
|
956
|
+
expect(http_client).to receive(:post).with('email/withTemplate', json_representation_of(email)) {response}
|
877
957
|
subject.deliver_with_template(message_hash)
|
878
958
|
end
|
879
959
|
|
@@ -886,7 +966,7 @@ describe Postmark::ApiClient do
|
|
886
966
|
end
|
887
967
|
|
888
968
|
it 'converts response to ruby format' do
|
889
|
-
expect(http_client).to receive(:post).with('email/withTemplate',
|
969
|
+
expect(http_client).to receive(:post).with('email/withTemplate', json_representation_of(email)) {response}
|
890
970
|
r = subject.deliver_with_template(message_hash)
|
891
971
|
r.should have_key(:message_id)
|
892
972
|
end
|