basecradle 0.13.0 → 0.14.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 +45 -1
- data/README.md +5 -5
- data/lib/basecradle/client.rb +8 -4
- data/lib/basecradle/contact_messages.rb +10 -5
- data/lib/basecradle/dashboard.rb +15 -0
- data/lib/basecradle/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e52bd37f68c6961351dde4657cb665f346548f20023d9372166333ff943529b0
|
|
4
|
+
data.tar.gz: f5725693ace3374348be8ef9771db763533d7526ff902ac7f029b3425e061137
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0c2c1cb2a1b5d23214bb743a6be23f1756565696bb2cb807f7c1634fa0fc4c24a326ea862fcbac2d25ed78b8f41f3ee1c1017dea3ca025909345a78b81703058
|
|
7
|
+
data.tar.gz: 70618de428d28dfa790c95f278ddcd043062c0759efd30f1c277204f1704b031977560ada0c8e31ad35c5ac8957e9010c4be846696b2ab011b6fc4a1eae0c91c
|
data/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,49 @@ section**. The newest heading is always the version `lib/basecradle/version.rb`
|
|
|
9
9
|
a release writes its entry and its version in the same PR — and `test/changelog_test.rb`
|
|
10
10
|
fails CI if the two ever disagree.
|
|
11
11
|
|
|
12
|
+
## [0.14.0] - 2026-10-23
|
|
13
|
+
|
|
14
|
+
A parity release. The first full SDK parity audit (basecradle#715) checked every issue
|
|
15
|
+
ever filed on either SDK against the other's shipped code. This release brings over what
|
|
16
|
+
the Python SDK had and this one did not, plus one behavior the founder ruled on.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **The Dashboard's orientation blocks are typed**
|
|
21
|
+
([#254](https://github.com/basecradle/basecradle-ruby/pull/254), mirroring
|
|
22
|
+
basecradle-python#188). `bc.me.environment.concepts_url` links the platform's
|
|
23
|
+
vocabulary. `bc.me.interaction.pagination` (a `BaseCradle::DashboardPagination`:
|
|
24
|
+
`summary`, `guide_url`) says how lists page and what is safe to checkpoint on.
|
|
25
|
+
`bc.me.interaction.tools` (a `BaseCradle::DashboardTools`: `summary`, `mapping_url`)
|
|
26
|
+
says that platform tools are thin wrappers over this same HTTP API. Both blocks read as
|
|
27
|
+
attributes, like every other section. They were previously reachable only as bare
|
|
28
|
+
Hashes through `[]`.
|
|
29
|
+
- **The drift-guard checks error codes too**
|
|
30
|
+
([#253](https://github.com/basecradle/basecradle-ruby/pull/253), mirroring
|
|
31
|
+
basecradle-python#265). Besides the spec's endpoints, `rake test:live` now reads the
|
|
32
|
+
API docs' Error Codes table and fails when the platform documents a code that has no
|
|
33
|
+
typed exception in `BaseCradle::CODE_TO_ERROR`. Without a typed exception, callers would
|
|
34
|
+
get a bare `BaseCradle::Error` for that code. The offline suite also pins its error
|
|
35
|
+
catalog to the registry, so a mapping removed, or added without a test, fails CI.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- **`contact_message.add_note` appends the new note to `notes`**
|
|
40
|
+
([#257](https://github.com/basecradle/basecradle-ruby/pull/257); founder ruling,
|
|
41
|
+
basecradle#715). It still returns the `BaseCradle::Note`. It now also appends that note
|
|
42
|
+
to the record's own `notes`, unless a note with the same `uuid` is already there, so you
|
|
43
|
+
no longer need to re-fetch the record to read it. This matches the Python SDK. Code that
|
|
44
|
+
appended the returned note by hand should stop doing so, or it will hold the note twice.
|
|
45
|
+
- **Retry backoff is capped at 8 seconds**
|
|
46
|
+
([#255](https://github.com/basecradle/basecradle-ruby/pull/255), mirroring
|
|
47
|
+
basecradle-python#107). With `max_retries:` set, the wait still doubles from 0.5s, but it
|
|
48
|
+
never exceeds `BaseCradle::Client::RETRY_MAX_DELAY` (8.0). Before this, the tenth retry
|
|
49
|
+
waited 256 seconds.
|
|
50
|
+
- **Event Delivery is described as "outbound delivery", never "push"**
|
|
51
|
+
([#256](https://github.com/basecradle/basecradle-ruby/pull/256), mirroring
|
|
52
|
+
basecradle-python#181). This is a wording change in the README and in the 0.4.0 entry
|
|
53
|
+
below; no behavior changes.
|
|
54
|
+
|
|
12
55
|
## [0.13.0] - 2026-10-23
|
|
13
56
|
|
|
14
57
|
### Added
|
|
@@ -593,7 +636,7 @@ enrichments come with them — but three things that worked on a `Hash` no longe
|
|
|
593
636
|
header. The platform stores **at most one record per key** (scoped per timeline + author;
|
|
594
637
|
per timeline for authorless webhook endpoints), so a replayed keyed create returns the
|
|
595
638
|
**original record** — no duplicate record, no second **Event Delivery** event, no task
|
|
596
|
-
activation. (Event Delivery is the platform's *outbound*
|
|
639
|
+
activation. (Event Delivery is the platform's *outbound* delivery to your integration;
|
|
597
640
|
the webhook endpoints named above are the *inbound* feature the SDK models — opposite
|
|
598
641
|
directions, different features.) A key identifies one logical create: the same key with a
|
|
599
642
|
different body still returns the original record. Keys never expire and never appear in a
|
|
@@ -687,6 +730,7 @@ the Python SDK's behavior in idiomatic Ruby. Zero runtime dependencies.
|
|
|
687
730
|
- **Quality bars** — a README-as-tested-doc harness (every example runs against a mocked
|
|
688
731
|
API) and a spec drift-guard (CI fails if the live API grows beyond the SDK).
|
|
689
732
|
|
|
733
|
+
[0.14.0]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.14.0
|
|
690
734
|
[0.13.0]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.13.0
|
|
691
735
|
[0.12.0]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.12.0
|
|
692
736
|
[0.11.0]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.11.0
|
data/README.md
CHANGED
|
@@ -175,7 +175,7 @@ inventing a value.
|
|
|
175
175
|
|
|
176
176
|
## Webhooks
|
|
177
177
|
|
|
178
|
-
External services deliver into a timeline by POSTing to an endpoint's secret ingest URL. Each delivery becomes a readable event. This is the **inbound** direction — data arriving at BaseCradle. Its
|
|
178
|
+
External services deliver into a timeline by POSTing to an endpoint's secret ingest URL. Each delivery becomes a readable event. This is the **inbound** direction — data arriving at BaseCradle. Its counterpart is Event Delivery, the platform's outbound delivery to your integration, which the SDK does not model.
|
|
179
179
|
|
|
180
180
|
```ruby
|
|
181
181
|
require "basecradle"
|
|
@@ -258,7 +258,7 @@ A create can succeed on the server while its response is lost on the wire — re
|
|
|
258
258
|
|
|
259
259
|
Keys are scoped **per timeline and per author** for all four resources — yours never collide with another peer's, and the same key on two timelines creates two records.
|
|
260
260
|
|
|
261
|
-
Opt into automatic retries with `max_retries:`. It is off by default, and even when on it only re-sends what's safe: any read (`GET`) and any create that carries an `idempotency_key`. An **unkeyed** create is never retried — which is why the two features ship together.
|
|
261
|
+
Opt into automatic retries with `max_retries:`. It is off by default, and even when on it only re-sends what's safe: any read (`GET`) and any create that carries an `idempotency_key`. An **unkeyed** create is never retried — which is why the two features ship together. Retries back off exponentially — 0.5s, 1s, 2s, … — capped at 8s between attempts.
|
|
262
262
|
|
|
263
263
|
```ruby
|
|
264
264
|
require "basecradle"
|
|
@@ -387,8 +387,8 @@ bc.notes.each { |n| puts n.body } # every note, across every subject, newest fi
|
|
|
387
387
|
A contact message is a flat record with no `type`/`content` wrapper, so `message.uuid`
|
|
388
388
|
is its identity. `set_status` changes the triage verdict in any direction (reopen with
|
|
389
389
|
`"received"`). It updates the object you called it on and returns it. `add_note` returns
|
|
390
|
-
the new `BaseCradle::Note` and
|
|
391
|
-
`
|
|
390
|
+
the new `BaseCradle::Note` and appends it to `message.notes`, unless a note with that
|
|
391
|
+
`uuid` is already there. A note can never be edited or deleted afterwards. A note
|
|
392
392
|
body is not idempotent, so `add_note` is never retried automatically.
|
|
393
393
|
|
|
394
394
|
`data` is a plain `Hash`, exactly as the platform sent it. The SDK does not model the
|
|
@@ -578,7 +578,7 @@ deliberately.
|
|
|
578
578
|
```bash
|
|
579
579
|
bundle install # install dev dependencies
|
|
580
580
|
bundle exec rake # lint + tests (offline — the default)
|
|
581
|
-
bundle exec rake test:live # the
|
|
581
|
+
bundle exec rake test:live # the drift-guard (one GET each of the live spec and API docs)
|
|
582
582
|
bundle exec rubocop # lint only
|
|
583
583
|
gem build basecradle.gemspec # build the gem
|
|
584
584
|
actionlint # lint the GitHub workflows (CI runs this; see below)
|
data/lib/basecradle/client.rb
CHANGED
|
@@ -40,8 +40,11 @@ module BaseCradle
|
|
|
40
40
|
# idempotency key (see +max_retries+).
|
|
41
41
|
DEFAULT_MAX_RETRIES = 0
|
|
42
42
|
|
|
43
|
-
# Backoff between retries doubles each attempt from this base (0.5s, 1s, 2s, …)
|
|
43
|
+
# Backoff between retries doubles each attempt from this base (0.5s, 1s, 2s, …), and
|
|
44
|
+
# is capped at RETRY_MAX_DELAY seconds, so a generous max_retries never sleeps for
|
|
45
|
+
# minutes between attempts.
|
|
44
46
|
RETRY_BASE_DELAY = 0.5
|
|
47
|
+
RETRY_MAX_DELAY = 8.0
|
|
45
48
|
|
|
46
49
|
# Connection failures Net::HTTP raises that mean "the request never got a response".
|
|
47
50
|
CONNECTION_ERRORS = [
|
|
@@ -73,7 +76,8 @@ module BaseCradle
|
|
|
73
76
|
# off. When set above 0, only requests that are safe to re-send are retried: any +GET+
|
|
74
77
|
# (reads change nothing) and any create carrying an +idempotency_key+ (the platform
|
|
75
78
|
# dedupes keyed creates, so a resend can't duplicate the record). An unkeyed +POST+ is
|
|
76
|
-
# never retried, whatever this is set to. Retries back off exponentially
|
|
79
|
+
# never retried, whatever this is set to. Retries back off exponentially, capped at
|
|
80
|
+
# RETRY_MAX_DELAY seconds.
|
|
77
81
|
def initialize(token = nil, base_url: DEFAULT_BASE_URL, timeout: DEFAULT_TIMEOUT,
|
|
78
82
|
max_retries: DEFAULT_MAX_RETRIES)
|
|
79
83
|
resolved = token || ENV.fetch("BASECRADLE_TOKEN", nil)
|
|
@@ -347,9 +351,9 @@ module BaseCradle
|
|
|
347
351
|
(200..299).cover?(response.code.to_i)
|
|
348
352
|
end
|
|
349
353
|
|
|
350
|
-
# Exponential backoff before the next attempt (attempt is 1-based).
|
|
354
|
+
# Exponential backoff before the next attempt (attempt is 1-based), capped.
|
|
351
355
|
def backoff(attempt)
|
|
352
|
-
sleep(RETRY_BASE_DELAY *
|
|
356
|
+
sleep([ RETRY_BASE_DELAY * 2**(attempt - 1), RETRY_MAX_DELAY ].min)
|
|
353
357
|
end
|
|
354
358
|
|
|
355
359
|
def build_uri(path, params)
|
|
@@ -85,18 +85,23 @@ module BaseCradle
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
# Write a note about this contact message, and return it as a +Note+ (you are its
|
|
88
|
-
# author).
|
|
88
|
+
# author). A note can never be edited or deleted afterwards, by anyone. Raises
|
|
89
|
+
# +ValidationError+ (422) for a blank body, and appends nothing.
|
|
89
90
|
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
# (+bc.contact_messages.get(uuid)+) to read it with the new note embedded.
|
|
91
|
+
# The API answers with the note alone, in full, and this object appends it to its own
|
|
92
|
+
# +notes+ — unless a note with that +uuid+ is already there.
|
|
93
93
|
#
|
|
94
94
|
# Never auto-retried — the notes endpoint takes no +Idempotency-Key+, so a resend after
|
|
95
95
|
# a lost response could write the note twice.
|
|
96
96
|
def add_note(body:)
|
|
97
97
|
response = require_client.request("POST", "/contact_messages/#{uuid}/notes",
|
|
98
98
|
json: { "note" => { "body" => body } })
|
|
99
|
-
|
|
99
|
+
note = response.fetch("note")
|
|
100
|
+
notes = @data["notes"] || []
|
|
101
|
+
unless notes.any? { |existing| existing["uuid"] == note["uuid"] }
|
|
102
|
+
@data = @data.merge("notes" => notes + [ note ])
|
|
103
|
+
end
|
|
104
|
+
Note.new(note, client: @client)
|
|
100
105
|
end
|
|
101
106
|
end
|
|
102
107
|
|
data/lib/basecradle/dashboard.rb
CHANGED
|
@@ -15,6 +15,19 @@ module BaseCradle
|
|
|
15
15
|
attribute :name
|
|
16
16
|
attribute :summary
|
|
17
17
|
attribute :you_are
|
|
18
|
+
attribute :concepts_url # the vocabulary: timeline, participation, trust, task, ...
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# How lists page, and what is safe to checkpoint on — the gist plus the full guide.
|
|
22
|
+
class DashboardPagination < ApiObject
|
|
23
|
+
attribute :summary
|
|
24
|
+
attribute :guide_url
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Platform tools are thin wrappers over this same HTTP API — there is no other backend.
|
|
28
|
+
class DashboardTools < ApiObject
|
|
29
|
+
attribute :summary
|
|
30
|
+
attribute :mapping_url # the tool-action → endpoint table
|
|
18
31
|
end
|
|
19
32
|
|
|
20
33
|
# Your data surfaces — timelines first, then every cross-timeline list.
|
|
@@ -25,6 +38,8 @@ module BaseCradle
|
|
|
25
38
|
attribute :tasks_url
|
|
26
39
|
attribute :webhook_endpoints_url
|
|
27
40
|
attribute :webhook_events_url
|
|
41
|
+
attribute :pagination, wrap: DashboardPagination
|
|
42
|
+
attribute :tools, wrap: DashboardTools
|
|
28
43
|
end
|
|
29
44
|
|
|
30
45
|
# Where to manage yourself: profile, sessions, password.
|
data/lib/basecradle/version.rb
CHANGED