Skip to content

chore: sync Java client with Apify OpenAPI spec v2-2026-07-10T105921Z; add iteration helpers and StreamedLog#6

Merged
Pijukatel merged 37 commits into
masterfrom
claude/jolly-albattani-6r538u
Jul 12, 2026
Merged

chore: sync Java client with Apify OpenAPI spec v2-2026-07-10T105921Z; add iteration helpers and StreamedLog#6
Pijukatel merged 37 commits into
masterfrom
claude/jolly-albattani-6r538u

Conversation

@Pijukatel

Copy link
Copy Markdown
Collaborator

Syncs the Java client to Apify OpenAPI spec v2-2026-07-10T105921Z, adds lazy iteration helpers across all collections, and adds a StreamedLog log-redirection helper. Version bumped to 0.3.0.

Added

  • Lazy iteration helpers on every collection client (iterate(...)), plus DatasetClient.iterateItems(...) and KeyValueStoreClient.iterateKeys(...), backed by a shared PaginatedIterator. limit caps total items yielded (matching the reference); page size is a separate chunkSize argument. RequestQueueClient.paginateRequests(...) mirrors the reference cursor iterator.
  • StreamedLog log-redirection helper: RunClient.getStreamedLog() / getStreamedLog(StreamedLogOptions) return an AutoCloseable StreamedLog that follows the run's live log in a background thread and redirects each complete, timestamped message to a destination (default a per-run-prefixed java.util.logging logger). Options: toLog(Consumer<String>), prefix(String), fromStart(boolean).

Changed

  • Bumped spec constant to v2-2026-07-10T105921Z and version to 0.3.0.
  • Breaking: RunClient.getStreamedLog() now returns a StreamedLog helper instead of a raw InputStream (for raw access use run(id).log().stream(new LogOptions().raw(true))). StoreCollectionClient.iterate now takes a chunkSize argument with limit redefined as a total-item cap.
  • Standardized the "official, but experimental" disclaimer across README and all docs pages.

Fixed (StreamedLog robustness)

  • Last complete log message no longer dropped when stopping a live stream (final flush moved to finally).
  • stop()/close() can no longer hang on a startup race (stream opened in start() before the reader launches); close() is fully idempotent; a consumer calling stop()/close() on itself no longer self-joins/deadlocks.
  • A throwing destination consumer no longer escapes as an uncaught daemon-thread exception — it is caught, redirection stops, and one warning is logged (matching the reference); the pending-message buffer is reader-local to avoid a concurrent-reader race.

Documentation

  • Documented all option-object fields (dataset item filters, store list, actor start), the StreamedLog lifecycle, and aligned getStatistics()/store() type references.

See CHANGELOG.md for the full [0.2.0]/[0.3.0] entries.


Generated by Claude Code

claude added 30 commits July 10, 2026 22:21
Forward-compatible spec update (error responses + relaxed nullability);
version bump 0.1.3 -> 0.1.4.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Addresses review items: README Maven/Gradle install coordinates still
pinned 0.1.3; CHANGELOG cited OpenAPI schema names as if Java classes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Adds iterate()/iterateItems()/iterateKeys() matching the reference JS
iterable list(); shared PaginatedIterator engine; hermetic + integration
tests; docs. Store.iterate signature change (limit = total cap, adds
chunkSize) is breaking. Bumps client to 0.2.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
The API clamps over-large page sizes and some endpoints under-report
(dataset items total=0), so short-page and total-based termination both
truncate results. Advance offset by actual count and stop only on an
empty page or the caller cap. Hardens hermetic tests with server-clamp
scenarios and a terminating empty page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Remove stray cp.txt + gitignore it; add hermetic KeyIteratorTest;
pattern-based offline CI test selection; client-side cap trim on both
iterators; doc fixes (nested webhook list+iterate, paginateRequests
exception, iterateKeys limit meaning, iterateItems filter caveat,
normalized null-semantics, quick-start pom.xml, ActorRun statuses,
write return types).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Move the iterateItems filter caveat blockquote after the DatasetClient
table (it had split the table); run only *IntegrationTest in the
integration CI step to avoid re-running hermetic tests; reflow a KVS
comment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Extend the dataset-items iteration caveat (docs + Javadoc) to cover
silent early-termination when an offset window is fully filtered out,
not just the overlap/repeat mode.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
…ests, docs)

Publish workflow now selects hermetic tests by pattern (auto-includes
new ones); add iterateKeys(options, chunkSize) overload for parity;
hermetic test for the overshoot-trim branch; README quick start reads
the token from the environment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Add hermetic tests for the iterateKeys(options, chunkSize) page-size and
cap-combine paths; add convenience iterate(options) overloads across all
collection clients and iterateItems for parity with iterateKeys;
document the two-form iteration convention in the docs index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
The (options, Class) overload clashed with (options, Long chunkSize),
making iterateItems(opts, null) ambiguous. Drop it; typed iteration uses
the 3-arg form. Add a hermetic test for the single-arg iterate delegation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Add DatasetItemsIteratorTest (MockBackend) covering iterateItems /
fetchItemsPage: bare-array paging with empty-page termination, total-cap
trim, and typed decoding — so the dataset-items path is gated offline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Avoids the raw-type in decodesIntoRequestedType.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Terminate key iteration on the API's isTruncated flag; normalize
chunkSize in the PaginatedIterator constructor; reconcile the docs
iteration note with the per-resource tables.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Iterators now snapshot the caller's filters at call time (matching the
old copy() behavior), so mutating the options object mid-iteration no
longer leaks into later pages. Align docs storage-name snippet with the
runnable program's unique-name isolation. Add an isolation regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Actor-creation snippet uses a unique name (matching the storage snippet
and runnable program); README no longer claims blanket item-equivalence
across iterator forms, pointing to the iterateItems+filter caveat.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
…aveat

Document that the client is on Maven Central and show mavenCentral() in
the Gradle snippet; note that lastRun() RunClients are for reads and the
mutating methods need a concrete run id.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Constructor already normalizes totalLimit/chunkSize to positive-or-null
and capRemaining is always > 0 at the call site (exhausted guard), so the
in-method > 0 re-checks were dead. Collapse to a plain null-aware min and
document the input contract. No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
GET /v2/actors/{actorId}/versions takes no pagination params and returns
the full {total, items} list on every request (server ignores offset),
identical to the env-vars collection. Routing it through the offset/limit
PaginatedIterator looped forever (no limit) or duplicated items (with a
limit), since that engine only terminates on an empty page or the total
cap. Make iterate(ListOptions) a single fetch mirroring the env-vars
sibling, honoring the limit as a client-side cap and dropping the
meaningless chunkSize overload.

Correct CHANGELOG (Actor-version regrouped with env-vars as non-paginated)
and docs/actors.md. Integration test now fully drains the versions
iterator; add hermetic ReviewFixesTest regression tests that reproduce the
endpoint with a constant mock backend and assert single-fetch, exactly-once
iteration, and the limit cap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Add a parity note (docs/README.md Iteration section + ListOptions.limit
Javadoc) that during iterate() a non-positive/zero limit means no cap,
unlike list() which sends limit=0 verbatim. Note that the log-redirection
example fragment throws a checked IOException and link the runnable
LogRedirection.java. Documentation only; no behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Extend the list-vs-iterate limit(0) clarification to every options class
that feeds an iterate helper (StorageListOptions, ActorListOptions,
StoreListOptions, DatasetListItemsOptions, ListKeysOptions), since these
are independent classes and do not inherit ListOptions' Javadoc.
ListRequestsOptions is unchanged (it has no iterate overload). Docs only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
- Attribute checked IOException to transferTo(), not getStreamedLog()
- Add "non-positive = no cap" nuance to all iterate Javadocs + doc tables
- Reword README iteration note (drop "falsy"/untested server outcome)
- Fix runs list signature note (RunListOptions) in README
- Use absolute GitHub links for runnable example programs
- Add return types to the TaskClient docs table
- Note versions iterate ignores offset
- Replace non-standard CHANGELOG "### Breaking" with Changed + bold prefix

Documentation/Javadoc only; no runtime or public-interface change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
- ActorVersionCollectionClient: iterator is eager (single fetch), not lazy
- RunListOptions: correct startedAfter/startedBefore scoping per spec
- Add experimental/AI-generated disclaimer to all resource doc pages
- Retitle examples.md ("Examples") and stop overclaiming runnable
- KeyValueStoreClient.iterateKeys: page size left to server, bounded by limit
- Add DatasetClient.iterateItems(options, Class<T>) convenience overload + docs
- Move README imports note above the first quick-start fragment

Docs/Javadoc plus one additive (non-breaking) overload; no behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Revert the iterateItems(options, Class<T>) overload re-added in 09b39b6: it
reintroduces the iterateItems(opts, null) ambiguity that commit 02c977e
removed. Typed default-page-size iteration uses the 3-arg form with a null
chunk size (documented). Fix Javadoc/doc paths /v2/acts -> /v2/actors in
RunListOptions and docs/webhooks.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
- Add experimental/AI-generated disclaimer to docs/examples.md
- Update inbound link text "Runnable examples" -> "Examples"
- Reorder README quick-start (source before compile) and align classpaths
- Enumerate DownloadItemsFormat constants in docs/storages.md
- Name the public com.apify.client.Version class in README Versioning
- Add first-mention pointer for types used in the README quick-start

Documentation only; no code or public-interface change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
The engine backs every offset/limit-paginated collection's iterate method;
the cursor iterateKeys and single-fetch versions/env-vars iterators do not
use it. Comment only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
…laimer

Implement a StreamedLog helper (start/stop lifecycle, line-aware buffering,
fromStart/toLog options, default per-run prefixed logger) matching the
reference JS run.getStreamedLog(), returned by RunClient.getStreamedLog().
Update example (g), docs, and add hermetic + integration test coverage.
Standardize the "official, but experimental" disclaimer across all doc pages.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
The final flush that emits StreamedLog's retained last message ran inside
the try block, so a stop()-induced IOException from a blocked read() skipped
it and the message was lost. Move the flush into finally so a stop still
delivers it. Open the log stream in start() before launching the reader
thread, eliminating a startup race where stop() closed a null stream and
join() hung. Drop the redundant finally close now covered by
try-with-resources.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Apply spotless formatting (test line exceeded 100 cols), document that
start() now opens the stream synchronously and can throw ApifyApiException,
reword the stream-close comment to reflect stop()'s idempotent close, and
move MockBackend streaming fields to the field block.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
… doc placeholder

- StreamedLog.close() is now synchronized and idempotent: running-check + stop
  happen atomically via a shared private stopStreaming(); close() never throws on
  a double/concurrent close while stop() keeps its explicit-misuse contract.
- Rename ReviewFixesTest -> ClientBehaviourRegressionTest to name the behaviours
  it verifies rather than the review process; update stale reference comment.
- docs/runs.md: use RUN_ID placeholder to match the UPPER_SNAKE convention.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
claude added 7 commits July 11, 2026 15:19
Reword StreamedLogTest.closeAfterStopIsNoOp to describe sequential idempotency
and add concurrentCloseNeverThrowsWhileStopRaces, which fires stop() and close()
from two threads to pin that close() never throws under any interleaving.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Add a one-line description for each previously-undescribed field on
DatasetListItemsOptions, StoreListOptions, and ActorStartOptions, matching
the repo's inline field-description style. Semantics verified against the
option classes' setter Javadoc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
…ailure

Guard stopStreaming() against a thread joining itself: the toLog consumer
runs on the reader thread, so stop()/close() from inside it would join the
thread on itself and hang forever while holding the synchronized monitor.

Wrap the default log-prefix lookup (two synchronous GETs at helper
construction) in try/catch, falling back to the runId-only prefix so a
401/403/5xx on the cosmetic lookup no longer aborts getStreamedLog().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Document that stop() joins the reader only when called from another thread;
a self-call from inside the consumer skips the join and returns immediately,
and a repeat self-stop throws because the reader's final flush may re-invoke
the consumer. Steer consumer-driven self-termination to the idempotent close().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Guard destination.accept() so a throwing user consumer is caught and
logged at WARNING (matching the JS reference) instead of escaping as an
uncaught daemon-thread exception. Make the pending-message buffer local
to each reader run so a start() racing a still-draining self-stopped
reader cannot corrupt shared non-thread-safe parsing state.

Also align docs: getStatistics() shown as Optional<JsonNode> in the
index, store() cross-references StoreCollectionClient, and the account
example documents getExtra() for non-modelled fields.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
Introduce a forwardingFailed flag so a throwing destination consumer is
invoked exactly once: the emit loop returns on the first failure and the
final flush is skipped, logging a single warning and delivering nothing
further (matching the JS reference). Condense and correct the catch-block
comment. Tighten the hermetic test to assert exactly-once invocation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019rkZYoebybdnv7MFH9Ua6E
@Pijukatel Pijukatel merged commit e1e67fa into master Jul 12, 2026
2 checks passed
@Pijukatel Pijukatel deleted the claude/jolly-albattani-6r538u branch July 12, 2026 05:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants