feat: Add pluggable HTTP transport adapter - #964
Conversation
6803c98 to
d2937fb
Compare
Introduce an injectable HTTP transport so consumers can supply a custom adapter via Client.new(http_adapter:) instead of being hard-wired to Net::HTTP. Purely additive: the default path is unchanged and there are no breaking changes for consumers. - Add Recurly::HTTP::Adapter base documenting the #call contract. - Add Recurly::HTTP::DefaultHttpAdapter wrapping Net::HTTP and the shared ConnectionPool. - The client retains URL building, auth headers, idempotency, retry, and error mapping; the adapter owns only transport, translating transport exceptions into a neutral TransportError.
d2937fb to
69b8e05
Compare
|
Adversarial review notes
Clean otherwise — retry counter sharing, 5xx inline re-issue, header case-insensitivity, timeout defaults, and the transport error hierarchy all look correct. Code quality review (recurly:review-pr + marvin:code-reviewer-code-quality) Blocking:
Advisory:
Test coverage review (marvin:test-reviewer-coverage + marvin:test-reviewer-calibration) Blocking:
Advisory:
Integration seam review (marvin:test-reviewer-integration) Advisory:
Seams with adequate coverage: all four AC verification (marvin:code-reviewer-ac) — All 5 criteria: met.
Automated review by Claude. |
Fix an empty-body + JSON content-type response raising a raw TypeError instead of a typed APIError; restore the removed set_debug_output security test; add missing coverage for 5xx on non-GET verbs, the 299/300 status boundary, and the reason-phrase fallback. Also address advisory review notes: TransportError#cause no longer shadows Exception#cause, drop the redundant body param and dead logger param, rename timeout: to read_timeout:, fix the misleading Retrying log, and consolidate HTTP_STATUS_MESSAGES. Co-Authored-By: Claude <noreply@anthropic.com>
|
Addressed in cc417df: Fixed (blocking):
Fixed (bug found while verifying the interface-compatibility note):
Fixed (advisory):
Confirmed as non-issues:
Deliberately deferred:
Full suite (919 examples) passes. |
|
Re-review after fix commit — all blocking issues resolved Ran adversarial, code-quality, and test-coverage passes on the updated diff. Summary: All 7 claimed fixes verified. Previous blocking concerns are addressed:
Remaining items are advisory only:
None of these are blocking. The PR is in good shape. |
epagerecurly
left a comment
There was a problem hiding this comment.
All blocking issues from the initial review are resolved. Ran adversarial, code-quality, and test-coverage passes on the updated diff — zero blocking findings across all three. A few advisories remain (timeout unit mismatch in the constructor, keyword naming, a couple untested defensive branches) but none are blocking.
epagerecurly
left a comment
There was a problem hiding this comment.
All blocking issues from the initial review are resolved. Ran adversarial, code-quality, and test-coverage passes on the updated diff — zero blocking findings across all three. A few advisories remain (timeout unit mismatch in the DefaultHttpAdapter constructor, cause: keyword naming, a couple untested defensive branches) but none are blocking.
read_timeout:/open_timeout: on DefaultHttpAdapter.new were in milliseconds while every other timeout knob in the SDK (Client's per-request options, #call's own overrides) is in seconds. A caller following the SDK-wide convention got timeouts 1000x too short. Normalize the constructor to seconds and add a unit test. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
epagerecurly
left a comment
There was a problem hiding this comment.
Timeout fix in c67f11f is correct — constructor now stores values as-is (seconds), consistent with the per-request override unit. Unit test pins it. No further concerns; the broader adapter seam is clean.
Description
Add an injectable HTTP transport so consumers can supply a custom adapter via
Client.new(http_adapter:)instead of being hard-wired toNet::HTTP. Purely additive: the default path is unchanged and there are no breaking changes for consumers.Recurly::HTTP::Adapterbase documenting the contract#call(method, url, headers, body) -> AdapterResponse.Recurly::HTTP::DefaultHttpAdapterwrappingNet::HTTPand the shared connection pool.TransportError.Testing
Automated Test Coverage:
bundle exec rspec(ruby 2.7.8)Manual:
Client.new(http_adapter:)and confirm requests route through it.Expected: adapter receives (method, url, headers, body).
Expected: default Net::HTTP path behaves identically.