diff --git a/CHANGELOG.md b/CHANGELOG.md
index 09d3ff0..78fa200 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,52 @@ All notable changes to the Apify Java client are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.4.0] - 2026-07-20
+
+### Changed (breaking)
+
+- Reorganized `com.apify.client` into resource-oriented sub-packages: `actor`, `build`, `run`,
+ `dataset`, `keyvalue`, `requestqueue`, `task`, `schedule`, `webhook`, `user`, `log`, `store`, and
+ `http` (the replaceable transport). `ApifyClient`, `ApifyClientBuilder`, `ApifyApiException`,
+ `ApifyTransportException`, `Version`, and the shared list/pagination types (`ListOptions`,
+ `StorageListOptions`, `PaginationList`) remain in the root `com.apify.client` package.
+- Renamed `HttpBackend` to `HttpClient` and `DefaultHttpBackend` to `DefaultApifyHttpClient`, to
+ avoid confusion with the JDK's own `java.net.http.HttpClient`.
+- Renamed `HttpClient.sendStreaming` to `sendStreamingResponse` (it streams the response, not the
+ request).
+- `HttpClientCore.TransportException` is now the top-level, public `ApifyTransportException`
+ (previously a nested, internal-only type, despite already being thrown across the public API on
+ an exhausted transport-failure retry budget).
+- `ApifyClientBuilder.build()` now validates its configuration (a blank base URL, a negative retry
+ count, or a negative duration) and throws `IllegalArgumentException`, instead of failing later
+ with a confusing, indirect error.
+
+### Changed
+
+- `DatasetClient`/`KeyValueStoreClient` are now fully immutable after construction: the
+ public-base-URL override is resolved in the constructor instead of by a post-construction
+ mutator.
+- `HttpClientCore` deserializes the API's error envelope via a typed Jackson DTO instead of manual
+ `JsonNode` navigation, and parses the request path via `java.net.URI` instead of manual string
+ slicing.
+- Broadened transport-timeout detection (`doNotRetryTimeouts`) to also recognize
+ `SocketTimeoutException`, not just the default backend's `HttpTimeoutException`, so a custom
+ `HttpClient` implementation gets the same retry behavior.
+- Consolidated every resource's API path segment (`datasets`, `actor-builds`, `webhooks`, ...) into
+ a single internal `ResourcePaths` class instead of duplicating literals between `ApifyClient` and
+ each resource client.
+- Lowered `DefaultApifyHttpClient`'s connection-establishment timeout from 30s to 10s.
+- Bumped the Jackson dependency to 2.19.4.
+- The brotli4j native compression codec is no longer bundled by default; gzip remains the automatic
+ fallback, and brotli can be opted into by adding the platform-appropriate native artifact.
+- Added an SLF4J logging facade dependency (no implementation bundled); the client now logs
+ retry/backoff and give-up events.
+- Tightened the "official, but experimental" disclaimer wording (it repeated itself) across the
+ README, documentation, and Javadoc.
+- README: trimmed the quick-start example, clarified what `Version.API_SPEC_VERSION` means, used
+ `Optional.ifPresent` in the single-resource example, documented the client's synchronous and
+ thread-safe nature, and added a resource-to-package table.
+
## [0.3.1] - 2026-07-14
### Added
diff --git a/README.md b/README.md
index 4a08490..2a6ff5f 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,7 @@
# Apify API client for Java
-> **Official, but experimental — AI-generated and AI-maintained.** This is an official Apify client,
-> but it is experimental: it is generated and maintained by AI. Review the code before relying on it
-> in production and report issues on the repository.
+> **Official, but experimental — AI-generated and AI-maintained.** Review the code before relying
+> on it in production and report issues on the repository.
A resource-oriented Java client for the [Apify API](https://docs.apify.com/api/v2), mirroring the
official [JavaScript](https://github.com/apify/apify-client-js) reference client: start from an
@@ -23,7 +22,7 @@ Maven (Maven Central is a default repository, so no extra configuration is neede
com.apifyapify-client
- 0.3.1
+ 0.4.0
```
@@ -35,40 +34,19 @@ repositories {
}
dependencies {
- implementation 'com.apify:apify-client:0.3.1'
+ implementation 'com.apify:apify-client:0.4.0'
}
```
## Quick start
-A complete, copy-pasteable first program (save as `HelloApify.java`). First scaffold a minimal
-`pom.xml` next to it so Maven can resolve the client and its runtime dependencies:
-
-```xml
-
- 4.0.0
- com.example
- hello-apify
- 1.0.0
-
- 17
-
-
-
- com.apify
- apify-client
- 0.3.1
-
-
-
-```
-
-Create `HelloApify.java`:
+A complete, copy-pasteable first program. Add the client as a dependency in your project (see
+Installation above), then run this as `HelloApify.java`:
```java
import com.apify.client.ApifyClient;
-import com.apify.client.ActorRun;
-import com.apify.client.ActorStartOptions;
+import com.apify.client.actor.ActorStartOptions;
+import com.apify.client.run.ActorRun;
class HelloApify {
public static void main(String[] args) {
@@ -80,23 +58,21 @@ class HelloApify {
}
```
-Then populate a `lib/` directory with the client and its runtime dependencies, and compile and run
-against the JVM's `lib/*` classpath wildcard — quote it so the shell does not expand it:
-
-```bash
-# 1. Collect apify-client and its runtime dependencies (Jackson, brotli4j codecs, …) into lib/.
-mvn dependency:copy-dependencies -DoutputDirectory=lib -DincludeScope=runtime
-
-# 2. Compile and run. '.' is for the compiled HelloApify.class; lib/* is the JVM classpath wildcard.
-javac -cp '.:lib/*' HelloApify.java # Windows: javac -cp ".;lib/*" HelloApify.java
-java -cp '.:lib/*' HelloApify # Windows: java -cp ".;lib/*" HelloApify
-```
-
-The remaining snippets below are fragments that assume a configured `client` and these imports: all
-public client types live in the `com.apify.client` package (e.g. `import com.apify.client.*;`); the
-snippets also use `com.fasterxml.jackson.databind.JsonNode` (from the Jackson dependency) for untyped
-data, `java.time.Duration` in the configuration examples, and standard JDK types such as
-`java.util.Optional` and `java.util.Map` (`import java.util.*;`).
+The client is synchronous (each call blocks until the HTTP response arrives, there is no async or
+reactive variant) and, once built via [`ApifyClient.create`](#quick-start) or
+[`ApifyClient.builder()`](#configuration), safe for concurrent use from multiple threads: an
+`ApifyClient` and the resource clients it returns carry no mutable state after construction.
+
+The remaining snippets below are fragments that assume a configured `client` and these imports: the
+client's types are organized by resource into sub-packages of `com.apify.client` (`actor`, `build`,
+`run`, `dataset`, `keyvalue`, `requestqueue`, `task`, `schedule`, `webhook`, `user`, `log`, `store`,
+and `http` for the replaceable transport), with `ApifyClient`, `Version`, `ApifyApiException` and the
+shared list/pagination types staying in the `com.apify.client` root package — see
+[`docs/`](docs/README.md) for the exact package of each type, or import every package with one
+wildcard each (`import com.apify.client.*; import com.apify.client.actor.*; …`). The snippets also
+use `com.fasterxml.jackson.databind.JsonNode` (from the Jackson dependency) for untyped data,
+`java.time.Duration` in the configuration examples, and standard JDK types such as `java.util.Optional`
+and `java.util.Map` (`import java.util.*;`).
```java
ApifyClient client = ApifyClient.create("my-api-token");
@@ -137,12 +113,14 @@ ApifyClient configured =
### Replaceable HTTP transport
-The transport is a replaceable component. The default is `DefaultHttpBackend` (backed by the JDK's
-`java.net.http.HttpClient`); provide your own `HttpBackend` to share a connection pool or customize
-proxy/TLS:
+The transport is a replaceable component, defined by the `com.apify.client.http.HttpClient`
+interface (distinct from the JDK's own `java.net.http.HttpClient`, which the default implementation
+happens to use under the hood — always refer to the JDK one by its fully-qualified name to avoid
+ambiguity, as the snippet below does). The default is `DefaultApifyHttpClient`; provide your own
+`HttpClient` to share a connection pool or customize proxy/TLS:
```java
-HttpBackend backend = new DefaultHttpBackend(java.net.http.HttpClient.newHttpClient());
+HttpClient backend = new DefaultApifyHttpClient(java.net.http.HttpClient.newHttpClient());
ApifyClient withBackend = ApifyClient.builder().token("t").httpBackend(backend).build();
```
@@ -150,16 +128,31 @@ Cross-cutting behaviour applied to every request lives in the client, not the ba
bearer-token authentication, the mandated `User-Agent` header, and retries with exponential
backoff and jitter on `429`, `5xx` and network errors.
+### Logging
+
+The client logs retry/backoff and give-up events through [SLF4J](https://www.slf4j.org/) (a facade
+only — no logging implementation is bundled). Add an SLF4J binding of your choice (e.g. Logback) to
+your own project's dependencies to see these logs; with no binding present, SLF4J silently discards
+them, so this is safe to leave unconfigured.
+
+### Request-body compression
+
+Request bodies of 1024 bytes or more are compressed before sending, preferring
+[brotli](https://github.com/hyperxpro/Brotli4j) (`Content-Encoding: br`) and falling back to gzip.
+The brotli native codec is **not** bundled by default (it is platform-specific, and forcing every
+consumer to pull down every OS/architecture's native binary is wasteful) — without it the client
+transparently uses gzip, which is fully functional on its own. To opt into brotli, add both
+`com.aayushatharva.brotli4j:brotli4j` and your platform's `com.aayushatharva.brotli4j:native--`
+artifact (matching the brotli4j version this client compiles against — see `pom.xml`) as
+dependencies of your own project.
+
## Fetching single resources
Methods that fetch a single resource return an `Optional`: a missing resource is reported by an
empty `Optional` rather than an exception.
```java
-Optional maybeActor = client.actor("apify/hello-world").get();
-if (maybeActor.isPresent()) {
- System.out.println(maybeActor.get().getTitle());
-}
+client.actor("apify/hello-world").get().ifPresent(actor -> System.out.println(actor.getTitle()));
```
## Error handling
@@ -189,12 +182,16 @@ try {
The public `com.apify.client.Version` class (`import com.apify.client.Version;`) exposes two
constants:
-- `Version.CLIENT_VERSION` — the semantic version of this client (`0.3.1`).
-- `Version.API_SPEC_VERSION` — the Apify OpenAPI specification version this client was verified
- against (`v2-2026-07-13T092445Z`).
+- `Version.CLIENT_VERSION` — the semantic version of this client (`0.4.0`).
+- `Version.API_SPEC_VERSION` — the version of the [Apify OpenAPI specification](https://docs.apify.com/api/openapi.json)
+ (its `info.version`, e.g. `v2-2026-07-13T092445Z`) that this release of the client was last
+ checked and updated against. It is a point-in-time reference for maintainers, not a compatibility
+ guarantee: the client also works against other spec versions, since the Apify API is additive and
+ backwards-compatible in practice.
Changes to the public interface other than additive ones are considered breaking changes and follow
-[Semantic Versioning](https://semver.org/).
+[Semantic Versioning](https://semver.org/). See [`CHANGELOG.md`](CHANGELOG.md) for the list of
+changes in each release, including breaking ones (e.g. `0.4.0`'s package reorganization).
### Releasing
@@ -234,21 +231,25 @@ Full documentation is in the [`docs/`](docs/README.md) directory, organized by r
## Resources
-| Accessor | Client | Description |
-|---|---|---|
-| `actors()` / `actor(id)` | `ActorCollectionClient` / `ActorClient` | Actors |
-| `builds()` / `build(id)` | `BuildCollectionClient` / `BuildClient` | Actor builds |
-| `runs()` / `run(id)` | `RunCollectionClient` / `RunClient` | Actor runs |
-| `datasets()` / `dataset(id)` | `DatasetCollectionClient` / `DatasetClient` | Datasets |
-| `keyValueStores()` / `keyValueStore(id)` | `KeyValueStoreCollectionClient` / `KeyValueStoreClient` | Key-value stores |
-| `requestQueues()` / `requestQueue(id)` | `RequestQueueCollectionClient` / `RequestQueueClient` | Request queues |
-| `tasks()` / `task(id)` | `TaskCollectionClient` / `TaskClient` | Actor tasks |
-| `schedules()` / `schedule(id)` | `ScheduleCollectionClient` / `ScheduleClient` | Schedules |
-| `webhooks()` / `webhook(id)` | `WebhookCollectionClient` / `WebhookClient` | Webhooks |
-| `webhookDispatches()` / `webhookDispatch(id)` | `WebhookDispatchCollectionClient` / `WebhookDispatchClient` | Webhook dispatches |
-| `store()` | `StoreCollectionClient` | Apify Store |
-| `me()` / `user(id)` | `UserClient` | Users |
-| `log(id)` | `LogClient` | Build/run logs |
+Each resource's classes live in their own sub-package of `com.apify.client` (see the Package
+column); `ApifyClient` itself, and the shared list/pagination types, stay in the root
+`com.apify.client` package.
+
+| Accessor | Client | Package | Description |
+|---|---|---|---|
+| `actors()` / `actor(id)` | `ActorCollectionClient` / `ActorClient` | `com.apify.client.actor` | Actors |
+| `builds()` / `build(id)` | `BuildCollectionClient` / `BuildClient` | `com.apify.client.build` | Actor builds |
+| `runs()` / `run(id)` | `RunCollectionClient` / `RunClient` | `com.apify.client.run` | Actor runs |
+| `datasets()` / `dataset(id)` | `DatasetCollectionClient` / `DatasetClient` | `com.apify.client.dataset` | Datasets |
+| `keyValueStores()` / `keyValueStore(id)` | `KeyValueStoreCollectionClient` / `KeyValueStoreClient` | `com.apify.client.keyvalue` | Key-value stores |
+| `requestQueues()` / `requestQueue(id)` | `RequestQueueCollectionClient` / `RequestQueueClient` | `com.apify.client.requestqueue` | Request queues |
+| `tasks()` / `task(id)` | `TaskCollectionClient` / `TaskClient` | `com.apify.client.task` | Actor tasks |
+| `schedules()` / `schedule(id)` | `ScheduleCollectionClient` / `ScheduleClient` | `com.apify.client.schedule` | Schedules |
+| `webhooks()` / `webhook(id)` | `WebhookCollectionClient` / `WebhookClient` | `com.apify.client.webhook` | Webhooks |
+| `webhookDispatches()` / `webhookDispatch(id)` | `WebhookDispatchCollectionClient` / `WebhookDispatchClient` | `com.apify.client.webhook` | Webhook dispatches |
+| `store()` | `StoreCollectionClient` | `com.apify.client.store` | Apify Store |
+| `me()` / `user(id)` | `UserClient` | `com.apify.client.user` | Users |
+| `log(id)` | `LogClient` | `com.apify.client.log` | Build/run logs |
## License
diff --git a/docs/README.md b/docs/README.md
index f2459be..385d118 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,8 +1,7 @@
# Apify Java client documentation
-> **Official, but experimental — AI-generated and AI-maintained.** This is an official Apify client,
-> but it is experimental: it is generated and maintained by AI. Review the code before relying on it
-> in production and report issues on the repository.
+> **Official, but experimental — AI-generated and AI-maintained.** Review the code before relying
+> on it in production and report issues on the repository.
This directory documents the public API of the Apify Java client, organized by resource. Each page
lists the available methods with their parameters and short snippets. The snippets are code
@@ -31,10 +30,18 @@ empty `Optional` rather than an exception. API failures are thrown as `ApifyApiE
## Imports and dependencies
-Snippets in these docs assume the client types are imported from `com.apify.client` (e.g.
-`import com.apify.client.*;`) plus standard-library types (`java.util.List`, `java.util.ArrayList`,
-`java.util.Map`, `java.util.Optional`, `java.util.Iterator`, `java.util.function.Consumer`,
-`java.time.Duration`, `java.io.InputStream`).
+The client's classes are organized by resource into sub-packages of `com.apify.client`: `actor`,
+`build`, `run`, `dataset`, `keyvalue`, `requestqueue`, `task`, `schedule`, `webhook`, `user`, `log`,
+`store`, and `http` (the replaceable transport). `ApifyClient`, `ApifyClientBuilder`,
+`ApifyApiException`, `Version`, and the shared list/pagination types (`ListOptions`,
+`StorageListOptions`, `PaginationList`) stay in the `com.apify.client` root package. See each
+resource page for its exact package, or import every sub-package's classes with one wildcard
+per package (e.g. `import com.apify.client.*; import com.apify.client.actor.*; import
+com.apify.client.dataset.*; …`).
+
+Snippets in these docs additionally assume standard-library types (`java.util.List`,
+`java.util.ArrayList`, `java.util.Map`, `java.util.Optional`, `java.util.Iterator`,
+`java.util.function.Consumer`, `java.time.Duration`, `java.io.InputStream`).
Raw-JSON return values use Jackson's `com.fasterxml.jackson.databind.JsonNode`. Jackson is a
transitive dependency of this client, so it is already on your classpath.
diff --git a/docs/actors.md b/docs/actors.md
index 0c640c4..5cfdb1e 100644
--- a/docs/actors.md
+++ b/docs/actors.md
@@ -1,8 +1,7 @@
# Actors, versions & environment variables
-> **Official, but experimental — AI-generated and AI-maintained.** This is an official Apify client,
-> but it is experimental: it is generated and maintained by AI. Review the code before relying on it
-> in production and report issues on the repository.
+> **Official, but experimental — AI-generated and AI-maintained.** Review the code before relying
+> on it in production and report issues on the repository.
Access the Actor collection with `client.actors()` and a single Actor with `client.actor(id)`,
where `id` is an Actor ID or `username~name` (a `/` in the id is accepted and normalized).
diff --git a/docs/builds.md b/docs/builds.md
index 8854024..4d8aafd 100644
--- a/docs/builds.md
+++ b/docs/builds.md
@@ -1,8 +1,7 @@
# Builds
-> **Official, but experimental — AI-generated and AI-maintained.** This is an official Apify client,
-> but it is experimental: it is generated and maintained by AI. Review the code before relying on it
-> in production and report issues on the repository.
+> **Official, but experimental — AI-generated and AI-maintained.** Review the code before relying
+> on it in production and report issues on the repository.
Access the build collection with `client.builds()` (or `client.actor(id).builds()` for an Actor's
builds) and a single build with `client.build(id)`.
diff --git a/docs/examples.md b/docs/examples.md
index 4743610..5a7284c 100644
--- a/docs/examples.md
+++ b/docs/examples.md
@@ -1,8 +1,7 @@
# Examples
-> **Official, but experimental — AI-generated and AI-maintained.** This is an official Apify client,
-> but it is experimental: it is generated and maintained by AI. Review the code before relying on it
-> in production and report issues on the repository.
+> **Official, but experimental — AI-generated and AI-maintained.** Review the code before relying
+> on it in production and report issues on the repository.
Each example below is a code fragment (not a standalone `main`) that assumes a configured `client`
and the imports listed in the [documentation index](README.md#imports-and-dependencies). The
diff --git a/docs/misc.md b/docs/misc.md
index dc5bf13..8ed40df 100644
--- a/docs/misc.md
+++ b/docs/misc.md
@@ -1,8 +1,7 @@
# Store, users & logs
-> **Official, but experimental — AI-generated and AI-maintained.** This is an official Apify client,
-> but it is experimental: it is generated and maintained by AI. Review the code before relying on it
-> in production and report issues on the repository.
+> **Official, but experimental — AI-generated and AI-maintained.** Review the code before relying
+> on it in production and report issues on the repository.
## Apify Store — `client.store()`
diff --git a/docs/runs.md b/docs/runs.md
index e1c03a3..32a7925 100644
--- a/docs/runs.md
+++ b/docs/runs.md
@@ -1,8 +1,7 @@
# Runs
-> **Official, but experimental — AI-generated and AI-maintained.** This is an official Apify client,
-> but it is experimental: it is generated and maintained by AI. Review the code before relying on it
-> in production and report issues on the repository.
+> **Official, but experimental — AI-generated and AI-maintained.** Review the code before relying
+> on it in production and report issues on the repository.
Access the run collection with `client.runs()` (or `client.actor(id).runs()` /
`client.task(id).runs()`) and a single run with `client.run(id)`.
diff --git a/docs/schedules.md b/docs/schedules.md
index d820b6d..4de8b53 100644
--- a/docs/schedules.md
+++ b/docs/schedules.md
@@ -1,8 +1,7 @@
# Schedules
-> **Official, but experimental — AI-generated and AI-maintained.** This is an official Apify client,
-> but it is experimental: it is generated and maintained by AI. Review the code before relying on it
-> in production and report issues on the repository.
+> **Official, but experimental — AI-generated and AI-maintained.** Review the code before relying
+> on it in production and report issues on the repository.
Schedules automatically start Actor or task runs at specified times. Access the collection with
`client.schedules()` and a single schedule with `client.schedule(id)`.
diff --git a/docs/storages.md b/docs/storages.md
index d118ba3..54faeae 100644
--- a/docs/storages.md
+++ b/docs/storages.md
@@ -1,8 +1,7 @@
# Storages: datasets, key-value stores, request queues
-> **Official, but experimental — AI-generated and AI-maintained.** This is an official Apify client,
-> but it is experimental: it is generated and maintained by AI. Review the code before relying on it
-> in production and report issues on the repository.
+> **Official, but experimental — AI-generated and AI-maintained.** Review the code before relying
+> on it in production and report issues on the repository.
The three storage types share a consistent shape: a collection client (`list`, `getOrCreate`) and a
single-resource client (`get`, `update`, `delete`, plus storage-specific operations). Run-nested
diff --git a/docs/tasks.md b/docs/tasks.md
index 9fe6f19..70064e0 100644
--- a/docs/tasks.md
+++ b/docs/tasks.md
@@ -1,8 +1,7 @@
# Tasks
-> **Official, but experimental — AI-generated and AI-maintained.** This is an official Apify client,
-> but it is experimental: it is generated and maintained by AI. Review the code before relying on it
-> in production and report issues on the repository.
+> **Official, but experimental — AI-generated and AI-maintained.** Review the code before relying
+> on it in production and report issues on the repository.
Tasks are pre-configured Actor runs with stored input. Access the task collection with
`client.tasks()` and a single task with `client.task(id)`.
diff --git a/docs/webhooks.md b/docs/webhooks.md
index 44236e3..ff0ba96 100644
--- a/docs/webhooks.md
+++ b/docs/webhooks.md
@@ -1,8 +1,7 @@
# Webhooks & dispatches
-> **Official, but experimental — AI-generated and AI-maintained.** This is an official Apify client,
-> but it is experimental: it is generated and maintained by AI. Review the code before relying on it
-> in production and report issues on the repository.
+> **Official, but experimental — AI-generated and AI-maintained.** Review the code before relying
+> on it in production and report issues on the repository.
Webhooks notify an external service when specific events occur. Access the collection with
`client.webhooks()` and a single webhook with `client.webhook(id)`. Dispatches (individual
diff --git a/pom.xml b/pom.xml
index 7d62df7..ca74362 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.apifyapify-client
- 0.3.1
+ 0.4.0jarApify Java Client
@@ -39,8 +39,9 @@
17UTF-8
- 2.17.2
+ 2.19.45.10.2
+ 2.0.161.23.0
@@ -56,44 +57,41 @@
${jackson.version}
+
+
+ org.slf4j
+ slf4j-api
+ ${slf4j.version}
+
+
+ falls back to gzip when no native codec is available for the running platform. Only the
+ core Java API is a direct (and `optional`, so it is not forced on consumers transitively)
+ dependency; the platform-specific native codec is deliberately NOT bundled by default —
+ every consumer of this library would otherwise be forced to pull down five separate
+ per-OS/arch native binaries whether or not they want brotli. Add brotli4j's
+ platform-appropriate `native--` artifact yourself to opt in; without it, the
+ client transparently falls back to the JDK's built-in gzip codec (see
+ HttpClientCore.detectBrotli()), so compression is fully functional either way. -->
com.aayushatharva.brotli4jbrotli4j${brotli4j.version}
+ true
+
+
com.aayushatharva.brotli4jnative-linux-x86_64${brotli4j.version}
- runtime
-
-
- com.aayushatharva.brotli4j
- native-linux-aarch64
- ${brotli4j.version}
- runtime
-
-
- com.aayushatharva.brotli4j
- native-osx-x86_64
- ${brotli4j.version}
- runtime
-
-
- com.aayushatharva.brotli4j
- native-osx-aarch64
- ${brotli4j.version}
- runtime
-
-
- com.aayushatharva.brotli4j
- native-windows-x86_64
- ${brotli4j.version}
- runtime
+ test
diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml
index 252e702..1b956b0 100644
--- a/spotbugs-exclude.xml
+++ b/spotbugs-exclude.xml
@@ -1,26 +1,54 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/apify/client/ApifyApiException.java b/src/main/java/com/apify/client/ApifyApiException.java
index ced3cbd..1045672 100644
--- a/src/main/java/com/apify/client/ApifyApiException.java
+++ b/src/main/java/com/apify/client/ApifyApiException.java
@@ -1,6 +1,7 @@
package com.apify.client;
import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.Map;
/**
@@ -25,7 +26,7 @@ public class ApifyApiException extends RuntimeException {
private final String path;
private final transient Map data;
- ApifyApiException(
+ public ApifyApiException(
int statusCode,
String type,
String message,
@@ -39,7 +40,10 @@ public class ApifyApiException extends RuntimeException {
this.attempt = attempt;
this.httpMethod = httpMethod;
this.path = path;
- this.data = data;
+ // Defensive copy: the constructor is now public (required for the internal HTTP layer, in a
+ // different package since the package split, to construct this exception), so a caller-held
+ // reference to the original map must not be able to mutate this exception's state afterwards.
+ this.data = data == null ? null : new LinkedHashMap<>(data);
}
/** The HTTP status code of the error response. */
diff --git a/src/main/java/com/apify/client/ApifyClient.java b/src/main/java/com/apify/client/ApifyClient.java
index bc59f35..a2e7a8f 100644
--- a/src/main/java/com/apify/client/ApifyClient.java
+++ b/src/main/java/com/apify/client/ApifyClient.java
@@ -1,13 +1,40 @@
package com.apify.client;
+import com.apify.client.actor.ActorClient;
+import com.apify.client.actor.ActorCollectionClient;
+import com.apify.client.build.BuildClient;
+import com.apify.client.build.BuildCollectionClient;
+import com.apify.client.dataset.DatasetClient;
+import com.apify.client.dataset.DatasetCollectionClient;
+import com.apify.client.http.DefaultApifyHttpClient;
+import com.apify.client.http.HttpClient;
+import com.apify.client.internal.HttpClientCore;
+import com.apify.client.internal.ResourcePaths;
+import com.apify.client.keyvalue.KeyValueStoreClient;
+import com.apify.client.keyvalue.KeyValueStoreCollectionClient;
+import com.apify.client.log.LogClient;
+import com.apify.client.requestqueue.RequestQueueClient;
+import com.apify.client.requestqueue.RequestQueueCollectionClient;
+import com.apify.client.run.ActorRun;
+import com.apify.client.run.RunClient;
+import com.apify.client.run.RunCollectionClient;
+import com.apify.client.schedule.ScheduleClient;
+import com.apify.client.schedule.ScheduleCollectionClient;
+import com.apify.client.store.StoreCollectionClient;
+import com.apify.client.task.TaskClient;
+import com.apify.client.task.TaskCollectionClient;
+import com.apify.client.user.UserClient;
+import com.apify.client.webhook.WebhookClient;
+import com.apify.client.webhook.WebhookCollectionClient;
+import com.apify.client.webhook.WebhookDispatchClient;
+import com.apify.client.webhook.WebhookDispatchCollectionClient;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* The entry point for interacting with the Apify API.
*
- *
Official, but experimental — AI-generated and AI-maintained. This is an official Apify
- * client, but it is experimental: it is generated and maintained by AI. Review the code before
+ *
Official, but experimental — AI-generated and AI-maintained. Review the code before
* relying on it in production and report issues on the repository.
*
*
Construct it with {@link #create(String)} (token-only) or {@link #builder()}, then obtain
@@ -27,8 +54,8 @@
*
*
*
Public interface: {@link ApifyClient} and the resource clients it returns.
- *
Replaceable transport: the {@link HttpBackend} interface, with a default {@link
- * DefaultHttpBackend}; swap it via {@link ApifyClientBuilder#httpBackend(HttpBackend)}.
+ *
Replaceable transport: the {@link HttpClient} interface, with a default {@link
+ * DefaultApifyHttpClient}; swap it via {@link ApifyClientBuilder#httpBackend(HttpClient)}.
*
Cross-cutting behaviour (auth, User-Agent, retries with exponential backoff, timeouts)
* lives in the internal HTTP client and is applied to every request.
*
@@ -58,14 +85,21 @@ public static ApifyClientBuilder builder() {
return new ApifyClientBuilder();
}
- /** Returns the {@code User-Agent} header value this client sends. */
+ /**
+ * Returns the {@code User-Agent} header value this client sends on every API call. Exposed for
+ * introspection/debugging (e.g. logging it alongside a request, or reusing the same value on an
+ * adjacent raw HTTP call for consistent observability) — the client itself does not need callers
+ * to read this back.
+ */
public String getUserAgent() {
return http.userAgent();
}
/**
* Returns the fully-qualified API base URL this client targets (including the {@code /v2}
- * suffix).
+ * suffix). Exposed for introspection/debugging (e.g. confirming which environment a configured
+ * client points at, or building an adjacent raw HTTP call against the same base URL) — the client
+ * itself does not need callers to read this back.
*/
public String getApiBaseUrl() {
return baseUrl;
@@ -87,7 +121,7 @@ public ActorClient actor(String id) {
/** A client for the Actor build collection (list builds). */
public BuildCollectionClient builds() {
- return new BuildCollectionClient(http, baseUrl, "actor-builds");
+ return new BuildCollectionClient(http, baseUrl, ResourcePaths.ACTOR_BUILDS);
}
/** A client for a specific Actor build. */
@@ -99,12 +133,12 @@ public BuildClient build(String id) {
/** A client for the Actor run collection (list runs). */
public RunCollectionClient runs() {
- return new RunCollectionClient(http, baseUrl, "actor-runs");
+ return new RunCollectionClient(http, baseUrl, ResourcePaths.ACTOR_RUNS);
}
/** A client for a specific Actor run. */
public RunClient run(String id) {
- return new RunClient(this, http, baseUrl, "actor-runs", id);
+ return new RunClient(this, http, baseUrl, ResourcePaths.ACTOR_RUNS, id);
}
// ----- Dataset accessors ---------------------------------------------------
@@ -116,7 +150,7 @@ public DatasetCollectionClient datasets() {
/** A client for a specific dataset, addressed by ID or name. */
public DatasetClient dataset(String id) {
- return new DatasetClient(http, baseUrl, "datasets", id).withPublicBase(publicBaseUrl);
+ return new DatasetClient(http, baseUrl, ResourcePaths.DATASETS, id, publicBaseUrl);
}
// ----- Key-value store accessors -------------------------------------------
@@ -128,8 +162,8 @@ public KeyValueStoreCollectionClient keyValueStores() {
/** A client for a specific key-value store, addressed by ID or name. */
public KeyValueStoreClient keyValueStore(String id) {
- return new KeyValueStoreClient(http, baseUrl, "key-value-stores", id)
- .withPublicBase(publicBaseUrl);
+ return new KeyValueStoreClient(
+ http, baseUrl, ResourcePaths.KEY_VALUE_STORES, id, publicBaseUrl);
}
// ----- Request queue accessors ---------------------------------------------
@@ -141,7 +175,7 @@ public RequestQueueCollectionClient requestQueues() {
/** A client for a specific request queue, addressed by ID or name. */
public RequestQueueClient requestQueue(String id) {
- return new RequestQueueClient(http, baseUrl, "request-queues", id);
+ return new RequestQueueClient(http, baseUrl, ResourcePaths.REQUEST_QUEUES, id);
}
// ----- Task accessors ------------------------------------------------------
@@ -182,7 +216,7 @@ public WebhookClient webhook(String id) {
/** A client for the webhook dispatch collection. */
public WebhookDispatchCollectionClient webhookDispatches() {
- return new WebhookDispatchCollectionClient(http, baseUrl, "webhook-dispatches");
+ return new WebhookDispatchCollectionClient(http, baseUrl, ResourcePaths.WEBHOOK_DISPATCHES);
}
/** A client for a specific webhook dispatch. */
@@ -199,7 +233,7 @@ public StoreCollectionClient store() {
/** A client for accessing a build's or run's log. */
public LogClient log(String buildOrRunId) {
- return new LogClient(http, baseUrl, "logs", buildOrRunId);
+ return new LogClient(http, baseUrl, ResourcePaths.LOGS, buildOrRunId);
}
/** A client for the current user ({@code /users/me}). */
@@ -216,9 +250,13 @@ public UserClient user(String id) {
* Sets the status message of the current Actor run.
*
*
This convenience method updates the run identified by the {@code ACTOR_RUN_ID} environment
- * variable, so it only works when called from inside an Actor run. If {@code isTerminal} is true,
- * the message becomes final and won't be overwritten. Throws {@link IllegalStateException} if
- * {@code ACTOR_RUN_ID} is not set.
+ * variable, so it only works when called from inside an Actor run. This mirrors the reference
+ * JavaScript client's equivalent helper, which reads the same platform-injected variable — code
+ * running as an Actor already has {@code ACTOR_RUN_ID} in its environment, so there is nothing
+ * else meaningful to pass here (the alternative, {@link #run(String)}{@code .update(...)}, is
+ * always available for updating a run by an explicit, arbitrary ID). If {@code isTerminal} is
+ * true, the message becomes final and won't be overwritten. Throws {@link IllegalStateException}
+ * if {@code ACTOR_RUN_ID} is not set.
*/
public ActorRun setStatusMessage(String message, boolean isTerminal) {
String runId = System.getenv("ACTOR_RUN_ID");
diff --git a/src/main/java/com/apify/client/ApifyClientBuilder.java b/src/main/java/com/apify/client/ApifyClientBuilder.java
index ef4a377..e9c9ea5 100644
--- a/src/main/java/com/apify/client/ApifyClientBuilder.java
+++ b/src/main/java/com/apify/client/ApifyClientBuilder.java
@@ -1,5 +1,9 @@
package com.apify.client;
+import com.apify.client.http.DefaultApifyHttpClient;
+import com.apify.client.http.HttpClient;
+import com.apify.client.internal.HttpClientCore;
+import com.apify.client.internal.RetryConfig;
import java.time.Duration;
import java.util.function.BooleanSupplier;
@@ -14,6 +18,16 @@ public final class ApifyClientBuilder {
static final int DEFAULT_MAX_RETRIES = 8;
static final Duration DEFAULT_MIN_DELAY = Duration.ofMillis(500);
+
+ /**
+ * Default per-attempt request timeout, deliberately generous (matching the reference JS client):
+ * it bounds a single HTTP round-trip, which for this client can mean uploading/downloading a
+ * large dataset or key-value-store payload, or a long-polling {@code waitForFinish} call — not
+ * just a quick metadata fetch. Lower it via {@link #timeout(Duration)} for latency-sensitive
+ * calls; {@link ApifyClient} does not offer separate per-call-shape timeouts, since every call
+ * already shares this one configurable ceiling and the client has no way to know in advance which
+ * calls in a given application are "small" versus "large".
+ */
static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(360);
/** Environment variable that signals the client is running on the Apify platform. */
@@ -27,7 +41,7 @@ public final class ApifyClientBuilder {
private Duration maxDelayBetweenRetries = DEFAULT_TIMEOUT;
private Duration timeout = DEFAULT_TIMEOUT;
private String userAgentSuffix;
- private HttpBackend httpBackend;
+ private HttpClient httpBackend;
private BooleanSupplier isAtHomeFn = ApifyClientBuilder::defaultIsAtHome;
ApifyClientBuilder() {}
@@ -85,7 +99,7 @@ public ApifyClientBuilder userAgentSuffix(String userAgentSuffix) {
}
/** Replaces the default HTTP backend with a custom implementation (the replaceable transport). */
- public ApifyClientBuilder httpBackend(HttpBackend httpBackend) {
+ public ApifyClientBuilder httpBackend(HttpClient httpBackend) {
this.httpBackend = httpBackend;
return this;
}
@@ -96,9 +110,16 @@ ApifyClientBuilder isAtHomeFn(BooleanSupplier isAtHomeFn) {
return this;
}
- /** Builds the configured {@link ApifyClient}. */
+ /**
+ * Builds the configured {@link ApifyClient}.
+ *
+ * @throws IllegalArgumentException if a configured value is invalid (a blank base URL, a negative
+ * retry count, or a negative duration) — surfaced here, at configuration time, rather than as
+ * a confusing failure on the client's first API call.
+ */
public ApifyClient build() {
- HttpBackend backend = httpBackend != null ? httpBackend : new DefaultHttpBackend();
+ validate();
+ HttpClient backend = httpBackend != null ? httpBackend : new DefaultApifyHttpClient();
String userAgent = buildUserAgent(userAgentSuffix, isAtHomeFn);
RetryConfig retry =
new RetryConfig(maxRetries, minDelayBetweenRetries, maxDelayBetweenRetries, timeout);
@@ -110,12 +131,39 @@ public ApifyClient build() {
return new ApifyClient(http, apiBase, publicBase);
}
+ /**
+ * Fails fast on configuration mistakes that would otherwise surface as a confusing failure much
+ * later (e.g. a malformed request URL, or a retry loop that never sleeps). Does not validate
+ * {@code token}: the API itself is the authority on whether a token is required/valid (some
+ * proxies or Actor-local setups legitimately run without one).
+ */
+ private void validate() {
+ if (baseUrl == null || baseUrl.isBlank()) {
+ throw new IllegalArgumentException("baseUrl must not be null or blank");
+ }
+ if (publicBaseUrl != null && publicBaseUrl.isBlank()) {
+ throw new IllegalArgumentException("publicBaseUrl must not be blank when set");
+ }
+ if (maxRetries < 0) {
+ throw new IllegalArgumentException("maxRetries must not be negative: " + maxRetries);
+ }
+ requireNonNegative(minDelayBetweenRetries, "minDelayBetweenRetries");
+ requireNonNegative(maxDelayBetweenRetries, "maxDelayBetweenRetries");
+ requireNonNegative(timeout, "timeout");
+ }
+
+ private static void requireNonNegative(Duration duration, String name) {
+ if (duration == null || duration.isNegative()) {
+ throw new IllegalArgumentException(name + " must not be null or negative: " + duration);
+ }
+ }
+
+ /** Strips every trailing {@code /} from {@code s} (e.g. so {@code /v2} is never doubled up). */
private static String trimTrailingSlash(String s) {
- int end = s.length();
- while (end > 0 && s.charAt(end - 1) == '/') {
- end--;
+ while (s.endsWith("/")) {
+ s = s.substring(0, s.length() - 1);
}
- return s.substring(0, end);
+ return s;
}
/**
diff --git a/src/main/java/com/apify/client/ApifyTransportException.java b/src/main/java/com/apify/client/ApifyTransportException.java
new file mode 100644
index 0000000..1c2b6f4
--- /dev/null
+++ b/src/main/java/com/apify/client/ApifyTransportException.java
@@ -0,0 +1,23 @@
+package com.apify.client;
+
+/**
+ * Thrown when a request fails at the transport level — connection refused, DNS failure, a request
+ * timeout — before any HTTP response is received from the Apify API.
+ *
+ *
This is distinct from {@link ApifyApiException}, which is thrown when the API does
+ * respond, but with a non-success status. Transport failures are retried the same way as retryable
+ * HTTP statuses (see the client's retry/timeout configuration), and this exception is thrown only
+ * once the retry budget is exhausted (or immediately, for a timeout, when the caller has opted out
+ * of retrying timeouts).
+ *
+ *
It is an unchecked exception, consistent with {@link ApifyApiException}, so callers are not
+ * forced to wrap every call.
+ */
+public class ApifyTransportException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public ApifyTransportException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/main/java/com/apify/client/ListOptions.java b/src/main/java/com/apify/client/ListOptions.java
index 6e952c1..74d185a 100644
--- a/src/main/java/com/apify/client/ListOptions.java
+++ b/src/main/java/com/apify/client/ListOptions.java
@@ -1,5 +1,7 @@
package com.apify.client;
+import com.apify.client.internal.QueryParams;
+
/**
* The standard offset/limit pagination shared by most {@code list} endpoints (builds, runs, tasks,
* schedules, webhooks, Actor versions). All fields are optional; leave one unset to use the API
@@ -33,15 +35,15 @@ public ListOptions desc(Boolean desc) {
return this;
}
- Long offsetValue() {
+ public Long offsetValue() {
return offset;
}
- Long limitValue() {
+ public Long limitValue() {
return limit;
}
- void apply(QueryParams q) {
+ public void apply(QueryParams q) {
q.addLong("offset", offset).addLong("limit", limit);
applyFilters(q);
}
@@ -49,7 +51,7 @@ void apply(QueryParams q) {
/**
* Applies every filter except {@code offset}/{@code limit}, which the iterator drives per page.
*/
- void applyFilters(QueryParams q) {
+ public void applyFilters(QueryParams q) {
q.addBool("desc", desc);
}
}
diff --git a/src/main/java/com/apify/client/PaginationList.java b/src/main/java/com/apify/client/PaginationList.java
index 01f3a1b..d3997a1 100644
--- a/src/main/java/com/apify/client/PaginationList.java
+++ b/src/main/java/com/apify/client/PaginationList.java
@@ -55,28 +55,42 @@ public List getItems() {
return Collections.unmodifiableList(items);
}
- // Package-private setters used by the dataset-items path, which builds pages from headers.
- void setTotal(long total) {
+ // These setters are not intended for application use — the API always returns fully-populated
+ // pages. They are public only because com.apify.client.dataset.DatasetClient (a different
+ // package, post package-split) builds a page from response headers for the dataset-items
+ // endpoint, which reports pagination metadata via headers rather than a JSON envelope.
+
+ /** Not for application use; see the class-level note above. */
+ public void setTotal(long total) {
this.total = total;
}
- void setOffset(long offset) {
+ /** Not for application use; see the class-level note above. */
+ public void setOffset(long offset) {
this.offset = offset;
}
- void setLimit(long limit) {
+ /** Not for application use; see the class-level note above. */
+ public void setLimit(long limit) {
this.limit = limit;
}
- void setCount(long count) {
+ /** Not for application use; see the class-level note above. */
+ public void setCount(long count) {
this.count = count;
}
- void setDesc(boolean desc) {
+ /** Not for application use; see the class-level note above. */
+ public void setDesc(boolean desc) {
this.desc = desc;
}
- void setItems(List items) {
- this.items = items;
+ /**
+ * Not for application use; see the class-level note above. Defensively copies {@code items} so a
+ * caller-held reference to the original list cannot mutate this page afterwards (the constructor
+ * is public only for the same cross-package-wiring reason as the other setters here).
+ */
+ public void setItems(List items) {
+ this.items = items == null ? List.of() : List.copyOf(items);
}
}
diff --git a/src/main/java/com/apify/client/StorageListOptions.java b/src/main/java/com/apify/client/StorageListOptions.java
index f9b9e6e..09112e8 100644
--- a/src/main/java/com/apify/client/StorageListOptions.java
+++ b/src/main/java/com/apify/client/StorageListOptions.java
@@ -1,5 +1,7 @@
package com.apify.client;
+import com.apify.client.internal.QueryParams;
+
/**
* Options for the storage collection list endpoints ({@code GET /v2/datasets}, {@code
* /v2/key-value-stores}, {@code /v2/request-queues}), which add {@code unnamed} and {@code
@@ -46,15 +48,15 @@ public StorageListOptions ownership(String ownership) {
return this;
}
- Long offsetValue() {
+ public Long offsetValue() {
return offset;
}
- Long limitValue() {
+ public Long limitValue() {
return limit;
}
- void apply(QueryParams q) {
+ public void apply(QueryParams q) {
q.addLong("offset", offset).addLong("limit", limit);
applyFilters(q);
}
@@ -62,7 +64,7 @@ void apply(QueryParams q) {
/**
* Applies every filter except {@code offset}/{@code limit}, which the iterator drives per page.
*/
- void applyFilters(QueryParams q) {
+ public void applyFilters(QueryParams q) {
q.addBool("desc", desc).addBool("unnamed", unnamed).addString("ownership", ownership);
}
}
diff --git a/src/main/java/com/apify/client/Version.java b/src/main/java/com/apify/client/Version.java
index e4d344f..7008a23 100644
--- a/src/main/java/com/apify/client/Version.java
+++ b/src/main/java/com/apify/client/Version.java
@@ -13,7 +13,7 @@ public final class Version {
* The semantic version of this client library (see SemVer).
* Changes to the public interface other than additive ones are considered breaking changes.
*/
- public static final String CLIENT_VERSION = "0.3.1";
+ public static final String CLIENT_VERSION = "0.4.0";
/**
* The version of the Apify OpenAPI specification this client was generated and verified against.
diff --git a/src/main/java/com/apify/client/WebhookDispatchClient.java b/src/main/java/com/apify/client/WebhookDispatchClient.java
deleted file mode 100644
index 7234a21..0000000
--- a/src/main/java/com/apify/client/WebhookDispatchClient.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.apify.client;
-
-import java.util.Optional;
-
-/** A client for a specific webhook dispatch ({@code /v2/webhook-dispatches/{dispatchId}}). */
-public final class WebhookDispatchClient {
- private final ResourceContext ctx;
-
- WebhookDispatchClient(HttpClientCore http, String baseUrl, String id) {
- this.ctx = ResourceContext.single(http, baseUrl, "webhook-dispatches", id);
- }
-
- /** Fetches the dispatch, or empty if it does not exist. */
- public Optional get() {
- return ctx.getResource("", new QueryParams(), WebhookDispatch.class);
- }
-}
diff --git a/src/main/java/com/apify/client/Actor.java b/src/main/java/com/apify/client/actor/Actor.java
similarity index 94%
rename from src/main/java/com/apify/client/Actor.java
rename to src/main/java/com/apify/client/actor/Actor.java
index 135fa92..23bb4db 100644
--- a/src/main/java/com/apify/client/Actor.java
+++ b/src/main/java/com/apify/client/actor/Actor.java
@@ -1,5 +1,6 @@
-package com.apify.client;
+package com.apify.client.actor;
+import com.apify.client.ApifyResource;
import java.time.Instant;
/** An Actor on the Apify platform. */
diff --git a/src/main/java/com/apify/client/ActorBuildOptions.java b/src/main/java/com/apify/client/actor/ActorBuildOptions.java
similarity index 93%
rename from src/main/java/com/apify/client/ActorBuildOptions.java
rename to src/main/java/com/apify/client/actor/ActorBuildOptions.java
index 990ee5a..1121425 100644
--- a/src/main/java/com/apify/client/ActorBuildOptions.java
+++ b/src/main/java/com/apify/client/actor/ActorBuildOptions.java
@@ -1,4 +1,6 @@
-package com.apify.client;
+package com.apify.client.actor;
+
+import com.apify.client.internal.QueryParams;
/** Configures {@link ActorClient#build(String, ActorBuildOptions)}. */
public final class ActorBuildOptions {
diff --git a/src/main/java/com/apify/client/ActorClient.java b/src/main/java/com/apify/client/actor/ActorClient.java
similarity index 87%
rename from src/main/java/com/apify/client/ActorClient.java
rename to src/main/java/com/apify/client/actor/ActorClient.java
index ad05ddd..e1a2b5c 100644
--- a/src/main/java/com/apify/client/ActorClient.java
+++ b/src/main/java/com/apify/client/actor/ActorClient.java
@@ -1,5 +1,19 @@
-package com.apify.client;
-
+package com.apify.client.actor;
+
+import com.apify.client.ApifyClient;
+import com.apify.client.build.Build;
+import com.apify.client.build.BuildClient;
+import com.apify.client.build.BuildCollectionClient;
+import com.apify.client.internal.HttpClientCore;
+import com.apify.client.internal.Json;
+import com.apify.client.internal.QueryParams;
+import com.apify.client.internal.ResourceContext;
+import com.apify.client.internal.ResourcePaths;
+import com.apify.client.run.ActorRun;
+import com.apify.client.run.LastRunOptions;
+import com.apify.client.run.RunClient;
+import com.apify.client.run.RunCollectionClient;
+import com.apify.client.webhook.NestedWebhookCollectionClient;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.Optional;
@@ -16,10 +30,10 @@ public final class ActorClient {
private final String baseUrl;
private final String id;
- ActorClient(ApifyClient root, HttpClientCore http, String baseUrl, String id) {
+ public ActorClient(ApifyClient root, HttpClientCore http, String baseUrl, String id) {
this.root = root;
this.http = http;
- this.ctx = ResourceContext.single(http, baseUrl, "actors", id);
+ this.ctx = ResourceContext.single(http, baseUrl, ResourcePaths.ACTORS, id);
this.baseUrl = baseUrl;
this.id = id;
}
diff --git a/src/main/java/com/apify/client/ActorCollectionClient.java b/src/main/java/com/apify/client/actor/ActorCollectionClient.java
similarity index 76%
rename from src/main/java/com/apify/client/ActorCollectionClient.java
rename to src/main/java/com/apify/client/actor/ActorCollectionClient.java
index 799e2f2..7ddfb3c 100644
--- a/src/main/java/com/apify/client/ActorCollectionClient.java
+++ b/src/main/java/com/apify/client/actor/ActorCollectionClient.java
@@ -1,13 +1,18 @@
-package com.apify.client;
+package com.apify.client.actor;
+import com.apify.client.PaginationList;
+import com.apify.client.internal.HttpClientCore;
+import com.apify.client.internal.QueryParams;
+import com.apify.client.internal.ResourceContext;
+import com.apify.client.internal.ResourcePaths;
import java.util.Iterator;
/** A client for the Actor collection ({@code GET/POST /v2/actors}). */
public final class ActorCollectionClient {
private final ResourceContext ctx;
- ActorCollectionClient(HttpClientCore http, String baseUrl) {
- this.ctx = ResourceContext.collection(http, baseUrl, "actors");
+ public ActorCollectionClient(HttpClientCore http, String baseUrl) {
+ this.ctx = ResourceContext.collection(http, baseUrl, ResourcePaths.ACTORS);
}
/** Lists the account's Actors. */
diff --git a/src/main/java/com/apify/client/ActorEnvVar.java b/src/main/java/com/apify/client/actor/ActorEnvVar.java
similarity index 92%
rename from src/main/java/com/apify/client/ActorEnvVar.java
rename to src/main/java/com/apify/client/actor/ActorEnvVar.java
index 7a0f99f..69ce243 100644
--- a/src/main/java/com/apify/client/ActorEnvVar.java
+++ b/src/main/java/com/apify/client/actor/ActorEnvVar.java
@@ -1,4 +1,6 @@
-package com.apify.client;
+package com.apify.client.actor;
+
+import com.apify.client.ApifyResource;
/** An environment variable attached to an Actor version. */
public final class ActorEnvVar extends ApifyResource {
diff --git a/src/main/java/com/apify/client/ActorEnvVarClient.java b/src/main/java/com/apify/client/actor/ActorEnvVarClient.java
similarity index 83%
rename from src/main/java/com/apify/client/ActorEnvVarClient.java
rename to src/main/java/com/apify/client/actor/ActorEnvVarClient.java
index 7e6760c..745661d 100644
--- a/src/main/java/com/apify/client/ActorEnvVarClient.java
+++ b/src/main/java/com/apify/client/actor/ActorEnvVarClient.java
@@ -1,5 +1,8 @@
-package com.apify.client;
+package com.apify.client.actor;
+import com.apify.client.internal.HttpClientCore;
+import com.apify.client.internal.QueryParams;
+import com.apify.client.internal.ResourceContext;
import java.util.Optional;
/**
diff --git a/src/main/java/com/apify/client/ActorEnvVarCollectionClient.java b/src/main/java/com/apify/client/actor/ActorEnvVarCollectionClient.java
similarity index 84%
rename from src/main/java/com/apify/client/ActorEnvVarCollectionClient.java
rename to src/main/java/com/apify/client/actor/ActorEnvVarCollectionClient.java
index 4c31dea..871cf19 100644
--- a/src/main/java/com/apify/client/ActorEnvVarCollectionClient.java
+++ b/src/main/java/com/apify/client/actor/ActorEnvVarCollectionClient.java
@@ -1,5 +1,9 @@
-package com.apify.client;
+package com.apify.client.actor;
+import com.apify.client.PaginationList;
+import com.apify.client.internal.HttpClientCore;
+import com.apify.client.internal.QueryParams;
+import com.apify.client.internal.ResourceContext;
import java.util.Iterator;
/**
diff --git a/src/main/java/com/apify/client/ActorListOptions.java b/src/main/java/com/apify/client/actor/ActorListOptions.java
similarity index 95%
rename from src/main/java/com/apify/client/ActorListOptions.java
rename to src/main/java/com/apify/client/actor/ActorListOptions.java
index cdf9c57..86b0e38 100644
--- a/src/main/java/com/apify/client/ActorListOptions.java
+++ b/src/main/java/com/apify/client/actor/ActorListOptions.java
@@ -1,4 +1,6 @@
-package com.apify.client;
+package com.apify.client.actor;
+
+import com.apify.client.internal.QueryParams;
/** Options for {@link ActorCollectionClient#list(ActorListOptions)}. */
public final class ActorListOptions {
diff --git a/src/main/java/com/apify/client/ActorStartOptions.java b/src/main/java/com/apify/client/actor/ActorStartOptions.java
similarity index 94%
rename from src/main/java/com/apify/client/ActorStartOptions.java
rename to src/main/java/com/apify/client/actor/ActorStartOptions.java
index 3d5c111..0692f42 100644
--- a/src/main/java/com/apify/client/ActorStartOptions.java
+++ b/src/main/java/com/apify/client/actor/ActorStartOptions.java
@@ -1,5 +1,8 @@
-package com.apify.client;
+package com.apify.client.actor;
+import com.apify.client.internal.Json;
+import com.apify.client.internal.QueryParams;
+import com.apify.client.internal.ResourceContext;
import java.util.Base64;
import java.util.List;
@@ -108,7 +111,7 @@ void apply(QueryParams q) {
* parameter requires. Returns {@code null} for a {@code null} list. Shared by Actor and task
* start options (DRY).
*/
- static String encodeWebhooks(List