nextmail 0.0.1 → 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.
- checksums.yaml +4 -4
- data/README.md +13 -1
- data/lib/nextmail/api_paths.rb +19 -0
- data/lib/nextmail/client.rb +4 -0
- data/lib/nextmail/constants.rb +82 -2
- data/lib/nextmail/defaults.rb +10 -0
- data/lib/nextmail/event_stream.rb +120 -0
- data/lib/nextmail/http.rb +64 -0
- data/lib/nextmail/live_event_stream.rb +169 -0
- data/lib/nextmail/protocol.rb +44 -3
- data/lib/nextmail/resources/calendar.rb +106 -12
- data/lib/nextmail/resources/contacts.rb +24 -9
- data/lib/nextmail/resources/emails.rb +5 -0
- data/lib/nextmail/resources/live_events.rb +21 -0
- data/lib/nextmail/resources/share_links.rb +32 -0
- data/lib/nextmail/resources/shared.rb +15 -0
- data/lib/nextmail/resources/tasks.rb +6 -6
- data/lib/nextmail/resources/threads.rb +16 -0
- data/lib/nextmail/resources/traffic.rb +36 -0
- data/lib/nextmail/transport.rb +118 -3
- data/lib/nextmail/version.rb +1 -1
- data/lib/nextmail/wire.rb +14 -0
- data/lib/nextmail.rb +2 -0
- metadata +8 -2
data/lib/nextmail/transport.rb
CHANGED
|
@@ -43,6 +43,21 @@ module Nextmail
|
|
|
43
43
|
deliver(outbound, repeatable.nil? ? Protocol::RETRY[:METHODS].include?(verb) : repeatable, accept, binary)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
def stream(path, query: nil, headers: nil, accept: nil, api_key: nil, timeout: nil, idle_timeout: nil, &)
|
|
47
|
+
raise ArgumentError, Defaults::MESSAGES[:STREAM_BLOCK_REQUIRED] unless block_given?
|
|
48
|
+
|
|
49
|
+
outbound = HttpRequest.new(
|
|
50
|
+
method: Protocol::HTTP_METHODS[:GET],
|
|
51
|
+
url: Endpoint.build_url(@base_url, path, query),
|
|
52
|
+
headers: headers_for(body: nil, raw: nil, content_type: nil, api_key:, inbox_token: nil, anonymous: false,
|
|
53
|
+
idempotent: false, idempotency_key: nil, accept:, extra: headers),
|
|
54
|
+
body: nil,
|
|
55
|
+
timeout: seconds(timeout.nil? ? @timeout : timeout_option(timeout))
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
open_stream(outbound, idle_timeout.nil? ? nil : seconds(timeout_option(idle_timeout)), &)
|
|
59
|
+
end
|
|
60
|
+
|
|
46
61
|
def inspect
|
|
47
62
|
"#<#{self.class.name} base_url=#{@base_url.inspect}>"
|
|
48
63
|
end
|
|
@@ -81,20 +96,116 @@ module Nextmail
|
|
|
81
96
|
response = @adapter.call(outbound)
|
|
82
97
|
HttpResponse.new(status: Integer(response.status), headers: header_table(response.headers), body: response.body.to_s)
|
|
83
98
|
rescue Timeout::Error => error
|
|
84
|
-
|
|
99
|
+
timed_out(outbound.timeout, error)
|
|
85
100
|
rescue *PROGRAMMING_ERRORS
|
|
86
101
|
raise
|
|
87
102
|
rescue => error
|
|
103
|
+
unreachable(error)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def timed_out(seconds, error)
|
|
107
|
+
NetworkError.new(format(Defaults::MESSAGES[:TIMED_OUT], seconds:), original: error, timeout: true)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def unreachable(error)
|
|
88
111
|
NetworkError.new(format(Defaults::MESSAGES[:UNREACHABLE], base_url: @base_url, reason: error.message),
|
|
89
112
|
original: error)
|
|
90
113
|
end
|
|
91
114
|
|
|
115
|
+
def open_stream(outbound, idle)
|
|
116
|
+
refusal = nil
|
|
117
|
+
result = nil
|
|
118
|
+
inside = false
|
|
119
|
+
|
|
120
|
+
begin
|
|
121
|
+
receive_stream(outbound, idle) do |response|
|
|
122
|
+
if response.ok?
|
|
123
|
+
inside = true
|
|
124
|
+
result = yield response.with(body: guarded_chunks(response.body, idle))
|
|
125
|
+
inside = false
|
|
126
|
+
else
|
|
127
|
+
refusal = refused(response)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
rescue Timeout::Error => error
|
|
131
|
+
raise if inside
|
|
132
|
+
|
|
133
|
+
raise timed_out(outbound.timeout, error)
|
|
134
|
+
rescue *PROGRAMMING_ERRORS
|
|
135
|
+
raise
|
|
136
|
+
rescue => error
|
|
137
|
+
raise if inside
|
|
138
|
+
|
|
139
|
+
raise unreachable(error)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
raise api_error(refusal, retry_after(refusal.header(Protocol::HEADER_KEYS[:RETRY_AFTER]))) unless refusal.nil?
|
|
143
|
+
|
|
144
|
+
result
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def receive_stream(outbound, idle)
|
|
148
|
+
unless @adapter.respond_to?(:stream)
|
|
149
|
+
whole = @adapter.call(outbound)
|
|
150
|
+
return yield HttpStream.new(status: Integer(whole.status), headers: header_table(whole.headers),
|
|
151
|
+
body: [whole.body.to_s])
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
@adapter.stream(outbound, idle_timeout: idle) do |response|
|
|
155
|
+
yield HttpStream.new(status: Integer(response.status), headers: header_table(response.headers),
|
|
156
|
+
body: response.body)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def refused(response)
|
|
161
|
+
body = begin
|
|
162
|
+
response.read
|
|
163
|
+
rescue *PROGRAMMING_ERRORS
|
|
164
|
+
raise
|
|
165
|
+
rescue
|
|
166
|
+
""
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
HttpResponse.new(status: response.status, headers: response.headers, body:)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def guarded_chunks(body, idle)
|
|
173
|
+
Enumerator.new do |chunks|
|
|
174
|
+
inside = false
|
|
175
|
+
|
|
176
|
+
begin
|
|
177
|
+
body.each do |chunk|
|
|
178
|
+
inside = true
|
|
179
|
+
chunks << chunk
|
|
180
|
+
inside = false
|
|
181
|
+
end
|
|
182
|
+
rescue *PROGRAMMING_ERRORS
|
|
183
|
+
raise
|
|
184
|
+
rescue => error
|
|
185
|
+
raise if inside
|
|
186
|
+
|
|
187
|
+
raise stream_failure(error, idle)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def stream_failure(error, idle)
|
|
193
|
+
timeout = error.is_a?(Timeout::Error)
|
|
194
|
+
if timeout && !idle.nil?
|
|
195
|
+
return NetworkError.new(format(Defaults::MESSAGES[:STREAM_IDLE], seconds: idle), original: error, timeout:)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
NetworkError.new(format(Defaults::MESSAGES[:STREAM_BROKEN], base_url: @base_url, reason: error.message),
|
|
199
|
+
original: error, timeout:)
|
|
200
|
+
end
|
|
201
|
+
|
|
92
202
|
def header_table(headers)
|
|
93
203
|
(headers || {}).to_h { |name, value| [name.to_s.downcase, value.is_a?(Array) ? value.join(", ") : value.to_s] }
|
|
94
204
|
end
|
|
95
205
|
|
|
96
|
-
def headers_for(body:, raw:, content_type:, api_key:, inbox_token:, anonymous:, idempotent:, idempotency_key:, accept
|
|
97
|
-
|
|
206
|
+
def headers_for(body:, raw:, content_type:, api_key:, inbox_token:, anonymous:, idempotent:, idempotency_key:, accept:,
|
|
207
|
+
extra: nil)
|
|
208
|
+
headers = @headers.merge(extra_headers(extra)).merge(
|
|
98
209
|
Protocol::HEADER_KEYS[:ACCEPT] => accept || Protocol::CONTENT_TYPES[:JSON],
|
|
99
210
|
Protocol::HEADER_KEYS[:USER_AGENT] => @user_agent
|
|
100
211
|
)
|
|
@@ -141,6 +252,10 @@ module Nextmail
|
|
|
141
252
|
"#{Protocol::IDEMPOTENCY_KEY_PREFIX}#{SecureRandom.uuid}"
|
|
142
253
|
end
|
|
143
254
|
|
|
255
|
+
def extra_headers(extra)
|
|
256
|
+
(extra || {}).to_h { |name, value| [header_name(name), header_value(value)] }
|
|
257
|
+
end
|
|
258
|
+
|
|
144
259
|
def header_name(name)
|
|
145
260
|
text = name.to_s
|
|
146
261
|
raise ArgumentError, Defaults::MESSAGES[:HEADER_SHAPE] unless HEADER_NAME.match?(text)
|
data/lib/nextmail/version.rb
CHANGED
data/lib/nextmail/wire.rb
CHANGED
|
@@ -27,6 +27,20 @@ module Nextmail
|
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
def email_raw(body)
|
|
31
|
+
recipients(body).tap do |wire|
|
|
32
|
+
wire["raw"] = raw(wire["raw"]) if wire.key?("raw")
|
|
33
|
+
wire["followUpAt"] = instant(wire["followUpAt"]) if wire.key?("followUpAt")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def raw(value)
|
|
38
|
+
return [RawBody.read(value)].pack("m0") unless value.is_a?(String) && value.encoding != Encoding::BINARY
|
|
39
|
+
return value if BASE64_TEXT.match?(value.delete(BASE64_WHITESPACE))
|
|
40
|
+
|
|
41
|
+
raise ArgumentError, Defaults::MESSAGES[:RAW_MESSAGE_SHAPE]
|
|
42
|
+
end
|
|
43
|
+
|
|
30
44
|
def with_attachments(body)
|
|
31
45
|
body.dup.tap do |wire|
|
|
32
46
|
next unless wire.key?("attachments")
|
data/lib/nextmail.rb
CHANGED
|
@@ -23,8 +23,10 @@ require_relative "nextmail/raw_body"
|
|
|
23
23
|
require_relative "nextmail/wire"
|
|
24
24
|
require_relative "nextmail/image_crop"
|
|
25
25
|
require_relative "nextmail/http"
|
|
26
|
+
require_relative "nextmail/event_stream"
|
|
26
27
|
require_relative "nextmail/update_notice"
|
|
27
28
|
require_relative "nextmail/transport"
|
|
29
|
+
require_relative "nextmail/live_event_stream"
|
|
28
30
|
require_relative "nextmail/results"
|
|
29
31
|
require_relative "nextmail/pagination"
|
|
30
32
|
require_relative "nextmail/webhook"
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: nextmail
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nextmail
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-10-
|
|
10
|
+
date: 2026-10-30 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: Send email and broadcasts, work with threads, drafts, labels, templates,
|
|
13
13
|
rules, webhooks, tracking, contacts, audiences, sign-up forms, domains, suppressions,
|
|
@@ -26,9 +26,11 @@ files:
|
|
|
26
26
|
- lib/nextmail/defaults.rb
|
|
27
27
|
- lib/nextmail/endpoint.rb
|
|
28
28
|
- lib/nextmail/errors.rb
|
|
29
|
+
- lib/nextmail/event_stream.rb
|
|
29
30
|
- lib/nextmail/http.rb
|
|
30
31
|
- lib/nextmail/image_crop.rb
|
|
31
32
|
- lib/nextmail/languages.rb
|
|
33
|
+
- lib/nextmail/live_event_stream.rb
|
|
32
34
|
- lib/nextmail/pagination.rb
|
|
33
35
|
- lib/nextmail/protocol.rb
|
|
34
36
|
- lib/nextmail/raw_body.rb
|
|
@@ -64,6 +66,7 @@ files:
|
|
|
64
66
|
- lib/nextmail/resources/knowledge.rb
|
|
65
67
|
- lib/nextmail/resources/labels.rb
|
|
66
68
|
- lib/nextmail/resources/languages.rb
|
|
69
|
+
- lib/nextmail/resources/live_events.rb
|
|
67
70
|
- lib/nextmail/resources/me.rb
|
|
68
71
|
- lib/nextmail/resources/members.rb
|
|
69
72
|
- lib/nextmail/resources/out_of_office.rb
|
|
@@ -75,6 +78,8 @@ files:
|
|
|
75
78
|
- lib/nextmail/resources/senders.rb
|
|
76
79
|
- lib/nextmail/resources/sending.rb
|
|
77
80
|
- lib/nextmail/resources/settings.rb
|
|
81
|
+
- lib/nextmail/resources/share_links.rb
|
|
82
|
+
- lib/nextmail/resources/shared.rb
|
|
78
83
|
- lib/nextmail/resources/signatures.rb
|
|
79
84
|
- lib/nextmail/resources/subscriptions.rb
|
|
80
85
|
- lib/nextmail/resources/support.rb
|
|
@@ -85,6 +90,7 @@ files:
|
|
|
85
90
|
- lib/nextmail/resources/threads.rb
|
|
86
91
|
- lib/nextmail/resources/tools.rb
|
|
87
92
|
- lib/nextmail/resources/tracking.rb
|
|
93
|
+
- lib/nextmail/resources/traffic.rb
|
|
88
94
|
- lib/nextmail/resources/webhooks.rb
|
|
89
95
|
- lib/nextmail/resources/workspaces.rb
|
|
90
96
|
- lib/nextmail/results.rb
|