mailengin 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 +7 -0
- data/CHANGELOG.md +15 -0
- data/LICENSE +9 -0
- data/README.md +271 -0
- data/lib/mailengin/client.rb +58 -0
- data/lib/mailengin/emails.rb +65 -0
- data/lib/mailengin/error.rb +19 -0
- data/lib/mailengin/transport.rb +11 -0
- data/lib/mailengin/version.rb +3 -0
- data/lib/mailengin.rb +6 -0
- metadata +53 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 88d3555674981032a0373f45f7d3dcde4e8d7d66fc54df3db1518ea1555b4aa7
|
|
4
|
+
data.tar.gz: 69d7013ba3242bcf870f7355d8749e9e8c66a6f52dd394cdd70d1b643f853e9e
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 42eabe1d62d66e95dfc74b88ebf9fede0a2751b5c4dcad43993d62052a416b8e5951504d83729f0ff0c92d2ed07920c0c1e5784355ff3c15d391c343ad149318
|
|
7
|
+
data.tar.gz: bc0c863858e5e2d1127e141ed205cb3992fd91f5c75c11ce8e39ee609fbd9be12531db45438e69ac58ac348d720bd67de2960bb5ed07b79784a4a829a5091231
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this gem will be documented here.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## Unreleased
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Initial Ruby SDK with keyword-based APIs and immutable response objects.
|
|
12
|
+
- Single and personalized bulk email operations.
|
|
13
|
+
- Template, raw HTML, variables, sender override, and reply-routing support.
|
|
14
|
+
- Structured API, timeout, malformed-response, and network errors.
|
|
15
|
+
- Injectable transport, Minitest, CI, and trusted RubyGems publishing.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MailEngin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# MailEngin Ruby SDK
|
|
2
|
+
|
|
3
|
+
[](https://www.ruby-lang.org/)
|
|
4
|
+
[](https://rubygems.org/)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
|
|
7
|
+
The official Ruby SDK for sending transactional email through [MailEngin](https://mailengin.app). It uses the Ruby standard library and provides keyword-based APIs, immutable response objects, configurable timeouts, injectable transports, and structured errors.
|
|
8
|
+
|
|
9
|
+
> [!IMPORTANT]
|
|
10
|
+
> This gem is for server-side applications only. Never expose a MailEngin API key in browser, mobile, desktop, or other client-distributed code.
|
|
11
|
+
|
|
12
|
+
## Requirements
|
|
13
|
+
|
|
14
|
+
- Ruby 3.2 or newer
|
|
15
|
+
- A MailEngin API key and verified sending domain
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
Add the gem to your `Gemfile`:
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
gem "mailengin", "~> 0.1"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then run:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
bundle install
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or install it directly:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
gem install mailengin
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Before You Send
|
|
38
|
+
|
|
39
|
+
1. [Verify a sending domain](https://mailengin.app/dashboard/domains).
|
|
40
|
+
2. [Create an API key](https://mailengin.app/dashboard/api-keys) and save the full secret.
|
|
41
|
+
3. [Create and publish a Developer Template](https://mailengin.app/dashboard/dev-templates).
|
|
42
|
+
4. Copy the template API name, such as `welcome-email`.
|
|
43
|
+
|
|
44
|
+
Store the key in your runtime's secret manager or environment:
|
|
45
|
+
|
|
46
|
+
```env
|
|
47
|
+
MAILENGIN_API_KEY=re_your_full_secret_key
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
MailEngin displays the full key only once. A masked key cannot authenticate requests.
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
require "mailengin"
|
|
56
|
+
|
|
57
|
+
client = MailEngin::Client.new(api_key: ENV.fetch("MAILENGIN_API_KEY"))
|
|
58
|
+
|
|
59
|
+
email = client.emails.send(
|
|
60
|
+
to: "user@example.com",
|
|
61
|
+
from_email: "hello@yourdomain.com",
|
|
62
|
+
template_name: "welcome-email",
|
|
63
|
+
variables: { first_name: "Asha" }
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
puts email.id
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The published template supplies the subject and HTML. Values in `variables` replace matching template variables such as `{{first_name}}`.
|
|
70
|
+
|
|
71
|
+
## Send One Email
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
email = client.emails.send(
|
|
75
|
+
to: "customer@example.com",
|
|
76
|
+
from_email: "hello@yourdomain.com",
|
|
77
|
+
template_name: "account-verification",
|
|
78
|
+
variables: {
|
|
79
|
+
first_name: "Asha",
|
|
80
|
+
verification_url: "https://yourapp.com/verify/token"
|
|
81
|
+
},
|
|
82
|
+
reply_to_mailengin: true
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
puts "Queued email #{email.id} at #{email.created_at}"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Send keyword arguments
|
|
89
|
+
|
|
90
|
+
| Keyword | Type | Required | Description |
|
|
91
|
+
| --- | --- | --- | --- |
|
|
92
|
+
| `to` | `String` | Yes | Recipient email address. |
|
|
93
|
+
| `template_name` | `String` | Recommended | Published template API name or exact display name. |
|
|
94
|
+
| `template_id` | `String` | No | Legacy template identifier. Prefer `template_name`. |
|
|
95
|
+
| `variables` | `Hash` | No | Values used to render template variables. |
|
|
96
|
+
| `subject` | `String` | Raw HTML only | Template subject override, or required subject for raw HTML. |
|
|
97
|
+
| `from_email` | `String` | Recommended | Sender on a verified domain authorized for the API key. |
|
|
98
|
+
| `html` | `String` | Advanced | Raw HTML used when no template is supplied. |
|
|
99
|
+
| `reply_to_mailengin` | `true` / `false` | No | Route recipient replies into the MailEngin inbox. |
|
|
100
|
+
|
|
101
|
+
Exactly one content source is required: `template_name`, `template_id`, or `html`. Raw HTML sends also require `subject`.
|
|
102
|
+
|
|
103
|
+
## Send Personalized Bulk Email
|
|
104
|
+
|
|
105
|
+
Bulk requests support up to 1,000 recipients. Request-level variables apply to every recipient; recipient variables take precedence.
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
job = client.emails.send_bulk(
|
|
109
|
+
to: [
|
|
110
|
+
{
|
|
111
|
+
email: "asha@example.com",
|
|
112
|
+
variables: { first_name: "Asha" }
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
email: "ben@example.com",
|
|
116
|
+
variables: { first_name: "Ben" }
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
from_email: "hello@yourdomain.com",
|
|
120
|
+
template_name: "product-update",
|
|
121
|
+
variables: { product_name: "MailEngin" }
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
puts "Queued #{job.queued_count} recipients in job #{job.job_id}"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
For recipients without individual variables, use strings:
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
job = client.emails.send_bulk(
|
|
131
|
+
to: ["a@example.com", "b@example.com"],
|
|
132
|
+
template_name: "maintenance-notice"
|
|
133
|
+
)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
A successful bulk response confirms that recipients were queued. It is not a guarantee that every message was delivered.
|
|
137
|
+
|
|
138
|
+
## Send Raw HTML
|
|
139
|
+
|
|
140
|
+
Published templates are recommended for reusable product email. For a one-off message, provide both `subject` and `html`:
|
|
141
|
+
|
|
142
|
+
```ruby
|
|
143
|
+
email = client.emails.send(
|
|
144
|
+
to: "user@example.com",
|
|
145
|
+
from_email: "reports@yourdomain.com",
|
|
146
|
+
subject: "Your report is ready",
|
|
147
|
+
html: "<h1>Report ready</h1><p>You can download it now.</p>"
|
|
148
|
+
)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Sender Selection
|
|
152
|
+
|
|
153
|
+
MailEngin resolves the sender in this order:
|
|
154
|
+
|
|
155
|
+
1. `from_email` supplied in the request.
|
|
156
|
+
2. Sender saved in the published Developer Template.
|
|
157
|
+
3. `noreply@<authorized-domain>` fallback.
|
|
158
|
+
|
|
159
|
+
The sender domain must be verified and authorized for the API key.
|
|
160
|
+
|
|
161
|
+
## Error Handling
|
|
162
|
+
|
|
163
|
+
API, timeout, malformed-response, and network failures raise `MailEngin::Error`:
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
begin
|
|
167
|
+
client.emails.send(
|
|
168
|
+
to: "user@example.com",
|
|
169
|
+
template_name: "welcome-email"
|
|
170
|
+
)
|
|
171
|
+
rescue MailEngin::Error => error
|
|
172
|
+
warn error.message
|
|
173
|
+
warn error.status.inspect # HTTP status, when available
|
|
174
|
+
warn error.code.inspect # Machine-readable error code
|
|
175
|
+
warn error.request_id.inspect # Include when contacting support
|
|
176
|
+
warn error.retry_after.inspect # Seconds supplied with HTTP 429
|
|
177
|
+
warn error.body.inspect # Parsed JSON or response text
|
|
178
|
+
warn error.retryable?.to_s
|
|
179
|
+
end
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`retryable?` is true for network errors, timeouts, HTTP `408`, HTTP `429`, and `5xx` responses. The SDK never retries sends automatically because a retry could create a duplicate email until idempotency keys are supported.
|
|
183
|
+
|
|
184
|
+
Invalid local input raises `ArgumentError` before an API request is made.
|
|
185
|
+
|
|
186
|
+
## Configuration
|
|
187
|
+
|
|
188
|
+
```ruby
|
|
189
|
+
client = MailEngin::Client.new(
|
|
190
|
+
api_key: ENV.fetch("MAILENGIN_API_KEY"),
|
|
191
|
+
base_url: "https://api.mailengin.app",
|
|
192
|
+
timeout: 15
|
|
193
|
+
)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
| Keyword | Default | Description |
|
|
197
|
+
| --- | --- | --- |
|
|
198
|
+
| `api_key` | None | Full server-side MailEngin API key. |
|
|
199
|
+
| `base_url` | `https://api.mailengin.app` | Override for local, test, or dedicated environments. |
|
|
200
|
+
| `timeout` | `30` | Open and read timeout in seconds. |
|
|
201
|
+
| `transport` | `MailEngin::Transport` | Injectable object responding to `call`. |
|
|
202
|
+
|
|
203
|
+
Create one client and reuse it throughout the application.
|
|
204
|
+
|
|
205
|
+
## Rails Integration
|
|
206
|
+
|
|
207
|
+
Initialize one client after application configuration is available:
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
# config/initializers/mailengin.rb
|
|
211
|
+
MAILENGIN = MailEngin::Client.new(
|
|
212
|
+
api_key: ENV.fetch("MAILENGIN_API_KEY")
|
|
213
|
+
)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
For larger applications, register the client in your dependency container instead of relying on a global constant. Keep the API key in Rails credentials or the deployment environment, never in source control.
|
|
217
|
+
|
|
218
|
+
## Testing With an Injected Transport
|
|
219
|
+
|
|
220
|
+
Inject a small callable transport to test without a real API key or network request:
|
|
221
|
+
|
|
222
|
+
```ruby
|
|
223
|
+
response = Struct.new(:code, :body) do
|
|
224
|
+
def is_a?(type)
|
|
225
|
+
type == Net::HTTPSuccess || super
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def [](name)
|
|
229
|
+
nil
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
transport = lambda do |uri:, request:, timeout:|
|
|
234
|
+
response.new(
|
|
235
|
+
"200",
|
|
236
|
+
JSON.generate(
|
|
237
|
+
id: "email_123",
|
|
238
|
+
from: "hello@example.com",
|
|
239
|
+
to: "user@example.com",
|
|
240
|
+
template_name: "welcome-email",
|
|
241
|
+
created_at: "2026-08-31T12:00:00Z"
|
|
242
|
+
)
|
|
243
|
+
)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
client = MailEngin::Client.new(api_key: "test_key", transport: transport)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The repository Minitest suite contains complete transport examples for success, errors, and timeouts.
|
|
250
|
+
|
|
251
|
+
## Development
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
bundle install
|
|
255
|
+
bundle exec rake test
|
|
256
|
+
gem build mailengin.gemspec
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for contribution rules and [PUBLISHING.md](./PUBLISHING.md) for maintainer release instructions.
|
|
260
|
+
|
|
261
|
+
## Resources
|
|
262
|
+
|
|
263
|
+
- [MailEngin API documentation](https://mailengin.app/dashboard/docs)
|
|
264
|
+
- [Developer Templates](https://mailengin.app/dashboard/dev-templates)
|
|
265
|
+
- [API keys](https://mailengin.app/dashboard/api-keys)
|
|
266
|
+
- [Security policy](./SECURITY.md)
|
|
267
|
+
- [Changelog](./CHANGELOG.md)
|
|
268
|
+
|
|
269
|
+
## License
|
|
270
|
+
|
|
271
|
+
Released under the [MIT License](./LICENSE). Copyright 2026 MailEngin.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "net/http"
|
|
3
|
+
require "uri"
|
|
4
|
+
require_relative "error"
|
|
5
|
+
require_relative "emails"
|
|
6
|
+
require_relative "transport"
|
|
7
|
+
require_relative "version"
|
|
8
|
+
|
|
9
|
+
module MailEngin
|
|
10
|
+
class Client
|
|
11
|
+
DEFAULT_BASE_URL = "https://api.mailengin.app"
|
|
12
|
+
|
|
13
|
+
attr_reader :emails
|
|
14
|
+
|
|
15
|
+
def initialize(api_key:, base_url: DEFAULT_BASE_URL, timeout: 30, transport: Transport.new)
|
|
16
|
+
raise ArgumentError, "MailEngin requires a non-empty api_key" if api_key.to_s.strip.empty?
|
|
17
|
+
raise ArgumentError, "MailEngin timeout must be positive" unless timeout.positive?
|
|
18
|
+
@api_key = api_key.strip
|
|
19
|
+
@base_url = base_url.sub(%r{/+$}, "")
|
|
20
|
+
@timeout = timeout
|
|
21
|
+
@transport = transport
|
|
22
|
+
@emails = Emails.new(self)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def post(path, body)
|
|
26
|
+
uri = URI(@base_url + path)
|
|
27
|
+
request = Net::HTTP::Post.new(uri)
|
|
28
|
+
request["Authorization"] = "Bearer #{@api_key}"
|
|
29
|
+
request["Accept"] = "application/json"
|
|
30
|
+
request["Content-Type"] = "application/json"
|
|
31
|
+
request["User-Agent"] = "mailengin-ruby/#{VERSION}"
|
|
32
|
+
request.body = JSON.generate(body)
|
|
33
|
+
response = @transport.call(uri: uri, request: request, timeout: @timeout)
|
|
34
|
+
parsed = parse(response.body)
|
|
35
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
36
|
+
message = parsed.is_a?(Hash) && parsed["message"].is_a?(String) ? parsed["message"] : "MailEngin API request failed with status #{response.code}."
|
|
37
|
+
code = parsed.is_a?(Hash) && parsed["code"].is_a?(String) ? parsed["code"] : nil
|
|
38
|
+
retry_after = Float(response["Retry-After"], exception: false)
|
|
39
|
+
raise Error.new(message, status: response.code.to_i, code: code, request_id: response["x-request-id"], retry_after: retry_after, body: parsed)
|
|
40
|
+
end
|
|
41
|
+
raise Error.new("MailEngin API returned invalid JSON.", code: "invalid_response", body: response.body) unless parsed.is_a?(Hash)
|
|
42
|
+
parsed
|
|
43
|
+
rescue Net::OpenTimeout, Net::ReadTimeout => error
|
|
44
|
+
raise Error.new("MailEngin request timed out.", code: "request_timeout", cause: error)
|
|
45
|
+
rescue SocketError, SystemCallError, IOError => error
|
|
46
|
+
raise Error.new("Unable to reach the MailEngin API.", code: "network_error", cause: error)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def parse(raw)
|
|
52
|
+
return nil if raw.nil? || raw.empty?
|
|
53
|
+
JSON.parse(raw)
|
|
54
|
+
rescue JSON::ParserError
|
|
55
|
+
raw
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module MailEngin
|
|
2
|
+
SendEmailResponse = Data.define(:id, :from, :to, :template_name, :created_at)
|
|
3
|
+
SendBulkEmailResponse = Data.define(:success, :job_id, :queued_count, :sent_count, :failed_count, :template_name, :message)
|
|
4
|
+
|
|
5
|
+
class Emails
|
|
6
|
+
def initialize(client)
|
|
7
|
+
@client = client
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def send(to:, template_name: nil, template_id: nil, variables: nil, subject: nil, from_email: nil, html: nil, reply_to_mailengin: nil)
|
|
11
|
+
raise ArgumentError, "mailengin.emails.send requires to" if to.to_s.strip.empty?
|
|
12
|
+
require_content(template_name, template_id, html, subject)
|
|
13
|
+
body = compact(
|
|
14
|
+
to: to, template_name: template_name, template_id: template_id, variables: variables,
|
|
15
|
+
subject: subject, from_email: from_email, html: html, reply_to_mailengin: reply_to_mailengin
|
|
16
|
+
)
|
|
17
|
+
data = @client.post("/api/developer/send", body)
|
|
18
|
+
SendEmailResponse.new(
|
|
19
|
+
id: data.fetch("id"), from: data.fetch("from"), to: data.fetch("to"),
|
|
20
|
+
template_name: data["template_name"], created_at: data.fetch("created_at")
|
|
21
|
+
)
|
|
22
|
+
rescue KeyError, TypeError => error
|
|
23
|
+
raise Error.new("MailEngin API returned an invalid response.", code: "invalid_response", body: data, cause: error)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def send_bulk(to:, template_name: nil, template_id: nil, variables: nil, subject: nil, from_email: nil, html: nil, reply_to_mailengin: nil)
|
|
27
|
+
raise ArgumentError, "mailengin.emails.send_bulk requires recipients" unless to.is_a?(Array) && !to.empty?
|
|
28
|
+
raise ArgumentError, "mailengin.emails.send_bulk accepts up to 1000 recipients" if to.length > 1_000
|
|
29
|
+
recipients = to.map do |recipient|
|
|
30
|
+
if recipient.is_a?(String)
|
|
31
|
+
raise ArgumentError, "every bulk recipient requires an email" if recipient.strip.empty?
|
|
32
|
+
recipient
|
|
33
|
+
else
|
|
34
|
+
email = recipient[:email] || recipient["email"]
|
|
35
|
+
raise ArgumentError, "every bulk recipient requires an email" if email.to_s.strip.empty?
|
|
36
|
+
compact(email: email, variables: recipient[:variables] || recipient["variables"])
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
require_content(template_name, template_id, html, subject)
|
|
40
|
+
data = @client.post("/api/developer/send-bulk", compact(
|
|
41
|
+
to: recipients, template_name: template_name, template_id: template_id, variables: variables,
|
|
42
|
+
subject: subject, from_email: from_email, html: html, reply_to_mailengin: reply_to_mailengin
|
|
43
|
+
))
|
|
44
|
+
SendBulkEmailResponse.new(
|
|
45
|
+
success: data.fetch("success"), job_id: data.fetch("jobId"), queued_count: data.fetch("queued_count"),
|
|
46
|
+
sent_count: data["sent_count"], failed_count: data["failed_count"],
|
|
47
|
+
template_name: data["template_name"], message: data.fetch("message")
|
|
48
|
+
)
|
|
49
|
+
rescue KeyError, TypeError => error
|
|
50
|
+
raise Error.new("MailEngin API returned an invalid response.", code: "invalid_response", body: data, cause: error)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def require_content(template_name, template_id, html, subject)
|
|
56
|
+
has_template = !template_name.to_s.strip.empty? || !template_id.to_s.strip.empty?
|
|
57
|
+
raise ArgumentError, "provide template_name, template_id, or html" if !has_template && html.to_s.strip.empty?
|
|
58
|
+
raise ArgumentError, "raw HTML sends require subject" if !has_template && subject.to_s.strip.empty?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def compact(values)
|
|
62
|
+
values.reject { |_, value| value.nil? }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module MailEngin
|
|
2
|
+
class Error < StandardError
|
|
3
|
+
attr_reader :status, :code, :request_id, :retry_after, :body
|
|
4
|
+
|
|
5
|
+
def initialize(message, status: nil, code: nil, request_id: nil, retry_after: nil, body: nil, cause: nil)
|
|
6
|
+
super(message)
|
|
7
|
+
@status = status
|
|
8
|
+
@code = code
|
|
9
|
+
@request_id = request_id
|
|
10
|
+
@retry_after = retry_after
|
|
11
|
+
@body = body
|
|
12
|
+
set_backtrace(cause.backtrace) if cause
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def retryable?
|
|
16
|
+
%w[network_error request_timeout].include?(code) || [408, 429].include?(status) || (!status.nil? && status >= 500)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require "net/http"
|
|
2
|
+
|
|
3
|
+
module MailEngin
|
|
4
|
+
class Transport
|
|
5
|
+
def call(uri:, request:, timeout:)
|
|
6
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", open_timeout: timeout, read_timeout: timeout) do |http|
|
|
7
|
+
http.request(request)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
data/lib/mailengin.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: mailengin
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- MailEngin
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: Server-side Ruby client for sending transactional email through MailEngin.
|
|
13
|
+
email:
|
|
14
|
+
- support@mailengin.app
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- CHANGELOG.md
|
|
20
|
+
- LICENSE
|
|
21
|
+
- README.md
|
|
22
|
+
- lib/mailengin.rb
|
|
23
|
+
- lib/mailengin/client.rb
|
|
24
|
+
- lib/mailengin/emails.rb
|
|
25
|
+
- lib/mailengin/error.rb
|
|
26
|
+
- lib/mailengin/transport.rb
|
|
27
|
+
- lib/mailengin/version.rb
|
|
28
|
+
homepage: https://mailengin.app
|
|
29
|
+
licenses:
|
|
30
|
+
- MIT
|
|
31
|
+
metadata:
|
|
32
|
+
source_code_uri: https://github.com/mailengin/mailengin-ruby-sdk
|
|
33
|
+
bug_tracker_uri: https://github.com/mailengin/mailengin-ruby-sdk/issues
|
|
34
|
+
documentation_uri: https://mailengin.app/dashboard/docs
|
|
35
|
+
rubygems_mfa_required: 'true'
|
|
36
|
+
rdoc_options: []
|
|
37
|
+
require_paths:
|
|
38
|
+
- lib
|
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
40
|
+
requirements:
|
|
41
|
+
- - ">="
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '3.2'
|
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - ">="
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: '0'
|
|
49
|
+
requirements: []
|
|
50
|
+
rubygems_version: 4.0.16
|
|
51
|
+
specification_version: 4
|
|
52
|
+
summary: Official Ruby SDK for the MailEngin Email API
|
|
53
|
+
test_files: []
|