Class: Equipoise::Contacts

Inherits:
Object
  • Object
show all
Defined in:
lib/equipoise/contacts.rb

Constant Summary collapse

BATCH_LIMIT =

The receiver's hard per-batch cap. Kept for back-compat; the effective chunk size now comes from config.batch_size (clamped to this).

Configuration::BATCH_SIZE_BOUNDS.max

Instance Method Summary collapse

Constructor Details

#initialize(client) ⇒ Contacts

500



27
28
29
# File 'lib/equipoise/contacts.rb', line 27

def initialize(client)
  @client = client
end

Instance Method Details

#batch_sync(records, source: nil, chunk_size: nil) ⇒ Object

Upsert many contacts. Chunks to the configured batch_size (default 500, hard-capped at the receiver's per-batch limit) and returns a BatchResult aggregating every per-item result across chunks. Pass chunk_size: to override per call — it is clamped to the same hard cap, so a caller can never post an oversized (and rejected) batch. It NEVER raises mid-batch: a per-item server rejection comes back as an ok: false item (the receiver returns 200 per chunk), and a chunk-level transport/HTTP failure marks that chunk's records failed so earlier chunks' successes are preserved and the producer can retry only the failures.



52
53
54
55
56
57
58
59
60
61
62
# File 'lib/equipoise/contacts.rb', line 52

def batch_sync(records, source: nil, chunk_size: nil)
  config         = @client.config
  default_source = source || config.source
  prepared       = records.map { |record| prepare_record(record, default_source) }
  ensure_sources!(prepared)

  slice_size = config.bounded_batch_size(chunk_size)
  items      = []
  prepared.each_slice(slice_size) { |chunk| items.concat(sync_chunk(chunk)) }
  BatchResult.new(items)
end

#deactivate(external_id:, source: nil) ⇒ Object

Deactivate the external link; the Contact is retained.



65
66
67
68
# File 'lib/equipoise/contacts.rb', line 65

def deactivate(external_id:, source: nil)
  @client.request(:post, "#{sync_path(external_id)}/deactivate",
                  query: { source: resolve_source(source) })
end

#find(external_id:, source: nil) ⇒ Object

Read back the Contact linked to (source, external_id).



71
72
73
74
# File 'lib/equipoise/contacts.rb', line 71

def find(external_id:, source: nil)
  @client.request(:get, sync_path(external_id),
                  query: { source: resolve_source(source) })
end

#record_activity(external_id:, event_type:, source: nil, label: nil, occurred_at: nil, metadata: nil) ⇒ Object

Record an external event on the contact's timeline. event_type: "subscribed"/"unsubscribed" toggles the contact's email subscription (state + activity); any other event_type logs a generic timeline event. metadata is a free-form hash of producer context.



80
81
82
83
84
85
# File 'lib/equipoise/contacts.rb', line 80

def record_activity(external_id:, event_type:, source: nil, label: nil, occurred_at: nil, metadata: nil)
  body = { event_type: event_type, label: label, occurred_at: occurred_at, metadata:  }.compact
  @client.request(:post, "#{sync_path(external_id)}/activity",
                  query: { source: resolve_source(source) },
                  body:  body)
end

#subscribe(external_id:, source: nil) ⇒ Object

Convenience wrappers for the two subscription events.



88
89
90
# File 'lib/equipoise/contacts.rb', line 88

def subscribe(external_id:, source: nil)
  record_activity(external_id: external_id, event_type: "subscribed", source: source)
end

#sync(external_id:, source: nil, idempotency_key: nil, **attributes) ⇒ Object

Upsert one contact by (source, external_id). Unknown attributes (email/name/first_name/last_name/phone/tags/custom_fields/ create_missing_fields) pass straight through; nils are dropped.



34
35
36
37
38
39
40
41
# File 'lib/equipoise/contacts.rb', line 34

def sync(external_id:, source: nil, idempotency_key: nil, **attributes)
  payload = { source: resolve_source(source), external_id: external_id.to_s }
            .merge(attributes)
            .compact

  headers = idempotency_key ? { "Idempotency-Key" => idempotency_key } : {}
  @client.request(:post, "/contacts/sync", body: payload, headers: headers)
end

#unsubscribe(external_id:, source: nil) ⇒ Object



92
93
94
# File 'lib/equipoise/contacts.rb', line 92

def unsubscribe(external_id:, source: nil)
  record_activity(external_id: external_id, event_type: "unsubscribed", source: source)
end