Complete the escape hatch: Client::delete(), array bodies, deleteLink(); document it - #14
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 1.x #14 +/- ##
============================================
+ Coverage 98.80% 99.02% +0.21%
- Complexity 187 190 +3
============================================
Files 26 26
Lines 503 511 +8
============================================
+ Hits 497 506 +9
+ Misses 6 5 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
8cf564f to
94876c2
Compare
…(); document it
The SDK is deliberately narrow (no renew, session, subscriptions, ...), so
consumers will hit the boundary — and the low-level Client is meant to be the
way through. But it had no delete() (DELETE /payments/{id}/link is a 204 the
SDK could not even reach), post()/put()/patch() accepted only ?Payload (every
concrete Payload is final, so a consumer had to subclass the abstract base
just to send a body), and the README never mentioned get()/post() as the way
to call unmodeled endpoints.
- Client::delete($uri): array — [] for 204 No Content (decodeJson() now treats
204 as an empty body on every verb; an empty 200 body is still malformed).
- post()/put()/patch() accept Payload|array|null. Arrays run through the same
normalizer, so they are sent as given (keys untouched, nulls kept) while any
Payload / DateTimeInterface nested inside is still transformed; [] → {}.
- ResourceEndpoint::deleteSubResource() + PaymentsEndpoint::deleteLink().
- README: "Calling endpoints the SDK doesn't model" (renew, subscriptions,
DELETE, request()/getLastResponse(), mapping a raw array to a Payment) and a
deleteLink() mention in the link flow.
Note: ClientInterface gains delete() and wider body types — a BC break only for
consumers who IMPLEMENT the interface (a mock-only interface, one week after
1.0.0); callers are unaffected.
Refs #10 (finding 5).
94876c2 to
01e9dbd
Compare
Review question: why should post/put/patch take a nullable body? The null
predates this PR (cancel sent a bodyless POST) and put/patch only had it for
symmetry. Verified live that POST /payments/{id}/cancel accepts an explicit {}
body (200, cancel approved), so no endpoint needs a bodyless request:
- ClientInterface/Client: post()/put()/patch() take `Payload|array $body = []`;
an empty body is always sent as `{}` (the API rejects `[]`).
- ResourceEndpoint::postOperation() likewise; cancel() passes [].
- Tests: cancel now asserts a `{}` body + JSON content type; the escape-hatch
test covers a body-less post('payments/1/renew').
- CLAUDE.md updated.
|
Good catch — the I verified against the live API that |
Part of #10 (finding 5).
Why
The SDK is deliberately narrow (no
renew,session, subscriptions, …), so consumers will hit the boundary — and the low-levelClientis meant to be the way through. But it had nodelete()(DELETE /payments/{id}/link, a 204, was unreachable),post()/put()/patch()accepted only?Payload(every concretePayloadisfinal, so a consumer had to subclass the abstract base just to send a body), and the README never mentionedget()/post()as the way to call unmodeled endpoints.What
Client::delete(string $uri): array— returns[]for204 No Content.decodeJson()now treats 204 as an empty body on every verb (an empty200body is still aMalformedResponseException).post()/put()/patch()acceptPayload|array|null. Arrays run through the same Valinor normalizer, so they are sent as given (keys untouched,nulls kept — use the snake_case keys from the Quickpay docs) while anyPayload/\DateTimeInterfacenested inside is still transformed;[]→{}like an emptyPayload.ResourceEndpoint::deleteSubResource()+PaymentsEndpoint::deleteLink(int $id): void— invalidate a payment-window link (e.g. order cancelled before payment).request()/getLastResponse(), the host-pinning note, and how to map a raw payment array into a typedPayment. Plus adeleteLink()mention in the link-flow section.BC note
ClientInterfacegainsdelete()and wider body types onpost()/put()/patch(). That is a BC break only for consumers who implement the interface (a mock-only interface, one week after 1.0.0); every caller is unaffected. Flagging it so you can decide whether that's acceptable in 1.x — if not,delete()can live onClientonly for now.Tests:
delete()(204 →[], body decoded when present, DELETE verb/URI/auth, no Content-Type, host guard), 204 on other verbs, empty 200 still rejected, verbatim array bodies, nestedPayload+date transformation,[]→{},deleteLink().