Class: Equipoise::Contacts

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

Constant Summary collapse

BATCH_LIMIT =
500

Instance Method Summary collapse

Constructor Details

#initialize(client) ⇒ Contacts

Returns a new instance of Contacts.



25
26
27
# File 'lib/equipoise/contacts.rb', line 25

def initialize(client)
  @client = client
end

Instance Method Details

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

— Claude authored — upsert many contacts. Chunks to ≤500/call and returns a BatchResult aggregating every per-item result across chunks. 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.



47
48
49
50
51
52
53
54
55
# File 'lib/equipoise/contacts.rb', line 47

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

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

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

— Claude authored — deactivate the external link; the Contact is retained.



58
59
60
61
# File 'lib/equipoise/contacts.rb', line 58

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

— Claude authored — read back the Contact linked to (source, external_id).



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

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

— Claude authored — 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.



73
74
75
76
77
78
# File 'lib/equipoise/contacts.rb', line 73

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.



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

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

— Claude authored — 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.



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

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



85
86
87
# File 'lib/equipoise/contacts.rb', line 85

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