Repository navigation
Attachment downloads, bounded read retries, and Export Search fix - #858
russellhoff wants to merge 4 commits into
Conversation
Refresh attachment metadata and reuse the client transport without forwarding credentials to external origins. Limit retries to selected synchronous reads, honor Retry-After, and preserve interruption and injected client policies. Also fix Export Search to express the ticket type only through filter[type]. Cover downloads, pagination and retry behavior with local HTTP tests.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Two moderate issues and unresolved Javadoc placeholders remain.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 2
Open (2)
What changed in this PR
Adds attachment downloads, bounded retries for selected reads, safer transport behavior, and an Export Search query fix.
Changes:
- Adds authenticated attachment download APIs with metadata refresh.
- Adds bounded retries and
Retry-Afterhandling. - Disables method-agnostic default retries.
- Corrects Export Search filtering and adds regression coverage.
| File | Summary |
|---|---|
src/test/java/org/zendesk/client/v2/SearchExportTest.java |
Tests corrected Export Search parameters and pagination. |
src/test/java/org/zendesk/client/v2/ReadRetryTest.java |
Tests retry classification and interruption handling. |
src/test/java/org/zendesk/client/v2/ReadRetryHttpTest.java |
Tests HTTP retries and write non-replay behavior. |
src/test/java/org/zendesk/client/v2/AttachmentDownloadTest.java |
Tests downloads, redirects, and credential boundaries. |
src/main/java/org/zendesk/client/v2/ZendeskResponseRateLimitException.java |
Parses Retry-After values; moderate issue remains for RFC 850 and asctime dates. |
src/main/java/org/zendesk/client/v2/Zendesk.java |
Implements downloads, retries, and Export Search correction; metadata exception unwrapping and two Javadoc placeholders require changes. |
src/main/java/org/zendesk/client/v2/ReadRetry.java |
Defines bounded retry behavior. |
README.md |
Documents download and retry behavior. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Refactor attachment retrieval to handle exceptions and improve error handling. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
PierreBtz
left a comment
There was a problem hiding this comment.
Thanks for the addition. We use an external client to download attachments but I think it's a good idea to offer the feature natively in the zendesk client.
Currently there is a compilation issue that prevents me from finishing up the review and testing.
About the @since FIXME, you can use 1.7.0.
| throw new IllegalArgumentException("Attachment id must be positive"); | ||
| } | ||
| Attachment attachment; | ||
| try { |
There was a problem hiding this comment.
Unless I'm mistaken there is an issue here:
- the indentation is incorrect
- more importantly the method does not close properly. I think a call to
downloadAttachmentContentis missing.
There was a problem hiding this comment.
I think it's solved
Restore the missing content download return after metadata retrieval. Verify checked transport and interruption exceptions in both download phases. Parse legacy HTTP Retry-After dates with the fifty-year RFC 850 window and set contribution since tags to 1.7.0.
PierreBtz
left a comment
There was a problem hiding this comment.
Thanks for the contribution, I left a few comments but overall the change looks good.
| this.retryAfter = millis <= 0 ? 0L : 1 + (millis - 1) / 1000; | ||
| } catch (java.time.DateTimeException | ArithmeticException invalid) { | ||
| // An unusable header is not permission to retry earlier than requested. | ||
| this.retryAfter = Long.MAX_VALUE; |
There was a problem hiding this comment.
Before this change, the default retry would have been applied (DEFAULT_RETRY_AFTER). Is there a reason for not keeping this behavior ?
| RequestBuilder request = new RequestBuilder("GET").setUrl(contentUrl); | ||
| if (org.asynchttpclient.uri.Uri.create(url) | ||
| .isSameBase(org.asynchttpclient.uri.Uri.create(contentUrl))) { | ||
| request = reqBuilder("GET", contentUrl); |
There was a problem hiding this comment.
My understanding is that the intent is to reuse the auth header (since we are querying the ZD api again). But this call will actually reuse all the headers used for a ZD API call which does not seem to be what we want.
I'm especially thinking abbout the Accept header which will likely be application/json (API call) and might not be what the server expect for an attachment.
| try { | ||
| return operation.get(); | ||
| } catch (ZendeskException failure) { | ||
| long delay = delayMillis(failure, attempt); |
There was a problem hiding this comment.
nit: you can invert this line and the next one. It's not much but each time attempt == 3 you'll compute a delay that will never be used.

The client currently has no API for saving attachment content to a file, and its default transport may replay requests without distinguishing reads from writes. This change adds attachment downloads and bounded retries at selected synchronous read boundaries.
Changes
downloadAttachment(long, Path)anddownloadAttachment(Attachment, Path), refreshing metadata by ID and reusing the configured HTTP client.IOExceptionandInterruptedExceptionfrom both metadata retrieval and content download while preserving the interrupt flag and request cancellation.Retry-Aftervalues in seconds or any HTTP-date format (IMF-fixdate, RFC 850, or ANSI C asctime), including the RFC 850 fifty-year rule and space-padded asctime days. Retry only delays of at most 60 seconds; longer or invalid values are propagated without retrying. Injected clients retain their existing retry policy.filter[type]=ticketwithout appendingtype:ticketto the query.@since 1.7.0.Compatibility and limitations
Disabling default transport retries affects all requests using that transport. Explicit retries apply only to selected synchronous reads; writes and OAuth token requests are not retried by this policy. Async job methods do not gain these retries.
Attachment responses are buffered in memory, and the final file copy is not atomic.
Validation
mvn -B -ntp spotless:apply clean verify -Dsurefire.rerunFailingTestsCount=0— 150 tests, zero failures/errors, one skipped. Spotless and the Java 8 API compatibility check passed.