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..6d19bd0 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. | @@ -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(); 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()));