equipoise 0.1.0 → 0.2.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/CHANGELOG.md +21 -1
- data/README.md +24 -6
- data/lib/equipoise/client.rb +2 -2
- data/lib/equipoise/configuration.rb +69 -7
- data/lib/equipoise/contacts.rb +18 -11
- data/lib/equipoise/custom_fields.rb +1 -1
- data/lib/equipoise/errors.rb +2 -2
- data/lib/equipoise/syncable.rb +1 -1
- data/lib/equipoise/util.rb +1 -1
- data/lib/equipoise/version.rb +2 -3
- data/lib/equipoise.rb +5 -6
- data/lib/generators/equipoise/templates/equipoise.rb +10 -2
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0452669965e793163b574be7bec3312e057e7e93d959fd8fdc252adc53027d11'
|
|
4
|
+
data.tar.gz: 2da982746e503901ec54b261fb7845885fcc722c46ae03778388220632a68fc4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 300d97710b2ccde31122a986ecc53fb9f76c64c7040a0bd5e5199011cf383e73b27b5ada8f88e67720fde588af95715fa0d2cd71a535fb48869950809e04a723
|
|
7
|
+
data.tar.gz: 8b9a7a469777ab78cb8f0785d683643d72c24ee7951f013d6d003277593c6de5e36efede76bde0fddf6cb1f6bc09059352c623b87a078e796773443b32bbe286
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,26 @@ semantic versioning once published.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.2.0] - 2026-07-06
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Tunable transport limits with hard guardrails** (from AMP backfill feedback).
|
|
14
|
+
`read_timeout`, `max_retries`, and the new `batch_size` are configurable in the
|
|
15
|
+
`Equipoise.configure` block, but every value is clamped to a hard range so a
|
|
16
|
+
producer's config can never make the gem a bad citizen to the receiver:
|
|
17
|
+
- `read_timeout` → default raised to `30`s (headroom so a full-size batch
|
|
18
|
+
doesn't time out and get re-sent) and clamped to `5..120`s (a too-low timeout
|
|
19
|
+
is how one slow batch becomes two concurrent deliveries).
|
|
20
|
+
- `max_retries` → clamped to `0..5` (no retry storms).
|
|
21
|
+
- `batch_size` → clamped to `1..500` (the receiver's per-batch cap); drives the
|
|
22
|
+
default `contacts.batch_sync` chunk size, and an explicit `chunk_size:` is
|
|
23
|
+
clamped to the same ceiling.
|
|
24
|
+
Out-of-range values are clamped (and logged when a `logger` is set), never
|
|
25
|
+
raised, so a stray setting degrades to "safe" instead of failing boot.
|
|
26
|
+
|
|
27
|
+
## [0.1.0] - 2026-07-05
|
|
28
|
+
|
|
9
29
|
### Added
|
|
10
30
|
|
|
11
31
|
- **Activity / event ingestion** — `contacts.record_activity(external_id:,
|
|
@@ -24,7 +44,7 @@ semantic versioning once published.
|
|
|
24
44
|
- **`Equipoise.disable!` / `enable!` / `disabled?`** test mode — when disabled,
|
|
25
45
|
the client short-circuits to a no-op before any network call or validation,
|
|
26
46
|
so a stray `EQUIPOISE_API_KEY` in a test env can't make a live call.
|
|
27
|
-
- Initial producer client
|
|
47
|
+
- Initial producer client:
|
|
28
48
|
- `Equipoise.configure` / `Equipoise.client` with env-based, rotation-aware
|
|
29
49
|
credentials.
|
|
30
50
|
- `client.contacts.sync` / `batch_sync` (chunks to 500, deterministic per-chunk
|
data/README.md
CHANGED
|
@@ -9,17 +9,17 @@ tag reconciliation, custom-field coercion) lives server-side in the receiver's
|
|
|
9
9
|
`Crm::ContactSyncService`. This client just handles auth, JSON, idempotency,
|
|
10
10
|
bounded retry/backoff, and typed errors.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
The API contract (OpenAPI 3.1) and integration guide are documented with your
|
|
13
|
+
Equipoise account at [app.equipoi.se](https://app.equipoi.se).
|
|
14
14
|
|
|
15
|
-
> Pre-1.0
|
|
16
|
-
>
|
|
15
|
+
> Pre-1.0: the client is stable and published to RubyGems, but the server API
|
|
16
|
+
> surface may still change before 1.0.
|
|
17
17
|
|
|
18
18
|
## Install
|
|
19
19
|
|
|
20
20
|
```ruby
|
|
21
21
|
# In the producer app's Gemfile
|
|
22
|
-
gem "equipoise"
|
|
22
|
+
gem "equipoise"
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## Configure
|
|
@@ -43,6 +43,24 @@ non-loopback host is refused, so the key is never sent in cleartext).
|
|
|
43
43
|
Under Rails, run `rails g equipoise:install` to generate
|
|
44
44
|
`config/initializers/equipoise.rb`.
|
|
45
45
|
|
|
46
|
+
### Tuning (optional)
|
|
47
|
+
|
|
48
|
+
Three transport knobs are configurable and **clamped** to safe ranges, so a
|
|
49
|
+
producer's config can never make the client a bad citizen to the receiver — an
|
|
50
|
+
out-of-range value is clamped (and logged if a `logger` is set), never raised:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
Equipoise.configure do |c|
|
|
54
|
+
c.read_timeout = 30 # seconds per request; clamped to 5..120
|
|
55
|
+
c.max_retries = 2 # transient-failure retries; clamped to 0..5
|
|
56
|
+
c.batch_size = 500 # contacts per batch_sync request; clamped to 1..500
|
|
57
|
+
end
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
For a large `batch_sync` backfill, keep `read_timeout` comfortably above how long
|
|
61
|
+
one full batch takes server-side (or lower `batch_size`) so a slow batch never
|
|
62
|
+
times out and gets re-sent.
|
|
63
|
+
|
|
46
64
|
## Use
|
|
47
65
|
|
|
48
66
|
```ruby
|
|
@@ -57,7 +75,7 @@ Equipoise.client.contacts.sync(
|
|
|
57
75
|
external_id: user.id, email: user.email, idempotency_key: request_id
|
|
58
76
|
)
|
|
59
77
|
|
|
60
|
-
# Batch backfill — chunks to
|
|
78
|
+
# Batch backfill — chunks to batch_size (default 500), one bad item never fails it.
|
|
61
79
|
# It never raises mid-batch: per-item rejections and chunk-level transport
|
|
62
80
|
# failures both come back as failed items, so earlier successes are preserved.
|
|
63
81
|
result = Equipoise.client.contacts.batch_sync(
|
data/lib/equipoise/client.rb
CHANGED
|
@@ -52,10 +52,10 @@ module Equipoise
|
|
|
52
52
|
@custom_fields ||= CustomFields.new(self)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
#
|
|
55
|
+
# The single request chokepoint. Returns a Response on
|
|
56
56
|
# 2xx; raises a typed ApiError (or ConnectionError) otherwise.
|
|
57
57
|
def request(method, path, body: nil, query: nil, headers: {})
|
|
58
|
-
return null_response if config.disabled? # test-mode short-circuit
|
|
58
|
+
return null_response if config.disabled? # test-mode short-circuit — before validate! or any I/O
|
|
59
59
|
|
|
60
60
|
config.validate!
|
|
61
61
|
uri = build_uri(path, query)
|
|
@@ -18,6 +18,12 @@
|
|
|
18
18
|
# the wire). Local dev (`http://localhost`) is allowed; set `allow_insecure_http`
|
|
19
19
|
# to permit a trusted TLS-terminating internal endpoint.
|
|
20
20
|
#
|
|
21
|
+
# Transport knobs (read_timeout, max_retries, batch_size) are producer-tunable in
|
|
22
|
+
# the configure block but CLAMPED to hard ranges (see *_BOUNDS) so a misconfig
|
|
23
|
+
# can never make the gem a bad citizen to the receiver — an oversized batch, a
|
|
24
|
+
# retry storm, or a too-eager give-up-and-retry. Out-of-range values are clamped
|
|
25
|
+
# (and logged if a logger is set), never raised.
|
|
26
|
+
#
|
|
21
27
|
# Used by: Equipoise.configure, Equipoise::Client
|
|
22
28
|
# =============================================================================
|
|
23
29
|
|
|
@@ -25,20 +31,38 @@ module Equipoise
|
|
|
25
31
|
class Configuration
|
|
26
32
|
DEFAULT_BASE_URL = "https://app.equipoi.se/api/v1"
|
|
27
33
|
DEFAULT_OPEN_TIMEOUT = 5
|
|
28
|
-
DEFAULT_READ_TIMEOUT =
|
|
34
|
+
DEFAULT_READ_TIMEOUT = 30
|
|
29
35
|
DEFAULT_MAX_RETRIES = 2
|
|
36
|
+
DEFAULT_BATCH_SIZE = 500
|
|
37
|
+
|
|
38
|
+
# Hard limits — every producer-tunable transport knob is clamped into these
|
|
39
|
+
# ranges so a misconfigured producer can never punish the receiver:
|
|
40
|
+
# read_timeout — floor keeps the producer patient enough that one slow
|
|
41
|
+
# batch doesn't become two concurrent deliveries (a low timeout is how a
|
|
42
|
+
# retry double-submits); ceiling stops a producer worker hanging forever.
|
|
43
|
+
# max_retries — ceiling means a misconfig can't retry-storm the receiver.
|
|
44
|
+
# batch_size — ceiling is the receiver's own per-batch cap (it rejects
|
|
45
|
+
# anything larger); floor of 1 keeps slicing sane.
|
|
46
|
+
# Out-of-range values are clamped, not raised (see #clamp_to).
|
|
47
|
+
READ_TIMEOUT_BOUNDS = (5..120).freeze
|
|
48
|
+
MAX_RETRIES_BOUNDS = (0..5).freeze
|
|
49
|
+
BATCH_SIZE_BOUNDS = (1..500).freeze
|
|
50
|
+
|
|
30
51
|
LOOPBACK_HOSTS = %w[localhost 127.0.0.1 ::1 [::1]].freeze
|
|
31
52
|
PRODUCTION_HOST = URI.parse(DEFAULT_BASE_URL).host # "app.equipoi.se"
|
|
32
53
|
|
|
33
54
|
attr_accessor :open_timeout,
|
|
34
|
-
:read_timeout,
|
|
35
|
-
:max_retries,
|
|
36
55
|
:retry_backoff_base,
|
|
37
56
|
:max_retry_backoff,
|
|
38
57
|
:allow_insecure_http,
|
|
39
58
|
:enabled,
|
|
40
59
|
:logger,
|
|
41
60
|
:user_agent
|
|
61
|
+
# Clamped knobs — custom writers (below) hold every override inside its
|
|
62
|
+
# *_BOUNDS so the gem stays a good citizen no matter what a producer sets.
|
|
63
|
+
attr_reader :read_timeout,
|
|
64
|
+
:max_retries,
|
|
65
|
+
:batch_size
|
|
42
66
|
attr_writer :api_key,
|
|
43
67
|
:base_url,
|
|
44
68
|
:source
|
|
@@ -51,6 +75,7 @@ module Equipoise
|
|
|
51
75
|
@open_timeout = DEFAULT_OPEN_TIMEOUT
|
|
52
76
|
@read_timeout = DEFAULT_READ_TIMEOUT
|
|
53
77
|
@max_retries = DEFAULT_MAX_RETRIES
|
|
78
|
+
@batch_size = DEFAULT_BATCH_SIZE
|
|
54
79
|
@retry_backoff_base = 0.5
|
|
55
80
|
@max_retry_backoff = 30
|
|
56
81
|
@allow_insecure_http = false
|
|
@@ -59,14 +84,14 @@ module Equipoise
|
|
|
59
84
|
@user_agent = "equipoise-ruby/#{Equipoise::VERSION}"
|
|
60
85
|
end
|
|
61
86
|
|
|
62
|
-
#
|
|
87
|
+
# Test-mode kill switch. When disabled the client
|
|
63
88
|
# short-circuits to a no-op before any network call OR validation, so a stray
|
|
64
89
|
# EQUIPOISE_API_KEY in a producer's test env can never make a live call.
|
|
65
90
|
def disabled?
|
|
66
91
|
!enabled
|
|
67
92
|
end
|
|
68
93
|
|
|
69
|
-
#
|
|
94
|
+
# Explicit assignment wins; otherwise read ENV each call
|
|
70
95
|
# (rotation-aware).
|
|
71
96
|
def api_key
|
|
72
97
|
resolve(@api_key, "EQUIPOISE_API_KEY")
|
|
@@ -80,6 +105,32 @@ module Equipoise
|
|
|
80
105
|
resolve(@source, "EQUIPOISE_SOURCE")
|
|
81
106
|
end
|
|
82
107
|
|
|
108
|
+
# Clamped transport knobs. Each override is bounded to a
|
|
109
|
+
# hard range (see *_BOUNDS) so a producer's config can never make the gem a
|
|
110
|
+
# bad citizen to the receiver. An out-of-range value is clamped (and logged,
|
|
111
|
+
# if a logger is set) rather than raised, so a stray setting degrades to
|
|
112
|
+
# "safe" instead of crashing the producer's boot. read_timeout takes
|
|
113
|
+
# fractional seconds; the count knobs are whole numbers.
|
|
114
|
+
def read_timeout=(seconds)
|
|
115
|
+
@read_timeout = clamp_to(:read_timeout, seconds.to_f, READ_TIMEOUT_BOUNDS)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def max_retries=(count)
|
|
119
|
+
@max_retries = clamp_to(:max_retries, count.to_i, MAX_RETRIES_BOUNDS)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def batch_size=(size)
|
|
123
|
+
@batch_size = clamp_to(:batch_size, size.to_i, BATCH_SIZE_BOUNDS)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# The effective per-request chunk size: an explicit
|
|
127
|
+
# `batch_sync(chunk_size:)` wins, else the configured batch_size — but either
|
|
128
|
+
# way clamped to the receiver's hard cap so the gem never posts an oversized
|
|
129
|
+
# (and rejected) batch.
|
|
130
|
+
def bounded_batch_size(requested = nil)
|
|
131
|
+
clamp_to(:batch_size, (requested || batch_size).to_i, BATCH_SIZE_BOUNDS)
|
|
132
|
+
end
|
|
133
|
+
|
|
83
134
|
def validate!
|
|
84
135
|
raise ConfigurationError, "api_key is required (set EQUIPOISE_API_KEY or Equipoise.configure)" if api_key.to_s.empty?
|
|
85
136
|
raise ConfigurationError, "base_url is required" if base_url.to_s.empty?
|
|
@@ -91,6 +142,17 @@ module Equipoise
|
|
|
91
142
|
|
|
92
143
|
private
|
|
93
144
|
|
|
145
|
+
# Clamp a knob into its hard range; warn (never raise) so
|
|
146
|
+
# a misconfigured value is corrected in place instead of failing the
|
|
147
|
+
# producer's boot. A logger is optional, so the warning is best-effort.
|
|
148
|
+
def clamp_to(name, value, range)
|
|
149
|
+
bounded = value.clamp(range.min, range.max)
|
|
150
|
+
if bounded != value
|
|
151
|
+
logger&.warn("[equipoise] #{name}=#{value} is outside #{range.min}..#{range.max}; clamped to #{bounded}")
|
|
152
|
+
end
|
|
153
|
+
bounded
|
|
154
|
+
end
|
|
155
|
+
|
|
94
156
|
def resolve(explicit, env_key)
|
|
95
157
|
return explicit unless explicit.to_s.empty?
|
|
96
158
|
|
|
@@ -98,7 +160,7 @@ module Equipoise
|
|
|
98
160
|
value.to_s.empty? ? nil : value
|
|
99
161
|
end
|
|
100
162
|
|
|
101
|
-
#
|
|
163
|
+
# Never transmit the Bearer key in cleartext to a remote
|
|
102
164
|
# host. https is always fine; http is allowed only for loopback dev or when
|
|
103
165
|
# the producer explicitly opts in for a trusted internal endpoint.
|
|
104
166
|
def ensure_secure_transport!
|
|
@@ -112,7 +174,7 @@ module Equipoise
|
|
|
112
174
|
"Use https://, or set config.allow_insecure_http = true for a trusted internal endpoint."
|
|
113
175
|
end
|
|
114
176
|
|
|
115
|
-
#
|
|
177
|
+
# The key prefix encodes its environment (eq_test_ /
|
|
116
178
|
# eq_live_); refuse the dangerous host mismatches no producer can see coming:
|
|
117
179
|
# a TEST key pointed at the production host (test creds writing the real CRM)
|
|
118
180
|
# or a LIVE key pointed at localhost (prod creds in a dev loop). Staging and
|
data/lib/equipoise/contacts.rb
CHANGED
|
@@ -20,13 +20,15 @@
|
|
|
20
20
|
|
|
21
21
|
module Equipoise
|
|
22
22
|
class Contacts
|
|
23
|
-
|
|
23
|
+
# The receiver's hard per-batch cap. Kept for back-compat; the effective
|
|
24
|
+
# chunk size now comes from config.batch_size (clamped to this).
|
|
25
|
+
BATCH_LIMIT = Configuration::BATCH_SIZE_BOUNDS.max # 500
|
|
24
26
|
|
|
25
27
|
def initialize(client)
|
|
26
28
|
@client = client
|
|
27
29
|
end
|
|
28
30
|
|
|
29
|
-
#
|
|
31
|
+
# Upsert one contact by (source, external_id). Unknown
|
|
30
32
|
# attributes (email/name/first_name/last_name/phone/tags/custom_fields/
|
|
31
33
|
# create_missing_fields) pass straight through; nils are dropped.
|
|
32
34
|
def sync(external_id:, source: nil, idempotency_key: nil, **attributes)
|
|
@@ -38,35 +40,40 @@ module Equipoise
|
|
|
38
40
|
@client.request(:post, "/contacts/sync", body: payload, headers: headers)
|
|
39
41
|
end
|
|
40
42
|
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
+
# Upsert many contacts. Chunks to the configured
|
|
44
|
+
# batch_size (default 500, hard-capped at the receiver's per-batch limit) and
|
|
45
|
+
# returns a BatchResult aggregating every per-item result across chunks. Pass
|
|
46
|
+
# chunk_size: to override per call — it is clamped to the same hard cap, so a
|
|
47
|
+
# caller can never post an oversized (and rejected) batch. It NEVER raises
|
|
43
48
|
# mid-batch: a per-item server rejection comes back as an `ok: false` item
|
|
44
49
|
# (the receiver returns 200 per chunk), and a chunk-level transport/HTTP
|
|
45
50
|
# failure marks that chunk's records failed so earlier chunks' successes are
|
|
46
51
|
# preserved and the producer can retry only the failures.
|
|
47
|
-
def batch_sync(records, source: nil, chunk_size:
|
|
48
|
-
|
|
52
|
+
def batch_sync(records, source: nil, chunk_size: nil)
|
|
53
|
+
config = @client.config
|
|
54
|
+
default_source = source || config.source
|
|
49
55
|
prepared = records.map { |record| prepare_record(record, default_source) }
|
|
50
56
|
ensure_sources!(prepared)
|
|
51
57
|
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
slice_size = config.bounded_batch_size(chunk_size)
|
|
59
|
+
items = []
|
|
60
|
+
prepared.each_slice(slice_size) { |chunk| items.concat(sync_chunk(chunk)) }
|
|
54
61
|
BatchResult.new(items)
|
|
55
62
|
end
|
|
56
63
|
|
|
57
|
-
#
|
|
64
|
+
# Deactivate the external link; the Contact is retained.
|
|
58
65
|
def deactivate(external_id:, source: nil)
|
|
59
66
|
@client.request(:post, "#{sync_path(external_id)}/deactivate",
|
|
60
67
|
query: { source: resolve_source(source) })
|
|
61
68
|
end
|
|
62
69
|
|
|
63
|
-
#
|
|
70
|
+
# Read back the Contact linked to (source, external_id).
|
|
64
71
|
def find(external_id:, source: nil)
|
|
65
72
|
@client.request(:get, sync_path(external_id),
|
|
66
73
|
query: { source: resolve_source(source) })
|
|
67
74
|
end
|
|
68
75
|
|
|
69
|
-
#
|
|
76
|
+
# Record an external event on the contact's timeline.
|
|
70
77
|
# `event_type: "subscribed"`/`"unsubscribed"` toggles the contact's email
|
|
71
78
|
# subscription (state + activity); any other event_type logs a generic
|
|
72
79
|
# timeline event. `metadata` is a free-form hash of producer context.
|
|
@@ -32,7 +32,7 @@ module Equipoise
|
|
|
32
32
|
@client.request(:get, field_path(key), query: query)
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
#
|
|
35
|
+
# Create-or-update a field by key (server returns 201 vs
|
|
36
36
|
# 200). `customizable_type` from the body is ignored server-side (defaults to
|
|
37
37
|
# Contact); a client can't declare fields on arbitrary owner types.
|
|
38
38
|
def declare(key:, **attributes)
|
data/lib/equipoise/errors.rb
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
module Equipoise
|
|
27
27
|
class Error < StandardError
|
|
28
|
-
#
|
|
28
|
+
# The retry taxonomy lives in the gem so no producer has
|
|
29
29
|
# to memorize which failures are transient. Base default: not retryable.
|
|
30
30
|
# A producer's job becomes: `rescue Equipoise::Error => e; raise if e.retryable?`.
|
|
31
31
|
def retryable?
|
|
@@ -59,7 +59,7 @@ module Equipoise
|
|
|
59
59
|
status == 429 || status.to_i >= 500
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
#
|
|
62
|
+
# Pick the subclass by HTTP status and parse the error
|
|
63
63
|
# envelope. Robust to an empty body (401) and the legacy string error body.
|
|
64
64
|
def self.from_response(response)
|
|
65
65
|
status = response.status
|
data/lib/equipoise/syncable.rb
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
# (override `equipoise_sync!` to enqueue) so a web request never blocks on the
|
|
27
27
|
# round-trip. In dev, ensure the queue runs inline or the post never fires.
|
|
28
28
|
#
|
|
29
|
-
# Producer-side firing is exercised in AMP
|
|
29
|
+
# Producer-side firing is exercised in AMP; the gem suite tests the
|
|
30
30
|
# transport methods directly.
|
|
31
31
|
#
|
|
32
32
|
# Used by: producer models that opt in (e.g. AMP's User)
|
data/lib/equipoise/util.rb
CHANGED
|
@@ -14,7 +14,7 @@ module Equipoise
|
|
|
14
14
|
module Util
|
|
15
15
|
module_function
|
|
16
16
|
|
|
17
|
-
#
|
|
17
|
+
# Shallow string→symbol key transform; passes non-Hash
|
|
18
18
|
# input through untouched.
|
|
19
19
|
def symbolize_keys(hash)
|
|
20
20
|
return hash unless hash.is_a?(Hash)
|
data/lib/equipoise/version.rb
CHANGED
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
# =============================================================================
|
|
4
4
|
#
|
|
5
5
|
# Single source of truth for the gem version, read by the gemspec and exposed
|
|
6
|
-
# at runtime.
|
|
7
|
-
# RubyGems publish.
|
|
6
|
+
# at runtime.
|
|
8
7
|
# =============================================================================
|
|
9
8
|
|
|
10
9
|
module Equipoise
|
|
11
|
-
VERSION = "0.
|
|
10
|
+
VERSION = "0.2.0"
|
|
12
11
|
end
|
data/lib/equipoise.rb
CHANGED
|
@@ -16,8 +16,7 @@
|
|
|
16
16
|
#
|
|
17
17
|
# Equipoise.client.contacts.sync(external_id: user.id, email: user.email, name: user.name, tags: ["AMP"])
|
|
18
18
|
#
|
|
19
|
-
#
|
|
20
|
-
# Guide: documentation/integrations/EXTERNAL_CRM_SYNC.md
|
|
19
|
+
# API contract and integration guide: https://app.equipoi.se
|
|
21
20
|
# =============================================================================
|
|
22
21
|
|
|
23
22
|
require "json"
|
|
@@ -40,7 +39,7 @@ require "equipoise/custom_fields"
|
|
|
40
39
|
|
|
41
40
|
module Equipoise
|
|
42
41
|
class << self
|
|
43
|
-
#
|
|
42
|
+
# The process-wide configuration. Built
|
|
44
43
|
# lazily from ENV so a bare `require "equipoise"` is side-effect-free.
|
|
45
44
|
def configuration
|
|
46
45
|
@configuration ||= Configuration.new
|
|
@@ -52,7 +51,7 @@ module Equipoise
|
|
|
52
51
|
configuration
|
|
53
52
|
end
|
|
54
53
|
|
|
55
|
-
#
|
|
54
|
+
# Memoized default client. Most callers use this; tests
|
|
56
55
|
# and multi-tenant producers build their own Client with an explicit config.
|
|
57
56
|
def client
|
|
58
57
|
@client ||= Client.new(configuration)
|
|
@@ -62,7 +61,7 @@ module Equipoise
|
|
|
62
61
|
!configuration.api_key.to_s.empty?
|
|
63
62
|
end
|
|
64
63
|
|
|
65
|
-
#
|
|
64
|
+
# Test-mode kill switch. Put `Equipoise.disable!` in
|
|
66
65
|
# a producer's rails_helper (or set test env) so no spec can make a live call
|
|
67
66
|
# even if EQUIPOISE_API_KEY leaks into the environment. Calls become no-ops
|
|
68
67
|
# returning a benign Response.
|
|
@@ -78,7 +77,7 @@ module Equipoise
|
|
|
78
77
|
configuration.disabled?
|
|
79
78
|
end
|
|
80
79
|
|
|
81
|
-
#
|
|
80
|
+
# Reset global state (used by the test suite's before
|
|
82
81
|
# hook; also handy in a console).
|
|
83
82
|
def reset!
|
|
84
83
|
@configuration = nil
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
#
|
|
7
7
|
# Generated by `rails g equipoise:install`. Reads credentials from the
|
|
8
8
|
# environment so secrets stay out of source control and key rotation needs no
|
|
9
|
-
# redeploy. See
|
|
9
|
+
# redeploy. See https://app.equipoi.se for the API guide.
|
|
10
10
|
|
|
11
11
|
Equipoise.configure do |config|
|
|
12
12
|
# api_key and base_url are read from EQUIPOISE_API_KEY / EQUIPOISE_API_URL
|
|
@@ -17,6 +17,14 @@ Equipoise.configure do |config|
|
|
|
17
17
|
|
|
18
18
|
# config.base_url = "https://app.equipoi.se/api/v1" # override only if needed
|
|
19
19
|
# config.logger = Rails.logger # logs retries + failures
|
|
20
|
-
#
|
|
20
|
+
#
|
|
21
|
+
# Transport knobs — tunable, but clamped to hard ranges so the gem stays a good
|
|
22
|
+
# citizen to the receiver no matter what you set (out-of-range → clamped + logged):
|
|
23
|
+
# config.read_timeout = 30 # seconds per request; clamped to 5..120. Raise
|
|
24
|
+
# # it further for large batch_sync backfills so
|
|
25
|
+
# # a slow batch never times out and gets re-sent.
|
|
26
|
+
# config.max_retries = 2 # retries on transient failures; clamped to 0..5
|
|
27
|
+
# config.batch_size = 500 # contacts per batch_sync request; clamped to 1..500
|
|
28
|
+
#
|
|
21
29
|
# config.allow_insecure_http = true # trusted internal HTTP endpoint
|
|
22
30
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: equipoise
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Equipoise
|
|
@@ -84,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
84
84
|
- !ruby/object:Gem::Version
|
|
85
85
|
version: '0'
|
|
86
86
|
requirements: []
|
|
87
|
-
rubygems_version:
|
|
87
|
+
rubygems_version: 3.6.9
|
|
88
88
|
specification_version: 4
|
|
89
89
|
summary: Ruby client for the Equipoise External CRM Sync API.
|
|
90
90
|
test_files: []
|