millionsend 0.6.0 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0d6e46f436a0805ef763edac14bbd254e6f17503c598a8374b6abe9f45efcc76
4
- data.tar.gz: 04c8ab67229c03c8769c7f9ef872b0e11b98d8aa8919a21c0d4c6ce6cc0582f9
3
+ metadata.gz: b7af1c0b498dd1ff772b222aac67dce73ea3c1f4c71a7d2a9d4d3e427ef8a141
4
+ data.tar.gz: 8f019e8f091187618102b125d58bfe0233be20f838cb61059ef5e6ed979d1e09
5
5
  SHA512:
6
- metadata.gz: ea4ff1283155e92f65aea2088a6ae4abdfd5032b3c93c243eb838a30ab141070c328bc7d2a278e0399894ec1e18300abef83a727ace9ed48a4b7e3271941e284
7
- data.tar.gz: e845a9d24731c584b931a0de44fd7f0adee3760bcaa8d45d55412e301c6e9ad545cc1d62e294938e8adbdba07ebc204d156a96584d2d5b16fc721826853137dc
6
+ metadata.gz: b382ae969cd92102bb62a9108d64f7bf39b159c98c660ad03b2726a58573e81dde023ee474c2194e744d5c34775e8a1fb667bf9e64c52058850ddc4c9b19d9e6
7
+ data.tar.gz: 1666f792e81b7eaab5cc93fb314e11a80e90e92dec25a6a0c12ef0c090ddf4d03a48f982d7059f952e254af7878dfc62c6680e28d7a47a199e6989b5fbe9daf3
data/README.md CHANGED
@@ -123,6 +123,9 @@ Millionsend::Contacts.get("ada@acme.dev") # by id or email; also resend-ruby's g
123
123
  Millionsend::Contacts.update(id: contact[:id], unsubscribed: true, first_name: nil) # nil clears
124
124
  Millionsend::Contacts.remove("ada@acme.dev")
125
125
  Millionsend::Contacts.list(limit: 50)
126
+ # Bulk read (MillionSend extension): carry the property map and the topic subscriptions on every
127
+ # item, so an audience reads in one request per 100 contacts instead of one per contact
128
+ Millionsend::Contacts.list(limit: 100, include: ["properties", "topics"]) # ?include=properties,topics
126
129
 
127
130
  # Topic subscriptions (granular unsubscribe) — PATCH /contacts/:id/topics
128
131
  Millionsend::Contacts::Topics.update(email: "ada@acme.dev", topics: [{ id: topic_id, subscription: "opt_out" }]) # resend-ruby shape
@@ -149,6 +152,12 @@ result[:data] # [{ index:, id:, status: "created" | "updated" | "skipped" }]
149
152
  result[:counts] # { created:, updated:, skipped:, failed: }
150
153
  result[:errors] # permissive mode only: [{ index:, message: }]
151
154
 
155
+ # Bulk lookup (MillionSend extension) — up to 1000 contacts by id or email in one request, in
156
+ # request order; unknown entries are listed, not errors — one request against the rate limit
157
+ found = Millionsend::Contacts::Batch.get([contact[:id], "b@acme.dev", { email: "c@acme.dev" }], include: ["topics"])
158
+ found[:data] # [{ object: "contact", id:, email:, first_name:, last_name:, created_at:, unsubscribed:, topics: }]
159
+ found[:missing] # [{ index:, email: }] / [{ index:, id: }] — request entries that matched nobody
160
+
152
161
  # Bulk delete (MillionSend extension) — up to 1000 per call, exactly one of ids: / emails:
153
162
  Millionsend::Contacts::Batch.remove(emails: ["a@acme.dev", "b@acme.dev"]) # or ids: [...]
154
163
  # => { data: [{ object: "contact", contact: "<uuid>", deleted: true }, ...] } — only the rows actually deleted
@@ -307,6 +316,7 @@ segment = Millionsend::Segments.create(
307
316
  Millionsend::Segments.get(segment[:id]) # includes a live contact_count
308
317
  Millionsend::Segments.list
309
318
  Millionsend::Segments.contacts(segment[:id], limit: 50) # the contacts currently matching
319
+ Millionsend::Segments.contacts(segment[:id], include: ["properties", "topics"]) # with each contact's extras, as Contacts.list
310
320
  Millionsend::Segments.update(segment[:id], name: "Pro tier")
311
321
  Millionsend::Segments.remove(segment[:id])
312
322
  ```
@@ -29,11 +29,12 @@ module Millionsend
29
29
  Millionsend::Request.new(method: :delete, path: member_path(id_or_email)).perform
30
30
  end
31
31
 
32
- # GET /contacts — accepts limit:/after:/before:.
32
+ # GET /contacts — accepts limit:/after:/before: and include:
33
+ # (["properties", "topics"]) to carry the property map and the topic
34
+ # subscriptions on every item, as Contacts.get and Topics.list return them.
33
35
  def list(options = {})
34
- Millionsend::Request.new(
35
- method: :get, path: "/contacts", query: Millionsend::Util.list_query(options)
36
- ).perform
36
+ query = Millionsend::Util.list_query(options).merge(include: Millionsend::Util.include_query(options[:include]))
37
+ Millionsend::Request.new(method: :get, path: "/contacts", query: query).perform
37
38
  end
38
39
 
39
40
  # PATCH /contacts/:id_or_email/topics with a bare array of
@@ -53,8 +54,20 @@ module Millionsend
53
54
  # Every member method also accepts resend-ruby's addressing hash
54
55
  # ({ id: } / { email: } / { contact_id: }) in place of the bare value.
55
56
  def member_path(id_or_email)
56
- id_or_email = id_or_email[:email] || id_or_email[:id] || id_or_email[:contact_id] if id_or_email.is_a?(Hash)
57
- "/contacts/#{Millionsend::Util.encode(id_or_email)}"
57
+ "/contacts/#{Millionsend::Util.encode(unwrap(id_or_email))}"
58
+ end
59
+
60
+ # One /contacts/batch/get entry. The wire wants { id: } and { email: }
61
+ # told apart, and only an email carries "@".
62
+ def address(id_or_email)
63
+ value = unwrap(id_or_email)
64
+ value.to_s.include?("@") ? { email: value } : { id: value }
65
+ end
66
+
67
+ private
68
+
69
+ def unwrap(id_or_email)
70
+ id_or_email.is_a?(Hash) ? id_or_email[:email] || id_or_email[:id] || id_or_email[:contact_id] : id_or_email
58
71
  end
59
72
  end
60
73
 
@@ -96,6 +109,17 @@ module Millionsend
96
109
  ).perform
97
110
  end
98
111
 
112
+ # POST /contacts/batch/get — up to 1000 contacts by id or email (bare
113
+ # values or addressing hashes, like Contacts.get) in one request, one
114
+ # call against the rate limit. Returns { data: [...] } in request order
115
+ # plus missing: [{ index:, id: | email: }] for the entries that matched
116
+ # nobody — those never fail the call. include: ["properties", "topics"]
117
+ # attaches the same extras as Contacts.list.
118
+ def get(addresses, options = {})
119
+ body = { contacts: addresses.map { |a| Millionsend::Contacts.address(a) }, include: options[:include] }
120
+ Millionsend::Request.new(method: :post, path: "/contacts/batch/get", body: body.compact).perform
121
+ end
122
+
99
123
  # POST /contacts/batch/remove — { ids: [...] } or { emails: [...] }
100
124
  # (exactly one, up to 1000). Returns { data: [{ object:, contact:,
101
125
  # deleted: true }] } listing only the rows actually deleted; unknown
@@ -23,11 +23,12 @@ module Millionsend
23
23
  end
24
24
 
25
25
  # GET /segments/:id/contacts — the contacts currently matching, paginated
26
- # with limit:/after:/before:.
26
+ # with limit:/after:/before:; include: (["properties", "topics"]) as on
27
+ # Contacts.list.
27
28
  def contacts(id, options = {})
29
+ query = Millionsend::Util.list_query(options).merge(include: Millionsend::Util.include_query(options[:include]))
28
30
  Millionsend::Request.new(
29
- method: :get, path: "/segments/#{Millionsend::Util.encode(id)}/contacts",
30
- query: Millionsend::Util.list_query(options)
31
+ method: :get, path: "/segments/#{Millionsend::Util.encode(id)}/contacts", query: query
31
32
  ).perform
32
33
  end
33
34
 
@@ -33,6 +33,13 @@ module Millionsend
33
33
  { limit: options[:limit], after: options[:after], before: options[:before] }
34
34
  end
35
35
 
36
+ # ?include= for the contact lists: the names comma-joined, or nil (dropped
37
+ # from the query) when none were given.
38
+ def include_query(include)
39
+ names = Array(include)
40
+ names.empty? ? nil : names.join(",")
41
+ end
42
+
36
43
  # Per-request options for the Request constructor. Both call shapes land in
37
44
  # the same trailing positional hash: the original `idempotency_key: "k"` and
38
45
  # resend-ruby's keyword form `options: { idempotency_key: "k" }`, so the
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Millionsend
4
- VERSION = "0.6.0"
4
+ VERSION = "0.7.0"
5
5
  end
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.6.0
4
+ version: 0.7.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-09-04 00:00:00.000000000 Z
11
+ date: 2026-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake