clicksend 1.0.0 → 1.1.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 +121 -0
- data/README.md +418 -81
- data/docs/clicksend-api-notes.md +52 -16
- data/lib/clicksend/client.rb +58 -17
- data/lib/clicksend/connection.rb +203 -32
- data/lib/clicksend/errors.rb +99 -3
- data/lib/clicksend/instrumentation.rb +42 -0
- data/lib/clicksend/model.rb +9 -0
- data/lib/clicksend/page.rb +6 -3
- data/lib/clicksend/rate_limit.rb +40 -0
- data/lib/clicksend/resources/account.rb +1 -1
- data/lib/clicksend/resources/sms.rb +98 -20
- data/lib/clicksend/response.rb +35 -0
- data/lib/clicksend/retry_policy.rb +44 -27
- data/lib/clicksend/sms/history_record.rb +85 -0
- data/lib/clicksend/sms/message.rb +1 -1
- data/lib/clicksend/testing/failure.rb +81 -0
- data/lib/clicksend/testing/fake_api.rb +422 -0
- data/lib/clicksend/testing/payloads.rb +88 -0
- data/lib/clicksend/testing/records.rb +27 -0
- data/lib/clicksend/testing.rb +88 -0
- data/lib/clicksend/version.rb +1 -1
- data/lib/clicksend/webhook.rb +182 -0
- data/lib/clicksend.rb +4 -0
- metadata +10 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 73ecf8d49a2a4fa5ffceef490e0f6ebaeb3ccb903286f5b80f31ae82a1a22d30
|
|
4
|
+
data.tar.gz: 51cde5f40184bcd8b80b9bcf404076b48b17f9904eb26cb8aab781356980666c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 90b530124f465bf299cdc25acf770239f1963c9b87e96cc7a9685d8fbfc1029ec72c050e07e0882d2d7b65bc9244e74dfd36934b30c159f616b2e63151d6f086
|
|
7
|
+
data.tar.gz: d14f63a3e5a1ba4ee4f862b82902fcae71f079b5a1d55f06bc2b784c474e19612e37078f88f9382ad3ab48ba3f41568158b57f5fb0bd3757af5b03251395033b
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,126 @@ All notable changes to this project are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [1.1.0] - 2026-10-06
|
|
8
|
+
|
|
9
|
+
Failure semantics, observability and testing support for production messaging. Mostly additive;
|
|
10
|
+
read "Changed" before upgrading. Two additions are **experimental** and may change in a minor
|
|
11
|
+
release: `Clicksend::Webhook` and `Clicksend::RateLimit`, because both rest on behaviour ClickSend
|
|
12
|
+
doesn't document.
|
|
13
|
+
|
|
14
|
+
Retry safety is unchanged in principle and stricter in practice. ClickSend has no idempotency key,
|
|
15
|
+
so a send that may already have been processed is never repeated automatically, and such failures
|
|
16
|
+
are now marked as ambiguous. This reduces the risk of duplicate SMS; it is not a guarantee against
|
|
17
|
+
every possible duplicate (for example, a job runner re-running a job after a crash).
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **Explicit ambiguity.** When a request that is not safe to repeat (such as an SMS send) fails
|
|
22
|
+
in a way that ClickSend may still have processed, the error is extended with
|
|
23
|
+
`Clicksend::AmbiguousRequestError`. That covers:
|
|
24
|
+
- a timeout or reset after the request may have been written;
|
|
25
|
+
- a 5xx;
|
|
26
|
+
- an error reported inside a 2xx body;
|
|
27
|
+
- a 2xx answer the gem can't read, including a send result without a per-message status.
|
|
28
|
+
|
|
29
|
+
The error keeps its class, so existing `rescue` clauses still work. `Error#ambiguous?` is the
|
|
30
|
+
predicate. (A `dup` of the error loses the mark; `clone` and re-raising keep it.)
|
|
31
|
+
- **Request context on errors and responses.** `Error#request` and `Response#request` return a
|
|
32
|
+
`Clicksend::RequestInfo` with `http_method`, `path` (no query string or fragment), `operation`
|
|
33
|
+
(e.g. `"sms.deliver"`), `idempotent` and `attempts`. `Response#request` is not a `Data`
|
|
34
|
+
member, so `Response` equality, `to_h` and pattern matching are unchanged from 1.0.
|
|
35
|
+
- `Error#retryable?`: whether repeating the same request later is both safe and might succeed.
|
|
36
|
+
- **Retry configuration.** `Clicksend::RetryPolicy` is public:
|
|
37
|
+
`Client.new(retry_policy: RetryPolicy.new(max_retries:, base_delay:, max_delay:, max_retry_after:))`.
|
|
38
|
+
The rule deciding *which* failures may be retried lives in the connection and cannot be
|
|
39
|
+
changed by any policy, and the connection enforces the policy's own `max_retries`.
|
|
40
|
+
- **Rate limits (experimental).** `Response#rate_limit` and `APIError#rate_limit` return a
|
|
41
|
+
`Clicksend::RateLimit` (`limit`, `remaining`, `reset_in`) from the rate-limit headers observed
|
|
42
|
+
live on `GET /v3/account`. They are nil when ClickSend sends none.
|
|
43
|
+
- **Instrumentation.** `Client.new(instrumenter:)` accepts `ActiveSupport::Notifications` or any
|
|
44
|
+
object with the same `instrument` signature that yields once. It publishes `request.clicksend`
|
|
45
|
+
and `retry.clicksend`. Payloads never include credentials, query strings or bodies, and the
|
|
46
|
+
wrapped methods' paths contain no phone numbers or message text. `Client#request` and
|
|
47
|
+
`#paginate` take an optional `operation:` label.
|
|
48
|
+
- **Message history.** `sms.history(date_from:, date_to:, to:/from:/status:/message_id:, order:)`
|
|
49
|
+
returns a page of `Clicksend::SMS::HistoryRecord`, whose `delivered?`, `failed?` and `pending?`
|
|
50
|
+
follow ClickSend's "SMS error codes" article and are all false when a row can't be classified
|
|
51
|
+
(e.g. "Completed" with no gateway code, as observed live). ClickSend documents no way to look up
|
|
52
|
+
a send by your own reference; history, filtered by recipient and matched on `custom_string`, is
|
|
53
|
+
the closest. The README explains why a missing row is not proof that nothing was sent.
|
|
54
|
+
- **Webhooks (experimental).** `Clicksend::Webhook.parse_receipt`, `.parse_inbound` and `.parse`
|
|
55
|
+
turn pushed receipts and replies into `SMS::Receipt` and `SMS::InboundMessage`. ClickSend
|
|
56
|
+
documents no way to authenticate pushes, so there is deliberately no verification method; the
|
|
57
|
+
README explains how to secure the endpoint. No real push has been captured yet.
|
|
58
|
+
- **Testing.** `require "clicksend/testing"` adds `Clicksend::Testing::FakeAPI`, an in-memory
|
|
59
|
+
ClickSend you plug in as the transport, covering sends, receipts, replies and the account. It
|
|
60
|
+
records sent messages and can inject failures, including ambiguous ones with an explicit
|
|
61
|
+
`processed:` flag. It deliberately doesn't serve history; stub it. Mistakes in stubs surface
|
|
62
|
+
as `Clicksend::Testing::StubError` (not a `StandardError`), never as a simulated ClickSend
|
|
63
|
+
failure.
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
Behaviour an existing 1.0 application may notice:
|
|
68
|
+
|
|
69
|
+
- **Mark-read without a cutoff is no longer retried.** `sms.mark_receipts_read` and
|
|
70
|
+
`sms.mark_inbound_read` with no `before:` mark *everything* read at the moment ClickSend
|
|
71
|
+
processes the call. Retrying after an unknown outcome could hide items that arrived in between.
|
|
72
|
+
With `before:` they are still retried.
|
|
73
|
+
- **Error messages end with the request they came from**, e.g. `HTTP 500 (POST /v3/sms/send)`.
|
|
74
|
+
Code that matched the exact message text needs updating. `MessageRejected` now carries
|
|
75
|
+
`#request` too.
|
|
76
|
+
- **Unreadable 2xx answers to sends are ambiguous.** Before, they were plain
|
|
77
|
+
`MalformedResponseError`s. That covers `deliver` and `deliver_batch`, including a message
|
|
78
|
+
without a status, which 1.0 reported as a rejection (`MessageRejected` with a nil status, or
|
|
79
|
+
in `Batch#rejected`).
|
|
80
|
+
- **Custom transports:**
|
|
81
|
+
- An exception that is not a `Clicksend::Error` becomes a `ConnectionError` that may have been
|
|
82
|
+
sent. It is ambiguous for a send, retried for idempotent requests, and keeps the original as
|
|
83
|
+
`#cause`; `rescue MyTransportError` around client calls no longer matches.
|
|
84
|
+
- Any other `Clicksend::Error` raised by a transport is treated as an unknown outcome.
|
|
85
|
+
- A response without a valid HTTP status, or an unexpected 1xx/3xx, is ambiguous for a send
|
|
86
|
+
instead of being a rejection.
|
|
87
|
+
- Exceptions raised by transports are copied before context is added.
|
|
88
|
+
- **Loggers and instrumenters cannot change a result.** A logger or instrumenter that raises
|
|
89
|
+
after a request completes is logged and ignored. An instrumenter that never runs the block
|
|
90
|
+
raises `ConfigurationError`, and one that runs it twice can't send twice.
|
|
91
|
+
- **Configuration:**
|
|
92
|
+
- `Client.new(max_retries:)` and `retry_policy:` are mutually exclusive. `Client#with` replaces
|
|
93
|
+
one with the other, and `max_retries: nil` now means the default.
|
|
94
|
+
- `RetryPolicy.new` validates its arguments (`ConfigurationError`) and returns a frozen policy.
|
|
95
|
+
- `Client#request(idempotent:)` honours only `true`; other truthy values, such as `1` or
|
|
96
|
+
`"false"`, no longer make a request retryable.
|
|
97
|
+
|
|
98
|
+
### Compatibility
|
|
99
|
+
|
|
100
|
+
- Ruby 3.3 or newer, as before; tested on 3.3, 3.4 and 4.0, with a Ruby head canary in CI.
|
|
101
|
+
- Faraday 2 remains the only runtime dependency. ActiveSupport is used only in this gem's own
|
|
102
|
+
tests; `instrumenter:` duck-types it.
|
|
103
|
+
- No public method or class from 1.0 was removed. `require "clicksend/testing"` is opt-in and
|
|
104
|
+
not loaded by `require "clicksend"`.
|
|
105
|
+
|
|
106
|
+
### Verification
|
|
107
|
+
|
|
108
|
+
- Unit, integration (local real-socket servers) and contract specs cover the new behaviour.
|
|
109
|
+
Contract specs check the wrapped operations, the fields the models read, and the documented
|
|
110
|
+
page-size range against ClickSend's published OpenAPI files.
|
|
111
|
+
- Live checks on 2026-10-06 were read-only: `sms.history` filtered to ClickSend's test number,
|
|
112
|
+
and the rate-limit headers on `GET /v3/account`. No SMS was sent.
|
|
113
|
+
- **Not verified live:** a webhook push (none has been captured), and a delivery receipt. The
|
|
114
|
+
test number produced none, as ClickSend's legacy docs say it won't. Both are parsed according
|
|
115
|
+
to ClickSend's published schemas and archived documentation.
|
|
116
|
+
|
|
117
|
+
### Documentation
|
|
118
|
+
|
|
119
|
+
- README: positioning, unknown send outcomes and reconciliation, webhooks, history, background
|
|
120
|
+
jobs (including at-least-once job runners), instrumentation, and rate limits. Behaviour that
|
|
121
|
+
ClickSend doesn't document is labelled as inferred or observed.
|
|
122
|
+
- API notes: a review of all 34 of ClickSend's OpenAPI sections plus the archived push
|
|
123
|
+
documentation; read-only live checks of history and rate-limit headers on 2026-10-06.
|
|
124
|
+
- `design/1.1-audit-and-roadmap.md`: the audit, the decisions taken, the features rejected, and
|
|
125
|
+
the reviews.
|
|
126
|
+
|
|
7
127
|
## [1.0.0] - 2026-10-05
|
|
8
128
|
|
|
9
129
|
No changes to the library's behaviour or public API since 1.0.0.rc1.
|
|
@@ -72,6 +192,7 @@ A rewrite for ClickSend's REST v3 API and modern Ruby. See [MIGRATING.md](MIGRAT
|
|
|
72
192
|
- Last release of the original gem: send SMS, poll replies and delivery reports, and check
|
|
73
193
|
the balance through ClickSend's v2 API.
|
|
74
194
|
|
|
195
|
+
[1.1.0]: https://github.com/prayantr/clicksend/compare/v1.0.0...v1.1.0
|
|
75
196
|
[1.0.0]: https://github.com/prayantr/clicksend/compare/v1.0.0.rc1...v1.0.0
|
|
76
197
|
[1.0.0.rc1]: https://github.com/prayantr/clicksend/compare/c99edc5...v1.0.0.rc1
|
|
77
198
|
[0.0.3]: https://github.com/prayantr/clicksend/tree/c99edc5
|