millionsend 0.2.0 → 0.3.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 +20 -1
- data/lib/millionsend/deliverability.rb +14 -0
- data/lib/millionsend/emails.rb +8 -0
- data/lib/millionsend/request.rb +7 -0
- data/lib/millionsend/util.rb +10 -0
- data/lib/millionsend/version.rb +1 -1
- data/lib/millionsend.rb +6 -2
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3a05f5a8a6f73d20210b65a538c84b985206374628dd5298793c01d37bd9138f
|
|
4
|
+
data.tar.gz: 7fe8ff20393c98a38072d70617e0df869aefebdb14a577db320f8514d17eca05
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ec4a59041ec45a8c620b9fa2ff618971273b0dc6ba43ba75e758cec70653978e9a428249acd52c060b79ab83cdc70b5f119b019b2407b2ce802f2407444cbee9
|
|
7
|
+
data.tar.gz: 2fc47253ed0511c373c8273f84457ad90f812db1d4d6d9df8c4d58019780e34e484681239679033f9b9d0ca435cebac24c19f23f5438c26cd92c2d388bf6be85
|
data/README.md
CHANGED
|
@@ -48,10 +48,15 @@ on any non-2xx response (see [Error handling](#error-handling)).
|
|
|
48
48
|
Millionsend.api_key = "ms_123" # falls back to ENV["MILLIONSEND_API_KEY"]
|
|
49
49
|
Millionsend.base_url = "https://mail.acme.dev" # falls back to ENV["MILLIONSEND_BASE_URL"],
|
|
50
50
|
# then http://localhost:3001
|
|
51
|
+
Millionsend.allow_insecure_http = false # accept a non-loopback http:// base_url
|
|
51
52
|
```
|
|
52
53
|
|
|
53
54
|
MillionSend is self-hosted, so there is no cloud default — **set `base_url` to your
|
|
54
55
|
deployment in production.** An explicitly assigned value always wins over the environment.
|
|
56
|
+
Plain `http://` is only accepted for loopback hosts (`localhost`, `127.0.0.1`, `::1`); any
|
|
57
|
+
other `http://` URL raises `Millionsend::ApplicationError` on the first call, since the API
|
|
58
|
+
key is sent as a bearer header. Set `allow_insecure_http = true` to talk to a non-TLS
|
|
59
|
+
instance elsewhere (e.g. inside a private network).
|
|
55
60
|
Params are symbol-keyed hashes and map straight to the wire (Ruby's snake_case is already
|
|
56
61
|
the wire's snake_case: `reply_to`, `scheduled_at`, `segment_id`).
|
|
57
62
|
|
|
@@ -61,7 +66,8 @@ the wire's snake_case: `reply_to`, `scheduled_at`, `segment_id`).
|
|
|
61
66
|
|
|
62
67
|
```ruby
|
|
63
68
|
Millionsend::Emails.send(payload, idempotency_key: "order-42") # POST /emails
|
|
64
|
-
Millionsend::Emails.get(id) # GET /emails/:id
|
|
69
|
+
Millionsend::Emails.get(id) # GET /emails/:id (includes score: 0-10 or nil)
|
|
70
|
+
Millionsend::Emails.get_insights(id) # GET /emails/:id/insights (404 until computed)
|
|
65
71
|
Millionsend::Emails.cancel(id) # POST /emails/:id/cancel (scheduled only)
|
|
66
72
|
|
|
67
73
|
Millionsend::Batch.send([payload_a, payload_b], idempotency_key: "run-7") # up to 100
|
|
@@ -132,6 +138,19 @@ Millionsend::Segments.update(segment[:id], name: "Pro tier")
|
|
|
132
138
|
Millionsend::Segments.remove(segment[:id])
|
|
133
139
|
```
|
|
134
140
|
|
|
141
|
+
### Deliverability (MillionSend extension)
|
|
142
|
+
|
|
143
|
+
Per-email best-practice insights and the account-level deliverability score.
|
|
144
|
+
|
|
145
|
+
```ruby
|
|
146
|
+
insights = Millionsend::Emails.get_insights(email[:id]) # score, band, checks: [{id:, severity:, status:, penalty:, detail:}]
|
|
147
|
+
account = Millionsend::Deliverability.get # GET /deliverability — trailing-30-day account score
|
|
148
|
+
puts account[:score] # 0-10 (one decimal) or nil when there is not enough data
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Check ids and the band/severity/status values are an open set that grows across score
|
|
152
|
+
versions — treat them as strings, not a closed enum.
|
|
153
|
+
|
|
135
154
|
## Error handling
|
|
136
155
|
|
|
137
156
|
No `{ data, error }` tuple — a non-2xx response raises. The base class is `Millionsend::Error`,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Millionsend
|
|
4
|
+
# Account-level deliverability score over the trailing window
|
|
5
|
+
# (scores are 0-10, one decimal; null means not enough data).
|
|
6
|
+
module Deliverability
|
|
7
|
+
class << self
|
|
8
|
+
# GET /deliverability
|
|
9
|
+
def get
|
|
10
|
+
Millionsend::Request.new(method: :get, path: "/deliverability").perform
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
data/lib/millionsend/emails.rb
CHANGED
|
@@ -20,6 +20,14 @@ module Millionsend
|
|
|
20
20
|
Millionsend::Request.new(method: :get, path: "/emails/#{Millionsend::Util.encode(id)}").perform
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
# GET /emails/:id/insights — the pre-send best-practice report computed
|
|
24
|
+
# when the email was sent. 404 when the email is unknown or has no
|
|
25
|
+
# insights yet. Check ids and band/severity/status values are an open
|
|
26
|
+
# set that grows across score versions; they arrive as plain strings.
|
|
27
|
+
def get_insights(id)
|
|
28
|
+
Millionsend::Request.new(method: :get, path: "/emails/#{Millionsend::Util.encode(id)}/insights").perform
|
|
29
|
+
end
|
|
30
|
+
|
|
23
31
|
# POST /emails/:id/cancel — scheduled, unsent emails only.
|
|
24
32
|
def cancel(id)
|
|
25
33
|
Millionsend::Request.new(method: :post, path: "/emails/#{Millionsend::Util.encode(id)}/cancel").perform
|
data/lib/millionsend/request.rb
CHANGED
|
@@ -65,6 +65,13 @@ module Millionsend
|
|
|
65
65
|
def build_uri
|
|
66
66
|
base = Millionsend.base_url.to_s.sub(%r{/+\z}, "")
|
|
67
67
|
uri = URI.parse("#{base}#{@path}")
|
|
68
|
+
if !Millionsend.allow_insecure_http && Millionsend::Util.insecure_http?(uri)
|
|
69
|
+
raise Millionsend::ApplicationError.new(
|
|
70
|
+
"Refusing to send the API key over plain http to #{base}. " \
|
|
71
|
+
"Use https, or set Millionsend.allow_insecure_http = true.",
|
|
72
|
+
nil, "application_error"
|
|
73
|
+
)
|
|
74
|
+
end
|
|
68
75
|
query = (@query || {}).reject { |_, v| v.nil? }
|
|
69
76
|
uri.query = URI.encode_www_form(query) unless query.empty?
|
|
70
77
|
uri
|
data/lib/millionsend/util.rb
CHANGED
|
@@ -16,6 +16,16 @@ module Millionsend
|
|
|
16
16
|
URI.encode_www_form_component(value.to_s).gsub("+", "%20")
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
LOOPBACK_HOSTS = %w[localhost 127.0.0.1 ::1].freeze
|
|
20
|
+
|
|
21
|
+
# True for an http:// URI whose host is not loopback.
|
|
22
|
+
def insecure_http?(uri)
|
|
23
|
+
return false unless uri.scheme == "http"
|
|
24
|
+
|
|
25
|
+
host = uri.host.to_s.downcase
|
|
26
|
+
!LOOPBACK_HOSTS.include?(host) && !host.start_with?("127.")
|
|
27
|
+
end
|
|
28
|
+
|
|
19
29
|
# The keyset pagination params every list endpoint accepts. nil values are
|
|
20
30
|
# dropped when the query string is built.
|
|
21
31
|
def list_query(options)
|
data/lib/millionsend/version.rb
CHANGED
data/lib/millionsend.rb
CHANGED
|
@@ -10,6 +10,7 @@ require "millionsend/contacts"
|
|
|
10
10
|
require "millionsend/topics"
|
|
11
11
|
require "millionsend/broadcasts"
|
|
12
12
|
require "millionsend/segments"
|
|
13
|
+
require "millionsend/deliverability"
|
|
13
14
|
|
|
14
15
|
# Ruby client for the MillionSend HTTP API. Configure once, then call the
|
|
15
16
|
# resource modules:
|
|
@@ -21,14 +22,17 @@ require "millionsend/segments"
|
|
|
21
22
|
#
|
|
22
23
|
# api_key falls back to the MILLIONSEND_API_KEY env var; base_url to
|
|
23
24
|
# MILLIONSEND_BASE_URL and then http://localhost:3001 (MillionSend is
|
|
24
|
-
# self-hosted, so there is no cloud default).
|
|
25
|
-
#
|
|
25
|
+
# self-hosted, so there is no cloud default). Plain http is only accepted for
|
|
26
|
+
# loopback hosts unless allow_insecure_http is set, since the API key travels
|
|
27
|
+
# as a bearer header. Every call returns a symbol-keyed Hash on success and
|
|
28
|
+
# raises a Millionsend::Error on any non-2xx response.
|
|
26
29
|
module Millionsend
|
|
27
30
|
DEFAULT_BASE_URL = "http://localhost:3001"
|
|
28
31
|
USER_AGENT = "millionsend-ruby/#{VERSION}"
|
|
29
32
|
|
|
30
33
|
class << self
|
|
31
34
|
attr_writer :api_key, :base_url
|
|
35
|
+
attr_accessor :allow_insecure_http
|
|
32
36
|
|
|
33
37
|
def api_key
|
|
34
38
|
@api_key || ENV["MILLIONSEND_API_KEY"]
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: millionsend
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- MillionSend
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-31 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|
|
@@ -66,6 +66,7 @@ files:
|
|
|
66
66
|
- lib/millionsend/batch.rb
|
|
67
67
|
- lib/millionsend/broadcasts.rb
|
|
68
68
|
- lib/millionsend/contacts.rb
|
|
69
|
+
- lib/millionsend/deliverability.rb
|
|
69
70
|
- lib/millionsend/emails.rb
|
|
70
71
|
- lib/millionsend/error.rb
|
|
71
72
|
- lib/millionsend/request.rb
|