From a31564fd0f5fdc0e4ea32799b1912742e99252d4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 15 Aug 2026 10:54:51 +0000 Subject: [PATCH 1/2] docs: sync Task publish/unpublish docs with apify-docs #2840 apify-docs PR #2840 formalized the PUT /actor-tasks/{actorTaskId} description: publishing and unpublishing a task (via isPublic) require write permission to the task's Actor, not (as this client's docs stated, copied from the reference JS client) "both the task and its Actor." unpublish()'s docblock had gone further and claimed the opposite - that it needed only task-level permission - which directly contradicts the spec. - Corrected TaskClient::publish()/unpublish() docblocks and docs/tasks.md to match the spec's actual permission wording. - Added publish()'s concrete preconditions from the spec (Actor public, publicConfig.inputSchemaFields/datasetView set, Actor has fewer than 50 published tasks). - Bumped CLIENT_VERSION 0.5.0 -> 0.5.1 (patch: docs/comments only, no interface change). API_SPEC_VERSION is unchanged (v2-2026-08-05T133145Z already matches the live spec's info.version and already contains PR #2840's schema/description text). --- CHANGELOG.md | 8 ++++++++ docs/tasks.md | 5 +++-- src/Resource/TaskClient.php | 17 +++++++++-------- src/Version.php | 2 +- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecbbd8c..143dc05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.5.1 + +- Corrected the write-permission wording on `TaskClient::publish()`/`unpublish()`: the OpenAPI spec + states that both `isPublic` and `publicConfig` require write permission to the task's Actor, not + (as `unpublish()`'s docblock previously claimed) permission to the task alone. `publish()`'s + docblock now also states the Actor's fewer-than-50-published-tasks limit. +- `docs/tasks.md` updated to match. + ## 0.5.0 Breaking: `RequestQueueClient` methods that previously returned a raw `array` (or, for diff --git a/docs/tasks.md b/docs/tasks.md index cf7f54a..61444a2 100644 --- a/docs/tasks.md +++ b/docs/tasks.md @@ -25,8 +25,9 @@ foreach ($client->tasks()->iterate(new ListOptions(), 50) as $t) { - `get(): ?Task`, `update(mixed $newFields): Task`, `delete(): void` - `publish(): Task`, `unpublish(): Task` — publish/unpublish the task's public landing page, by - setting `isPublic` through `update()`. Publishing requires the task's Actor to be public and the - task to already have its `publicConfig` set up. + setting `isPublic` through `update()`. Both require write permission to the task's Actor; + publishing additionally requires the Actor to be public, to have fewer than 50 already-published + tasks, and the task's `publicConfig.inputSchemaFields`/`publicConfig.datasetView` to be set. - `start(mixed $input = null, ?TaskStartOptions $options = null): ActorRun` - `call(mixed $input = null, ?TaskStartOptions $options = null, ?int $waitSecs = null): ActorRun` - `getInput(): mixed`, `updateInput(mixed $input): mixed` diff --git a/src/Resource/TaskClient.php b/src/Resource/TaskClient.php index 1a76746..d04f814 100644 --- a/src/Resource/TaskClient.php +++ b/src/Resource/TaskClient.php @@ -59,11 +59,12 @@ public function delete(): void /** * Publishes the task on its public landing page, by setting {@code isPublic} through - * {@see update()}. + * {@see update()}. Requires write permission to the task's Actor. * - * The task's Actor must be public and the task must have its public display configuration - * ({@code publicConfig}) set up first. Requires write permission to both the task and its - * Actor. Publishing an already published task does nothing. + * To publish, the task's Actor must be public, its {@code publicConfig.inputSchemaFields} and + * {@code publicConfig.datasetView} must be set, and the Actor must have fewer than 50 + * published tasks; if any of these are not met, the request fails and nothing is changed. + * Publishing an already published task does nothing. */ public function publish(): Task { @@ -72,12 +73,12 @@ public function publish(): Task /** * Unpublishes the task from its public landing page, by setting {@code isPublic} through - * {@see update()}. + * {@see update()}. Like {@see publish()}, this requires write permission to the task's Actor: + * the API requires Actor write permission for both {@code isPublic} and {@code publicConfig}. * * The public display configuration ({@code publicConfig}) is preserved, so the task can be - * published again without re-entering it. Unlike {@see publish()}, this only requires write - * permission to the task itself (not its Actor), since it does not need to validate the - * Actor's public/display eligibility. Unpublishing a task that is not published does nothing. + * published again without re-entering it. Unpublishing a task that is not published does + * nothing. */ public function unpublish(): Task { diff --git a/src/Version.php b/src/Version.php index cf4211a..efde3e8 100644 --- a/src/Version.php +++ b/src/Version.php @@ -17,7 +17,7 @@ final class Version * The semantic version of this client library (see https://semver.org/). * Changes to the public interface other than additive ones are considered breaking changes. */ - public const CLIENT_VERSION = '0.5.0'; + public const CLIENT_VERSION = '0.5.1'; /** * The version of the Apify OpenAPI specification this client was generated and verified From 424d3d3d779485e0c268d89f90b645de2b513823 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 15 Aug 2026 11:10:20 +0000 Subject: [PATCH 2/2] test: add offline coverage for TaskClient::publish()/unpublish() request bodies TaskIntegrationTest::testPublishUnpublish is token-gated and, by design, asserts publish() fails against the shared test account's task (no publicConfig, foreign Actor) rather than exercising a successful call. That leaves the one behavior fully under the client's control - that publish() sends {"isPublic":true} and unpublish() sends {"isPublic":false} to the task PUT endpoint - without offline coverage, unlike the RequestQueue typed-result refactor's MockTransport suite. Adds tests/Unit/TaskPublishUnpublishTest.php, asserting the HTTP method, path, and exact request body for both methods via MockTransport. --- tests/Unit/TaskPublishUnpublishTest.php | 56 +++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tests/Unit/TaskPublishUnpublishTest.php diff --git a/tests/Unit/TaskPublishUnpublishTest.php b/tests/Unit/TaskPublishUnpublishTest.php new file mode 100644 index 0000000..f3069df --- /dev/null +++ b/tests/Unit/TaskPublishUnpublishTest.php @@ -0,0 +1,56 @@ +queueResponse(200, Json::encode(['data' => [ + 'id' => 'task1', + 'isPublic' => true, + ]])); + + $task = $this->client($transport)->task('task1')->publish(); + + self::assertTrue($task->isPublic()); + $request = $transport->lastRequest(); + self::assertSame('PUT', $request->getMethod()); + self::assertStringContainsString('/actor-tasks/task1', (string) $request->getUri()); + self::assertSame(['isPublic' => true], Json::decode(MockTransport::readBody($request))); + } + + public function testUnpublishSendsIsPublicFalse(): void + { + $transport = (new MockTransport())->queueResponse(200, Json::encode(['data' => [ + 'id' => 'task1', + 'isPublic' => false, + ]])); + + $task = $this->client($transport)->task('task1')->unpublish(); + + self::assertNotTrue($task->isPublic()); + $request = $transport->lastRequest(); + self::assertSame('PUT', $request->getMethod()); + self::assertStringContainsString('/actor-tasks/task1', (string) $request->getUri()); + self::assertSame(['isPublic' => false], Json::decode(MockTransport::readBody($request))); + } +}