From 8fe0fc648a9678d10a94d674872c0bee1d9673c3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 15 Aug 2026 09:53:24 +0000 Subject: [PATCH 1/2] chore: sync Java client with Apify OpenAPI spec v2-2026-08-14T072928Z Bump Version.API_SPEC_VERSION for the doc-only spec update that documents the already-implemented Task.isPublic / Task.publicConfig fields and the TaskPublicConfig schema (no new endpoints, no client code change). Correct doc comments that predated this spec version: Task.getIsPublic() and TaskPublicConfig no longer claim to be undocumented, and TaskClient.unpublish()'s doc comment (and the matching integration-test comment) no longer claims it needs less permission than publish() - the spec states both require write permission to the task's Actor, verified against the reference JS client and the live API. Trim two overlong comments (CompressionTest's brotli-fallback-gap rationale, TaskPublicConfig.getInputSchemaFields' restated Javadoc) to match the coding rules' conciseness requirement. Bump CLIENT_VERSION to 0.6.1 and sync the stale version strings in README.md (client version and API spec version were two releases behind). --- CHANGELOG.md | 16 ++++++++++++++++ README.md | 8 ++++---- docs/tasks.md | 2 +- pom.xml | 2 +- src/main/java/com/apify/client/Version.java | 4 ++-- src/main/java/com/apify/client/task/Task.java | 7 ++++--- .../java/com/apify/client/task/TaskClient.java | 5 ++--- .../com/apify/client/task/TaskPublicConfig.java | 15 +++++++++------ .../java/com/apify/client/CompressionTest.java | 9 +++------ .../client/integration/TaskIntegrationTest.java | 8 +++++--- 10 files changed, 47 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56be841..79ca825 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,22 @@ 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.6.1] - 2026-08-15 + +### Fixed + +- `TaskClient.unpublish()`'s doc comment incorrectly claimed it needs write permission to the task + only, not its Actor. The spec states both `publish()` and `unpublish()` require write permission + to the task's Actor; corrected the comment (and the matching integration-test comment) to say so. + +### Changed + +- Bumped `Version.API_SPEC_VERSION` to `v2-2026-08-14T072928Z`. The new spec version documents the + previously-undocumented `Task.isPublic` / `Task.publicConfig` fields and adds the + `TaskPublicConfig` schema - already implemented here - so this is a metadata/doc-accuracy sync, + not a code change. Updated `Task.getIsPublic()` / `TaskPublicConfig`'s doc comments accordingly: + they no longer describe these as undocumented. + ## [0.6.0] - 2026-08-11 ### Added diff --git a/README.md b/README.md index 92fc960..aa865e9 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Maven (Maven Central is a default repository, so no extra configuration is neede com.apify apify-client - 0.5.0 + 0.6.1 ``` @@ -35,7 +35,7 @@ repositories { } dependencies { - implementation 'com.apify:apify-client:0.5.0' + implementation 'com.apify:apify-client:0.6.1' } ``` @@ -293,10 +293,10 @@ 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.5.0`). +- `Version.CLIENT_VERSION` — the semantic version of this client (`0.6.1`). - `Version.API_SPEC_VERSION` — the version of the [Apify OpenAPI specification](https://docs.apify.com/api/openapi.json) (its `info.version` field) that this client's endpoints, parameters and models were last generated - and checked against (`v2-2026-07-22T122437Z`). It is a snapshot, not a live compatibility + and checked against (`v2-2026-08-14T072928Z`). It is a snapshot, not a live compatibility guarantee: the client keeps working against newer, backward-compatible spec revisions, but a feature added to the API after this snapshot has no corresponding method here yet. diff --git a/docs/tasks.md b/docs/tasks.md index 102f637..ecfd00f 100644 --- a/docs/tasks.md +++ b/docs/tasks.md @@ -24,7 +24,7 @@ Task task = client.tasks().create(Map.of( | Method | Description | |---|---| | `get()` / `update(Object)` / `delete()` | CRUD. Complete with `Optional` / `Task` / no value. | -| `publish()` / `unpublish()` | Publish/unpublish the task on its public landing page, by setting `isPublic` through `update(Object)`. `publish()` requires the task's Actor to be public, write permission to both the task and its Actor, and a configured `publicConfig`. `unpublish()` only requires write permission to the task; it preserves `publicConfig` so the task can be republished without re-entering it. Complete with the updated `Task`. | +| `publish()` / `unpublish()` | Publish/unpublish the task on its public landing page, by setting `isPublic` through `update(Object)`. Both require write permission to both the task and its Actor; `publish()` additionally requires the task's Actor to be public and a configured `publicConfig`. `unpublish()` preserves `publicConfig` so the task can be republished without re-entering it. Complete with the updated `Task`. | | `start(Object input, TaskStartOptions)` | Start a task run (input overrides stored input; `null` uses it). Completes with `ActorRun`. | | `call(Object input, TaskStartOptions, Long waitSecs)` | Start and poll until finished; does **not** stream the run's log. Completes with `ActorRun`. | | `call(Object input, TaskCallOptions, Long waitSecs)` | As above, additionally streaming the run's log for the duration of the wait by default (matching the reference client's `call` defaulting `options.log` to `'default'`). Use `TaskCallOptions.disableLogStreaming()` to opt out, or `logOptions(StreamedLogOptions)` for a custom destination. | diff --git a/pom.xml b/pom.xml index 0b0ec30..3edadbd 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.apify apify-client - 0.6.0 + 0.6.1 jar Apify Java Client diff --git a/src/main/java/com/apify/client/Version.java b/src/main/java/com/apify/client/Version.java index 8f62f1e..87b2a7e 100644 --- a/src/main/java/com/apify/client/Version.java +++ b/src/main/java/com/apify/client/Version.java @@ -13,13 +13,13 @@ 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.6.0"; + public static final String CLIENT_VERSION = "0.6.1"; /** * The version of the Apify OpenAPI specification this client was generated and verified against. * Corresponds to the {@code info.version} field of the Apify OpenAPI document. */ - public static final String API_SPEC_VERSION = "v2-2026-08-05T133145Z"; + public static final String API_SPEC_VERSION = "v2-2026-08-14T072928Z"; private Version() {} } diff --git a/src/main/java/com/apify/client/task/Task.java b/src/main/java/com/apify/client/task/Task.java index 2f063bd..d60d939 100644 --- a/src/main/java/com/apify/client/task/Task.java +++ b/src/main/java/com/apify/client/task/Task.java @@ -87,9 +87,10 @@ public ActorStandby getActorStandby() { } /** - * Whether the task is published on its public landing page. Not part of the documented {@code - * Task} schema in the OpenAPI spec, but the API returns it in practice (mirroring the reference - * JS client). Use {@link TaskClient#publish()} / {@link TaskClient#unpublish()} to change it. + * Whether the task is published on its public landing page, derived from {@link + * TaskPublicConfig#getPublishedAt() publicConfig.publishedAt}. A boxed {@code Boolean} (rather + * than a primitive) to distinguish a field the response did not include from an explicit {@code + * false}. Use {@link TaskClient#publish()} / {@link TaskClient#unpublish()} to change it. */ public Boolean getIsPublic() { return isPublic; diff --git a/src/main/java/com/apify/client/task/TaskClient.java b/src/main/java/com/apify/client/task/TaskClient.java index f97def6..2cf11fb 100644 --- a/src/main/java/com/apify/client/task/TaskClient.java +++ b/src/main/java/com/apify/client/task/TaskClient.java @@ -66,9 +66,8 @@ public CompletableFuture publish() { * #update(Object)}. * *

The public display configuration ({@code publicConfig}) is preserved, so the task can be - * published again without re-entering it. Requires write permission to the task only (unlike - * {@link #publish()}, it does not require permission to the task's Actor). Unpublishing a task - * that is not published does nothing. + * published again without re-entering it. Like {@link #publish()}, requires write permission to + * both the task and its Actor. Unpublishing a task that is not published does nothing. */ public CompletableFuture unpublish() { return update(Map.of("isPublic", false)); diff --git a/src/main/java/com/apify/client/task/TaskPublicConfig.java b/src/main/java/com/apify/client/task/TaskPublicConfig.java index a5ee76f..4f0f25b 100644 --- a/src/main/java/com/apify/client/task/TaskPublicConfig.java +++ b/src/main/java/com/apify/client/task/TaskPublicConfig.java @@ -12,8 +12,9 @@ * null}. {@code publishedAt} is read-only - use {@link TaskClient#publish()} and {@link * TaskClient#unpublish()} to change the publication state. * - *

Not part of the documented {@code Task} schema in the OpenAPI spec, but the API returns it in - * practice (mirroring the reference JS client's {@code TaskPublicConfig}). + *

{@link #getCategorization()} is not part of the documented {@code TaskPublicConfig} schema in + * the OpenAPI spec, but is kept here for parity with the reference JS client, which also exposes + * it. Every other field below is part of the documented schema. */ public final class TaskPublicConfig extends ApifyResource { private Instant publishedAt; @@ -39,16 +40,18 @@ public String getSeoDescription() { return seoDescription; } - /** The category the task is listed under on its public landing page. */ + /** + * The category the task is listed under on its public landing page. Not part of the documented + * schema; see the class-level note. + */ public String getCategorization() { return categorization; } /** Which input schema fields are shown on the public landing page. */ public List getInputSchemaFields() { - // Null-coalesce: Jackson binds directly to the (private) `inputSchemaFields` field for - // deserialization, which can leave it null (field absent or explicit `null` in the response). - // Unmodifiable wrapper: avoid exposing the backing list for external mutation. + // Jackson binds directly to the (private) `inputSchemaFields` field for deserialization, which + // can leave it null (field absent or explicit `null` in the response) - null-coalesce that. return inputSchemaFields == null ? List.of() : Collections.unmodifiableList(inputSchemaFields); } diff --git a/src/test/java/com/apify/client/CompressionTest.java b/src/test/java/com/apify/client/CompressionTest.java index 89912b9..1d2fa01 100644 --- a/src/test/java/com/apify/client/CompressionTest.java +++ b/src/test/java/com/apify/client/CompressionTest.java @@ -28,12 +28,9 @@ * *

Not exercised here: {@code HttpClientCore.compress}'s per-call fallback from a * failing brotli encode to gzip (as opposed to the upfront {@code BROTLI_AVAILABLE} check, - * which the brotli-path tests below do exercise). Accepted gap, not an oversight: brotli4j's - * in-memory {@code Encoder.compress} has no documented, portable way to be made to throw once the - * native codec has loaded for a given platform, so a test forcing that failure would need to fake - * the codec behind a seam that does not otherwise exist in this client - not worth adding for a - * defense-in-depth branch whose only effect, if it were ever reached, is choosing gzip over brotli - * (both already-tested, already-correct codings). + * which the brotli-path tests below do exercise). Accepted gap: brotli4j's {@code Encoder.compress} + * has no portable way to be forced to throw once the native codec has loaded, and the fallback only + * chooses between two codings already covered above. */ class CompressionTest { diff --git a/src/test/java/com/apify/client/integration/TaskIntegrationTest.java b/src/test/java/com/apify/client/integration/TaskIntegrationTest.java index 62f702a..4976c4f 100644 --- a/src/test/java/com/apify/client/integration/TaskIntegrationTest.java +++ b/src/test/java/com/apify/client/integration/TaskIntegrationTest.java @@ -104,9 +104,11 @@ void taskPublishUnpublish() { try { TaskClient tc = client.task(task.getId()); - // unpublish() only requires write permission to the task itself, not its Actor, so it - // succeeds here even though the task's Actor (apify/hello-world) is unowned by this test - // account. Reuses the update() PUT and leaves isPublic not-true. + // The freshly created task is already unpublished, so this unpublish() call is a no-op per + // the spec ("sending the value the task already has does nothing") - it succeeds even + // though the task's Actor (apify/hello-world) is unowned by this test account, without that + // being evidence that unpublish() needs less permission than publish(): both require write + // permission to the task's Actor. Reuses the update() PUT and leaves isPublic not-true. Task unpublished = tc.unpublish().join(); assertFalse(Boolean.TRUE.equals(unpublished.getIsPublic())); From 27733ba76f051d8c8318aed6d916f05c32f76c17 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 15 Aug 2026 09:58:38 +0000 Subject: [PATCH 2/2] fix: correct remaining stale isPublic doc claim in docs/tasks.md The Task-fields description of getIsPublic() still claimed the field was "not part of the documented Task schema... but the API returns it in practice" - the same false claim already corrected elsewhere in this PR, just missed at this second occurrence. Also note categorization explicitly as the one undocumented TaskPublicConfig field, for consistency with the class-level Javadoc fix. --- docs/tasks.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/tasks.md b/docs/tasks.md index ecfd00f..6d19bd0 100644 --- a/docs/tasks.md +++ b/docs/tasks.md @@ -57,9 +57,9 @@ ActorRun streamed = `getRestartOnError()` (`Boolean`)), `getInput()` (a `JsonNode` snapshot of the stored input, from whichever response last returned this `Task` object; prefer `TaskClient.getInput()` above to fetch it fresh on-demand), `getActorStandby()` (`ActorStandby`, from `com.apify.client.actor`, -standby-mode configuration overrides for this task, if any), `getIsPublic()` (`Boolean`; not part -of the documented `Task` schema in the OpenAPI spec, but the API returns it in practice, mirroring -the reference JS client — use `publish()`/`unpublish()` above to change it), and `getPublicConfig()` +standby-mode configuration overrides for this task, if any), `getIsPublic()` (`Boolean`; derived +from `publicConfig.publishedAt` — use `publish()`/`unpublish()` above to change it), and +`getPublicConfig()` (`TaskPublicConfig`, the task's public landing page display configuration, if any). Any field not covered by a typed getter is still available via the inherited `getExtra()` (see [the docs index](README.md#model-fields-and-unmodeled-data-getextra)). @@ -71,8 +71,9 @@ serving standby requests), `getDesiredRequestsPerActorRun()`, `getDisableStandby `TaskPublicConfig` fields (all optional; `null` when unset): `getPublishedAt()` (`Instant`; set when the task is published, `null` when unpublished — read-only, changed via `publish()` / -`unpublish()`), `getSeoTitle()`, `getSeoDescription()`, `getCategorization()`, -`getInputSchemaFields()` (`List`), `getDatasetName()`, `getDatasetView()`. +`unpublish()`), `getSeoTitle()`, `getSeoDescription()`, `getCategorization()` (not part of the +documented schema; kept for parity with the reference JS client), `getInputSchemaFields()` +(`List`), `getDatasetName()`, `getDatasetView()`. ```java Task task = client.task("TASK_ID").unpublish().join();