From a5f5030d2d91fc78cc8194f720a95bd8fea687a8 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Nov 2026 17:53:29 +0000 Subject: [PATCH 1/3] feat: sync .NET client with Apify OpenAPI spec v2-2026-11-09T143926Z, add zstd request compression and clean up docs and comments - Add RequestCompression.Zstd and ZstdHttpCompressor (ZstdSharp.Port). - Add ActorServerRequestOptions.ParentRequestId. - Regenerate the response schemas for the new spec version, bump the client to 0.10.0. - Shorten code comments and documentation, removing restated, outdated and repeated text. Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_017ocuAJkLeAK9WauXA9zCTU --- CHANGELOG.md | 13 ++ README.md | 2 +- docs/README.md | 183 +++++++----------- docs/actors.md | 48 +++-- docs/examples.md | 16 +- docs/runs.md | 6 +- docs/storages.md | 5 +- src/Apify.Client/Apify.Client.csproj | 6 +- src/Apify.Client/ApifyClient.cs | 133 ++++--------- src/Apify.Client/ApifyClientOptions.cs | 59 +++--- src/Apify.Client/ApifyClientVersion.cs | 4 +- .../Exceptions/ActorServerException.cs | 7 +- .../Exceptions/ApifyApiException.cs | 26 +-- .../Exceptions/ApifyApiExceptionTypes.cs | 22 +-- .../Exceptions/ApifyClientException.cs | 12 +- .../Exceptions/ApifyTransportException.cs | 8 +- .../InvalidResponseBodyException.cs | 7 +- .../Exceptions/ResponseValidationException.cs | 9 +- src/Apify.Client/Http/ApifyRequestOptions.cs | 15 +- src/Apify.Client/Http/GzipHttpCompressor.cs | 6 +- src/Apify.Client/Http/HttpClientTransport.cs | 42 ++-- src/Apify.Client/Http/HttpCompressor.cs | 15 +- src/Apify.Client/Http/IHttpTransport.cs | 38 ++-- src/Apify.Client/Http/ZstdHttpCompressor.cs | 50 +++++ src/Apify.Client/Internal/HttpClientCore.cs | 144 ++++++-------- src/Apify.Client/Internal/Json.cs | 14 +- src/Apify.Client/Internal/JsonValues.cs | 5 +- src/Apify.Client/Internal/PageSizing.cs | 10 +- src/Apify.Client/Internal/PagedIteration.cs | 36 ++-- src/Apify.Client/Internal/QueryParams.cs | 21 +- src/Apify.Client/Internal/RequestPolicy.cs | 25 ++- src/Apify.Client/Internal/RequestTier.cs | 7 +- src/Apify.Client/Internal/ResourceContext.cs | 153 +++++---------- .../Internal/ResourceLimitRetry.cs | 25 +-- .../Internal/ResponseOwningStream.cs | 5 +- src/Apify.Client/Internal/ResponseReader.cs | 13 +- src/Apify.Client/Internal/ResponseSchemas.cs | 21 +- .../Internal/ResponseSchemas.json | 2 +- src/Apify.Client/Internal/RetryConfig.cs | 26 +-- src/Apify.Client/Internal/Signatures.cs | 43 ++-- src/Apify.Client/Models/Actor.cs | 17 +- src/Apify.Client/Models/ActorEnvVar.cs | 3 +- src/Apify.Client/Models/ActorRun.cs | 24 +-- src/Apify.Client/Models/ActorTask.cs | 19 +- src/Apify.Client/Models/ActorVersion.cs | 7 +- src/Apify.Client/Models/ApifyResource.cs | 17 +- src/Apify.Client/Models/BatchDeleteResult.cs | 5 +- src/Apify.Client/Models/Build.cs | 20 +- .../Models/KeyValueStoreRecord.cs | 17 +- src/Apify.Client/Models/PaginationList.cs | 40 ++-- .../Models/RequestQueueOperationInfo.cs | 5 +- .../Models/RequestQueueRequest.cs | 22 +-- .../Models/RequestQueueRequestsPage.cs | 5 +- src/Apify.Client/Models/TaskPublicConfig.cs | 17 +- src/Apify.Client/Models/Webhook.cs | 5 +- src/Apify.Client/Options/ActorListOptions.cs | 5 +- .../Options/ActorServerRequestOptions.cs | 43 ++-- src/Apify.Client/Options/ActorStartOptions.cs | 20 +- .../Options/BatchAddRequestsOptions.cs | 11 +- .../Options/DatasetDownloadOptions.cs | 5 +- .../Options/DatasetListItemsOptions.cs | 18 +- .../Options/IterateKeysOptions.cs | 16 +- src/Apify.Client/Options/LastRunOptions.cs | 13 +- src/Apify.Client/Options/ListOptions.cs | 10 +- .../Options/PaginateRequestsOptions.cs | 19 +- .../Options/RequestQueueClientOptions.cs | 14 +- src/Apify.Client/Options/RunChargeOptions.cs | 9 +- .../Options/RunIterateDatasetItemsOptions.cs | 10 +- src/Apify.Client/Options/RunListOptions.cs | 18 +- src/Apify.Client/Options/SetRecordOptions.cs | 18 +- .../Options/StorageListOptions.cs | 11 +- src/Apify.Client/Options/StoreListOptions.cs | 11 +- src/Apify.Client/Options/TaskStartOptions.cs | 14 +- src/Apify.Client/Options/WaitForResources.cs | 25 +-- src/Apify.Client/RequestCompression.cs | 14 +- .../AbstractWebhookCollectionClient.cs | 7 +- src/Apify.Client/Resources/ActorClient.cs | 69 +++---- .../Resources/ActorCollectionClient.cs | 8 +- .../Resources/ActorEnvVarClient.cs | 8 +- .../Resources/ActorEnvVarCollectionClient.cs | 14 +- .../Resources/ActorServerClient.cs | 126 ++++++------ .../Resources/ActorServerResponse.cs | 6 +- .../Resources/ActorVersionClient.cs | 8 +- .../Resources/ActorVersionCollectionClient.cs | 20 +- src/Apify.Client/Resources/BuildClient.cs | 21 +- .../Resources/BuildCollectionClient.cs | 8 +- src/Apify.Client/Resources/DatasetClient.cs | 112 ++++------- .../Resources/DatasetCollectionClient.cs | 13 +- .../Resources/KeyValueStoreClient.cs | 88 +++------ .../KeyValueStoreCollectionClient.cs | 12 +- src/Apify.Client/Resources/LogClient.cs | 42 ++-- .../NestedWebhookCollectionClient.cs | 14 +- .../Resources/RequestQueueClient.cs | 124 +++++------- .../Resources/RequestQueueCollectionClient.cs | 13 +- src/Apify.Client/Resources/RunClient.cs | 119 +++++------- .../Resources/RunCollectionClient.cs | 8 +- src/Apify.Client/Resources/ScheduleClient.cs | 15 +- .../Resources/ScheduleCollectionClient.cs | 8 +- .../Resources/StoreCollectionClient.cs | 22 +-- src/Apify.Client/Resources/TaskClient.cs | 77 +++----- .../Resources/TaskCollectionClient.cs | 8 +- src/Apify.Client/Resources/UserClient.cs | 29 +-- src/Apify.Client/Resources/WebhookClient.cs | 8 +- .../Resources/WebhookCollectionClient.cs | 8 +- .../Resources/WebhookDispatchClient.cs | 8 +- .../WebhookDispatchCollectionClient.cs | 8 +- src/Apify.Client/Statistics.cs | 9 +- src/Apify.Client/StreamedLog.cs | 78 +++----- .../Examples/LogRedirectionExample.cs | 3 +- .../Examples/RunStoreActorExample.cs | 2 +- .../Integration/ActorRunIntegrationTests.cs | 5 +- .../Integration/DatasetIntegrationTests.cs | 5 +- .../Integration/IntegrationTestBase.cs | 60 ++---- .../Integration/IterationIntegrationTests.cs | 22 +-- .../KeyValueStoreIntegrationTests.cs | 29 +++ .../Integration/TaskIntegrationTests.cs | 7 +- .../Unit/ActorServerClientTests.cs | 17 ++ .../Unit/AutoPagingTests.cs | 24 +-- .../Unit/ClientOptionsAndStatsTests.cs | 4 +- tests/Apify.Client.Tests/Unit/ConfigTests.cs | 22 +-- .../Unit/DefaultHeadersAndCompressorTests.cs | 35 ++++ .../Unit/HttpClientTests.cs | 10 +- .../Apify.Client.Tests/Unit/MockTransport.cs | 6 +- .../Unit/ModelAccessorTests.cs | 2 +- .../Unit/RequestShapeTests.cs | 33 +--- .../Unit/ResponseDecompressionTests.cs | 7 +- .../Unit/StreamedLogTests.cs | 10 +- .../Unit/WaitForFinishTests.cs | 13 +- .../Unit/WaitForResourcesTests.cs | 12 +- 129 files changed, 1315 insertions(+), 1940 deletions(-) create mode 100644 src/Apify.Client/Http/ZstdHttpCompressor.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 35f8cf9..b732916 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 0.10.0 + +- Added `RequestCompression.Zstd` and `ZstdHttpCompressor` (quality 1 to 19, default 3), which compress request + bodies with `Content-Encoding: zstd`. The client now depends on `ZstdSharp.Port`, because .NET 8 has no zstd + encoder. +- Added `ActorServerRequestOptions.ParentRequestId`, which `ActorServerClient.RequestAsync` sends as the + `X-Actor-Request-ID` header, over one passed in `Headers`. +- Documented `zstd` as an accepted `SetRecordOptions.ContentEncoding`, and that `RunChargeOptions.RequestId` is + ignored by runs of single-tenant Server Actors. +- Shortened the code comments and documentation: removed restated, outdated and repeated text. +- Bumped `ApifyClientVersion.ApiSpecVersion` to the Apify OpenAPI spec `v2-2026-11-09T143926Z` and the project + version to `0.10.0`; regenerated the embedded response schemas. + ## 0.9.0 - Added `ActorClient.Server()`, which returns an `ActorServerClient` for Server Actors (formerly Actor diff --git a/README.md b/README.md index 1a9a788..984f60a 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ var run = await client.Actor("apify/hello-world").CallAsync(); // Read items from the run's default dataset. var items = await client.Dataset(run.DefaultDatasetId!).ListItemsAsync(); -// Items.Count is the number of items in THIS page; Total is the dataset's full count across all pages. +// Items.Count is the number of items on this page; Total is the count across all pages. Console.WriteLine($"Items on this page: {items.Items.Count} (of {items.Total} total)"); ``` diff --git a/docs/README.md b/docs/README.md index dadb17a..f490cb4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,14 +2,13 @@ See the [top-level README](../README.md) for the client's official-but-experimental status. -A resource-oriented .NET 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 -`ApifyClient`, then drill down into resources. +A resource-oriented client for the [Apify API](https://docs.apify.com/api/v2), mirroring the +[JavaScript](https://github.com/apify/apify-client-js) reference client: start from an `ApifyClient` and +drill down into resources. -All API calls are asynchronous and return `Task`/`Task`; every method takes an optional -`CancellationToken cancellationToken = default` as its final parameter (omitted from the reference -signatures on the pages below for brevity). Method names mirror the reference client with the .NET -`Async` suffix (`GetAsync`, `ListAsync`, `CallAsync`, …). +All calls are asynchronous. Every method takes an optional `CancellationToken cancellationToken = default` as +its last parameter, omitted from the signatures in these pages. Method names follow the reference client with +the `Async` suffix (`GetAsync`, `ListAsync`, `CallAsync`, …). ## Contents @@ -36,8 +35,7 @@ dotnet add package Apify.Client ## Quick start -All snippets in this documentation assume `ImplicitUsings` is disabled (the repository's convention), -so every `using` — even `System` — is listed explicitly and appears before any top-level statement. +The snippets assume `ImplicitUsings` is disabled, so every `using`, `System` included, is listed. ```csharp using System; @@ -45,42 +43,33 @@ using Apify.Client; var client = new ApifyClient("my-api-token"); -// Start an Actor and wait for it to finish. Pass waitSecs (e.g. 120) to bound the wait; -// by default it waits indefinitely. +// Start an Actor and wait for it to finish. Pass waitSecs to bound the wait. var run = await client.Actor("apify/hello-world").CallAsync(); // Read items from the run's default dataset. var items = await client.Dataset(run.DefaultDatasetId!).ListItemsAsync(); -// Items.Count is the number of items in THIS page; Total is the dataset's full count across all pages. +// Items.Count is the number of items on this page; Total is the count across all pages. Console.WriteLine($"Items on this page: {items.Items.Count} (of {items.Total} total)"); ``` -`new ApifyClient("my-api-token")` takes the token as an explicit argument — it does **not** read -`APIFY_TOKEN` (or any other environment variable) automatically. Read it yourself if you want that, -e.g. `new ApifyClient(Environment.GetEnvironmentVariable("APIFY_TOKEN") ?? throw new InvalidOperationException("Set APIFY_TOKEN"))` -(the null-coalescing throw keeps the call null-safe when the variable is unset). - -Get your API token from the -[Apify Console → Settings → API & Integrations](https://console.apify.com/settings/integrations). +The client does not read `APIFY_TOKEN` or any other environment variable; pass the token yourself, for example +`new ApifyClient(Environment.GetEnvironmentVariable("APIFY_TOKEN") ?? throw new InvalidOperationException("Set APIFY_TOKEN"))`. +Get a token in [Apify Console → Settings → API & Integrations](https://console.apify.com/settings/integrations). ## Namespaces -The public API is spread across a small set of namespaces. Because `ImplicitUsings` is disabled, add -the `using` directives for whichever ones a file references: - -| Namespace | What lives here | +| Namespace | Contents | |---|---| -| `Apify.Client` | The entry point (`ApifyClient`), `ApifyClientOptions`, `ApifyClientVersion`, and the log-redirection helper `StreamedLog`. | -| `Apify.Client.Resources` | Every resource client the entry point returns — `ActorClient`, `RunClient`, `BuildClient`, `DatasetClient`, `KeyValueStoreClient`, `RequestQueueClient`, `TaskClient`, `ScheduleClient`, `LogClient`, `UserClient`, `ActorVersionClient`, `ActorEnvVarClient`, the `…CollectionClient` types (including `ActorVersionCollectionClient`, `ActorEnvVarCollectionClient`, `NestedWebhookCollectionClient`, and `WebhookDispatchCollectionClient`), etc. | -| `Apify.Client.Models` | Data models returned by the clients — `Actor`, `ActorRun`, `Build`, `Dataset`, `RequestQueueRequest`, `ActorEnvVar`, `PaginationList`, and so on. | -| `Apify.Client.Options` | The option/request objects passed into methods — `ActorStartOptions`, `ActorBuildOptions`, `ActorListOptions`, `RunListOptions`, `RunResurrectOptions`, `RunChargeOptions`, `MetamorphOptions`, `LastRunOptions`, `DatasetListItemsOptions`, `DatasetDownloadOptions`, `DownloadItemsFormat`, `GetRecordOptions`, `SetRecordOptions`, `ListKeysOptions`, `IterateKeysOptions`, `ListRequestsOptions`, `PaginateRequestsOptions`, `BatchAddRequestsOptions`, `RequestQueueClientOptions`, `TaskStartOptions`, `ValidateInputOptions`, `LogOptions`, `ListOptions`, `StorageListOptions`, `StoreListOptions`. | -| `Apify.Client.Exceptions` | `ApifyClientException` (common base), `ApifyApiException` (and its per-status subclasses), `ApifyTransportException`, `InvalidResponseBodyException`, `ResponseValidationException` and `ActorServerException`. | -| `Apify.Client.Http` | The replaceable transport: `IHttpTransport` and the default `HttpClientTransport`. | -| `System.Text.Json.Nodes` (BCL) | Not an Apify namespace, but required whenever you name the JSON escape-hatch types the client returns/accepts — `JsonObject`/`JsonNode` (e.g. dataset items, `GetInputAsync`/`UpdateInputAsync`, `GetStatisticsAsync`, `GetOpenApiDefinitionAsync`, `MonthlyUsageAsync`/`LimitsAsync`, `RequestQueueRequest.UserData`). Add `using System.Text.Json.Nodes;`. | - -Fluent chains such as `client.Actor("id").Builds()` compile with only `using Apify.Client;` because the -intermediate types are inferred. You only need `using Apify.Client.Resources;` when you name a resource -client type explicitly — e.g. storing one in a variable or field: +| `Apify.Client` | `ApifyClient`, `ApifyClientOptions`, `ApifyClientVersion`, `RequestCompression`, `Statistics` and `StreamedLog`. | +| `Apify.Client.Resources` | The resource clients the entry point returns (`ActorClient`, `RunClient`, `DatasetClient`, the `…CollectionClient` types, …). | +| `Apify.Client.Models` | The models the clients return (`Actor`, `ActorRun`, `Build`, `PaginationList`, …). | +| `Apify.Client.Options` | The option objects passed to methods (`ActorStartOptions`, `ListOptions`, `SetRecordOptions`, …). | +| `Apify.Client.Exceptions` | `ApifyClientException` and its subclasses. | +| `Apify.Client.Http` | `IHttpTransport`, `HttpClientTransport` and the request body compressors. | +| `System.Text.Json.Nodes` | Not an Apify namespace, but needed to name `JsonObject` and `JsonNode`, which the clients use for dataset items, inputs and other free-form data. | + +Fluent chains such as `client.Actor("id").Builds()` need only `using Apify.Client;`. Add +`using Apify.Client.Resources;` to name a resource client type, for example in a variable: ```csharp using Apify.Client; @@ -111,60 +100,51 @@ var configured = new ApifyClient(new ApifyClientOptions | Option | Default | Meaning | |---|---|---| | `Token` | `null` | API token, sent as a Bearer token. | -| `BaseUrl` | `https://api.apify.com` | API base URL, with or without the `/v2` version path — it's appended only when not already present. | -| `PublicBaseUrl` | `https://api.apify.com` | Base URL used when building public, shareable resource URLs, whatever `BaseUrl` is (as in the reference client). Also accepted with or without `/v2`. | +| `BaseUrl` | `https://api.apify.com` | API base URL, with or without `/v2`. | +| `PublicBaseUrl` | `https://api.apify.com` | Base URL of public, shareable resource URLs, whatever `BaseUrl` is, as in the reference client. | | `MaxRetries` | `8` | Maximum retries for failed requests. | | `MinDelayBetweenRetriesMillis` | `500` | Minimum delay between retries (exponential backoff). | -| `MaxDelayBetweenRetriesMillis` | request timeout | Upper bound on the growing inter-retry delay. | -| `TimeoutSecs` | `360` | The overall per-request cap (the reference client's `timeoutMaxSecs`): every attempt's growing timeout, including a `WithTimeout` override, is clamped to it, regardless of tier. Also the base of the `Long` tier unless `TimeoutLongSecs` is set. | -| `TimeoutLongSecs` | `TimeoutSecs` | Base duration of the `Long` timeout tier: downloads, uploads, streaming. Matches the reference client's `timeoutLongSecs`. | -| `TimeoutShortSecs` | `5` | Base duration of the `Short` timeout tier: simple metadata reads/writes (get/update/delete a resource). | -| `TimeoutMediumSecs` | `30` | Base duration of the `Medium` timeout tier: listing, batch and trigger calls. | +| `MaxDelayBetweenRetriesMillis` | request timeout | Upper bound of the growing delay between retries. | +| `TimeoutSecs` | `360` | The overall per-request cap (the reference client's `timeoutMaxSecs`): every attempt's timeout, a `WithTimeout` override included, is clamped to it. Also the base of the `Long` tier unless `TimeoutLongSecs` is set. | +| `TimeoutLongSecs` | `TimeoutSecs` | Base duration of the `Long` tier: downloads, uploads and streaming. | +| `TimeoutShortSecs` | `5` | Base duration of the `Short` tier: metadata reads and writes. | +| `TimeoutMediumSecs` | `30` | Base duration of the `Medium` tier: listing, batch and trigger calls. | | `UserAgentSuffix` | `null` | Custom suffix appended to the `User-Agent` header. | -| `RequestCompression` | `RequestCompression.Brotli` | Algorithm used to compress request bodies ≥ 1024 bytes: `Brotli` (`Content-Encoding: br`) or `Gzip` (`Content-Encoding: gzip`). | -| `Compressor` | `null` | A custom `HttpCompressor` for a non-default compression quality or another algorithm; takes precedence over `RequestCompression`. | -| `Headers` | `null` | Additional headers sent with every request, such as an integration platform marker. They win over the built-in `User-Agent` and `Authorization`; header names are case-insensitive, and `client.UserAgent` reports a `User-Agent` override. | +| `RequestCompression` | `RequestCompression.Brotli` | Algorithm used to compress request bodies ≥ 1024 bytes: `Brotli` (`Content-Encoding: br`), `Gzip` (`Content-Encoding: gzip`) or `Zstd` (`Content-Encoding: zstd`). | +| `Compressor` | `null` | A custom `HttpCompressor` for another quality or algorithm; takes precedence over `RequestCompression`. | +| `Headers` | `null` | Headers sent with every request, such as an integration marker. They win over the built-in `User-Agent` and `Authorization`; names are case-insensitive. A content header such as `Content-Type` makes the constructor throw. | | `HttpTransport` | `HttpClientTransport` | The replaceable transport (`Apify.Client.Http.IHttpTransport`). | -Requests are retried on network errors, HTTP 429 (rate limit) and 5xx responses, with exponential -backoff and jitter. 4xx responses (other than 429) are thrown immediately as `ApifyApiException`. +Requests are retried with exponential backoff and jitter on network errors, 429 and 5xx responses. Other 4xx +responses throw `ApifyApiException` at once. ### Request statistics -`client.Stats` counts what the client has done, as the reference client's `stats` does: `Calls` (client calls), -`Requests` (HTTP requests, including retries) and `RateLimitErrors` (a snapshot list of the `429` answers per -attempt: index 0 holds the first-attempt errors, index 1 the first retry's, and so on). +`client.Stats` counts `Calls`, `Requests` (including retries) and `RateLimitErrors` (the `429` answers per attempt: +index 0 is the first attempt, index 1 the first retry, and so on), as the reference client's `stats` does. ### Timeout tiers -Each method internally picks the timeout tier that fits the expected duration of its request — `Short` -for metadata reads and writes, `Medium` for listing/batch/trigger calls, `Long` for downloads, uploads -and streaming — so the default wait before giving up is consistent across the client instead of a -single one-size-fits-all budget. The three tiers' durations are configured once, on the options above -(`TimeoutShortSecs`/`TimeoutMediumSecs`/`TimeoutLongSecs`); `TimeoutSecs` is the cap that none of them can exceed. - -Every resource client (`client.Actor(id)`, `client.Dataset(id)`, `client.Actors()`, and so on — the -return value of every accessor on `ApifyClient`) exposes `WithTimeout(TimeSpan timeout)`, a general -per-call(-instance) override: call it right after the accessor to make every call made through that -client instance use `timeout` instead of its tier default, for exactly one use of that client, e.g. -`client.Dataset(id).WithTimeout(TimeSpan.FromMinutes(5)).DownloadItemsAsync(...)`. A value above -`TimeoutSecs` (the overall budget) is capped at it, matching the reference client — raise `TimeoutSecs` -itself for a longer per-call timeout; a shorter override still narrows the cap, as before. Pass -`TimeSpan.Zero` for no timeout at all, matching the reference client's `'noTimeout'`. `RequestQueueClient` -(via `client.RequestQueue(id, new RequestQueueClientOptions { -TimeoutSecs = ... })`) and `SetRecordAsync` (via `SetRecordOptions.TimeoutSecs`) already had their own -equivalent, narrower overrides before `WithTimeout` existed and keep them instead. +Each method picks the timeout tier that fits its request: `Short` for metadata reads and writes, `Medium` for +listing, batch and trigger calls, `Long` for downloads, uploads and streaming. The tier durations are set by +`TimeoutShortSecs`, `TimeoutMediumSecs` and `TimeoutLongSecs`; `TimeoutSecs` is the cap none can exceed. + +Every resource client has `WithTimeout(TimeSpan timeout)`, which overrides the tier default for the calls made +through that client instance, for example `client.Dataset(id).WithTimeout(TimeSpan.FromMinutes(5)).DownloadItemsAsync(...)`. +A value above `TimeoutSecs` is capped at it, as in the reference client, and `TimeSpan.Zero` means no timeout. +`RequestQueueClient` (`RequestQueueClientOptions.TimeoutSecs`) and `SetRecordAsync` (`SetRecordOptions.TimeoutSecs`) +have their own narrower overrides. ### Request body compression -Request bodies of at least 1024 bytes are compressed before sending, unless their content type already -carries its own compression (images, audio, video, archives). Brotli is used by default; set -`RequestCompression = RequestCompression.Gzip` to send gzip-compressed bodies instead. +Bodies of at least 1024 bytes are compressed before sending, unless their content type already carries its own +compression (images, audio, video, archives). Brotli is the default; set `RequestCompression` to `Gzip` or `Zstd` +to use those. -For a different quality, pass a compressor: `BrotliHttpCompressor` takes a quality from 0 (the fastest) to 11 -(the best compression, default 6) and `GzipHttpCompressor` a `System.IO.Compression.CompressionLevel` -(`Fastest`, `Optimal` or `SmallestSize`; default `Optimal`). Derive from `HttpCompressor` to use another -algorithm the API accepts (`deflate`, `identity`): set `ContentEncoding` and implement `CompressAsync`. +For another quality, pass a compressor: `BrotliHttpCompressor` takes a quality from 0 to 11 (default 6), +`GzipHttpCompressor` a `System.IO.Compression.CompressionLevel` (default `Optimal`) and `ZstdHttpCompressor` a +quality from 1 to 19 (default 3). To use another encoding the API accepts (`deflate`, `identity`), derive from +`HttpCompressor`: set `ContentEncoding` and implement `CompressAsync`. ```csharp using Apify.Client; @@ -177,18 +157,10 @@ var client = new ApifyClient(new ApifyClientOptions }); ``` -### Default headers - -`ApifyClientOptions.Headers` adds headers to every request, for example an integration platform marker. They -replace the built-in `User-Agent` and `Authorization` headers when named the same, compared -case-insensitively. Only request headers are allowed; a content header such as `Content-Type` makes the -`ApifyClient` constructor throw, because the client sets those per request. - ### Replaceable HTTP transport -The transport is `Apify.Client.Http.IHttpTransport`. The default is `HttpClientTransport`, which wraps -`System.Net.Http.HttpClient`; you can pass a pre-configured `HttpClient` (proxy, TLS, connection pool) -or provide your own `IHttpTransport` (e.g. a mock in tests): +The default `HttpClientTransport` wraps `HttpClient`. Pass a configured `HttpClient` (proxy, TLS, connection +pool) or your own `IHttpTransport`, for example a mock in tests: ```csharp using System.Net.Http; @@ -203,20 +175,18 @@ var client = new ApifyClient(new ApifyClientOptions }); ``` -Requests to an Actor's server (`client.Actor(id).Server()`, see [Actors](actors.md)) must not follow -redirects, because they carry the API token in a header. They set -`Apify.Client.Http.ApifyRequestOptions.FollowRedirects` to `false` in `HttpRequestMessage.Options`, and a -custom `IHttpTransport` has to return a redirect response as it is for such a request. `HttpClientTransport` -does this with a second `HttpClient` that has automatic redirects disabled: it creates one itself, but a -transport wrapping your own `HttpClient` needs one too, passed as the second constructor argument +Requests to an Actor's server (`client.Actor(id).Server()`, see [Actors](actors.md)) carry the API token in a +header, so they must not follow redirects. They set `ApifyRequestOptions.FollowRedirects` to `false` in +`HttpRequestMessage.Options`, and a custom `IHttpTransport` has to return a redirect response as it is. +`HttpClientTransport` sends them through a second `HttpClient` with automatic redirects disabled. It creates one +itself; a transport wrapping your own `HttpClient` needs one passed as the second constructor argument (`new HttpClientTransport(httpClient, noRedirectHttpClient)`), otherwise such a request throws `InvalidOperationException`. ## Error handling -Methods that fetch a single resource return `null` when the resource does not exist (rather than -throwing). Other API failures are thrown as `Apify.Client.Exceptions.ApifyApiException`, which exposes -the HTTP status, API error `Type`, message, attempt count, and request method/path: +Methods that fetch a single resource return `null` when it does not exist. Other API failures throw +`ApifyApiException`: ```csharp using System; @@ -240,28 +210,23 @@ catch (ApifyApiException e) |---|---|---| | `StatusCode` | `int` | HTTP status code of the error response. | | `Type` | `string?` | Machine-readable API error type (e.g. `record-not-found`). | -| `ApiMessage` | `string` | Raw error message from the API, without the status/type prefix. | +| `ApiMessage` | `string` | Error message from the API, without the status and type prefix. | | `Attempt` | `int` | 1-based number of the API-call attempt that produced the error. | | `HttpMethod` | `string` | HTTP method of the failed call (e.g. `GET`, `POST`). | | `Path` | `string` | API endpoint path (URL excluding origin). | | `ErrorData` | `JsonObject?` | Additional structured error data from the API, if any. | -`ApifyTransportException` is thrown instead when the request never reaches the API (network failure, -timeout, or DNS error) after all retries are exhausted. - -`InvalidResponseBodyException` is thrown when a successful response's body is not valid JSON, and -`ResponseValidationException` when it is valid JSON but does not match the schema the API specification -declares for that operation (for example the `data` envelope or a required property is missing, or a -property has the wrong JSON type). Unknown properties and enum values are accepted, so new API fields do -not break the client. The response arrived in full, so neither is -retried. Both expose `StatusCode`, `HttpMethod`, `Path` and the raw `Body`. An error status with an -unparsable body throws the `ApifyApiException` for its status code, with the raw body in its message. - -`ApifyApiException`, `ApifyTransportException`, `InvalidResponseBodyException`, -`ResponseValidationException` and `ActorServerException` (an Actor that is not a Server Actor, see -[Actors](actors.md)) extend the common -`ApifyClientException`, so `catch (ApifyClientException)` catches every error this client raises for a -failed call without naming the concrete types: +The exception types, all deriving from `ApifyClientException` (so `catch (ApifyClientException)` catches them all): + +- `ApifyApiException`: the API answered with an error status. Per-status subclasses (`NotFoundException`, `RateLimitException`, …) let you catch one kind. +- `ApifyTransportException`: no response (network failure, timeout, DNS) after all retries. +- `InvalidResponseBodyException`: a successful response whose body is not JSON. +- `ResponseValidationException`: a successful response whose JSON does not match the specification's schema for the operation (a missing `data` envelope or required property, a wrong type). Unknown properties and enum values are accepted, so new API fields do not break the client. +- `ActorServerException`: an Actor that is not a Server Actor (see [Actors](actors.md)). + +`InvalidResponseBodyException` and `ResponseValidationException` are not retried and expose `StatusCode`, `HttpMethod`, +`Path` and the raw `Body`. An error status with an unparsable body throws the `ApifyApiException` of its status, with +the body in its message. ```csharp using System; diff --git a/docs/actors.md b/docs/actors.md index 7997d75..222ab68 100644 --- a/docs/actors.md +++ b/docs/actors.md @@ -42,17 +42,15 @@ foreach (var actor in page.Items) - `DeleteAsync()`. - `StartAsync(object? input = null, ActorStartOptions? options = null)` → `ActorRun` (returns immediately). - `CallAsync(object? input = null, ActorStartOptions? options = null, int? waitSecs = null, Action? log = null)` - → `ActorRun` (starts then waits; `waitSecs` bounds the wait, `null` waits indefinitely; `log`, if set, - redirects the run's live log to that sink for the duration of the wait; unlike the reference client, - omitting it does not redirect the log, so pass `StreamedLog.DefaultConsole` to get the reference's - default redirection to the console, with its ` runId: -> ` prefix). Time spent retrying - `ActorStartOptions.WaitForResources` does not count toward `waitSecs`. + → `ActorRun` (starts, then waits). `waitSecs` bounds the wait (`null` waits indefinitely) and does not count the + time spent retrying `WaitForResources`. `log` redirects the run's live log to that sink while waiting. Unlike + the reference client, omitting it redirects nothing; pass `StreamedLog.DefaultConsole` for the reference's + console redirection with the ` runId: -> ` prefix. - `ValidateInputAsync(object? input = null, ValidateInputOptions? options = null)` → `bool`. -`input` on `StartAsync`/`CallAsync`/`ValidateInputAsync` (and `RunClient.MetamorphAsync`, see -[Runs](runs.md)) is a plain object or array serialized to JSON, or raw bytes (`byte[]`) sent as-is — -pass `byte[]` together with `ActorStartOptions.ContentType` to send a pre-serialized or binary body -without re-encoding it as JSON. +`input` of `StartAsync`, `CallAsync`, `ValidateInputAsync` and `RunClient.MetamorphAsync` (see [Runs](runs.md)) is +serialized to JSON, except `byte[]`, which is sent as it is. Pair `byte[]` with `ActorStartOptions.ContentType` to +send a pre-serialized or binary body. - `BuildAsync(string versionNumber, ActorBuildOptions? options = null)` → `Build`. - `DefaultBuildAsync(int? waitForFinish = null)` → `BuildClient`. - `LastRun(LastRunOptions? options = null)` → `RunClient` (filter by `Status`/`Origin`). @@ -73,7 +71,7 @@ without re-encoding it as JSON. | `MaxTotalChargeUsd` | `double?` | Maximum total USD the run is allowed to charge. | | `ContentType` | `string?` | Content type of the `input` body (defaults to `application/json`). | | `RestartOnError` | `bool?` | Automatically restart the run's container if it exits with an error. | -| `WaitForResources` | `WaitForResources?` | Retry the start while the account has no free memory or concurrent-run slot for it, instead of throwing immediately. `WaitForResources.UntilAvailable` retries until the run starts; `WaitForResources.For(seconds)` bounds the retrying, after which the last error is thrown. `null` (the default) disables retrying. | +| `WaitForResources` | `WaitForResources?` | Retry the start while the account has no free memory or concurrent-run slot, instead of throwing. `WaitForResources.UntilAvailable` retries until the run starts; `WaitForResources.For(seconds)` bounds the retrying, then throws the last error. `null` (the default) does not retry. | | `ForcePermissionLevel` | `string?` | Override the Actor's permission level (`LIMITED_PERMISSIONS`/`FULL_PERMISSIONS`). | | `Webhooks` | `object?` | Ad-hoc webhooks (any JSON-serializable list) to attach to this run. | @@ -98,15 +96,13 @@ against) and `ContentType` (`string?`, content type of the input; defaults to `a ## Server Actors — `client.Actor(id).Server()` -A Server Actor (formerly Actor Standby) runs a web server that is called over HTTP without starting a run -for each call. `Server()` returns an `ActorServerClient`; every call returns the same client, so the server -URL it fetches is reused. +A Server Actor (formerly Actor Standby) runs a web server that is called over HTTP without starting a run per +call. `Server()` returns an `ActorServerClient`; every call returns the same client, which reuses the server URL. - `RequestAsync(string path = "/", ActorServerRequestOptions? options = null)` → `ActorServerResponse` — sends a request to the Actor's server and returns its response, whatever its status. -- `GetUrlAsync()` → `string` — the server URL (the Actor's `standbyUrl`), to call the server with another - HTTP client. Fetched from the API on the first call of `GetUrlAsync`, `GetOpenApiSpecAsync` or - `RequestAsync`, then reused. +- `GetUrlAsync()` → `string` — the server URL (the Actor's `standbyUrl`), to call the server with another HTTP + client. The first call of `GetUrlAsync`, `GetOpenApiSpecAsync` or `RequestAsync` fetches it, then it is reused. - `GetOpenApiSpecAsync()` → `JsonObject?` — the web server schema (OpenAPI specification) of the build that serves the server: `actorStandby.build` (a tag or a build number) or `latest`; `null` if the definition has none. @@ -121,16 +117,15 @@ second one that does not follow redirects (see `RequestAsync` details: -- The request goes to the server URL, and `path` is always resolved against it (a fragment is dropped), so - neither the request nor the token can reach another host. -- The token travels in the `X-Apify-Authorization` header, which the platform removes before the request - reaches the Actor's code. Headers you pass, `Authorization` included, reach the Actor as they are. -- Any response is returned, error statuses included. Only 429 and 503 are retried (with the client's retry - settings, returning the last response when they run out), because other statuses can come from the Actor's - code. Network errors and timeouts are not retried, since the Actor may have already handled the request, and - a redirect is returned as it is instead of followed. -- Bodies are sent uncompressed unless `Compressor` is set. The timeout defaults to the `Long` tier, which - covers the cold start of an idle Actor. +- `path` is resolved against the server URL (a fragment is dropped), so neither the request nor the token can + reach another host. +- The token travels in the `X-Apify-Authorization` header, which the platform removes before the request reaches + the Actor's code. Headers you pass, `Authorization` included, reach the Actor as they are. +- Any response is returned, error statuses included. Only 429 and 503 are retried (the last response is returned + when the retries run out), because other statuses can come from the Actor's code. Network errors and timeouts + are not retried, since the Actor may have handled the request, and a redirect is returned instead of followed. +- Bodies are sent uncompressed unless `Compressor` is set. The timeout defaults to the `Long` tier, which covers + the cold start of an idle Actor. `ActorServerRequestOptions` fields: @@ -141,6 +136,7 @@ second one that does not follow redirects (see | `Headers` | `IReadOnlyDictionary?` | Additional request headers. A `Content-Type` header equals `ContentType`; a `Content-Encoding` header marks a body that is already encoded and is then not compressed; other `Content-*` headers (e.g. `Content-Disposition`) are sent only with a `Body`. | | `Body` | `object?` | Request body: `byte[]` and `string` are sent as they are, anything else is serialized to JSON. | | `ContentType` | `string?` | Content type of the body; defaults to `application/json`, `text/plain; charset=utf-8` for a string and `application/octet-stream` for bytes. | +| `ParentRequestId` | `string?` | The `X-Actor-Request-ID` header of the request your multi-tenant Server Actor is handling, sent on as the same header so the called Actor charges at that request's pricing tier. Replaces an `X-Actor-Request-ID` in `Headers`. Use a client created with the run's `APIFY_TOKEN`. | | `Compressor` | `HttpCompressor?` | Compressor of the body (e.g. `new GzipHttpCompressor()`). Set it only if the Actor's server decodes the resulting `Content-Encoding`. | | `StreamResponse` | `bool` | Return the response body unread, to read it with `ReadAsStreamAsync`. The response holds its connection until disposed. | | `Timeout` | `TimeSpan?` | Timeout of the request; defaults to the `Long` tier. | diff --git a/docs/examples.md b/docs/examples.md index 3b0be00..8802df8 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -1,15 +1,11 @@ # Examples -Each example below is a complete, runnable scenario. The canonical, compiled versions live in the -client's source repository under +Each example is a complete scenario. Compiled versions live in the repository under [`tests/Apify.Client.Tests/Examples`](https://github.com/apify/apify-client-dotnet/tree/master/tests/Apify.Client.Tests/Examples) -— a repo-internal path that is not part of the published NuGet package — and are executed end-to-end against the live API by the -**Test examples** CI step (they require an `APIFY_TOKEN`), so the snippets here are guaranteed to stay -valid and working. +and run against the live API in the **Test examples** CI step, so they stay working. They need an `APIFY_TOKEN`. -Every snippet runs inside an `async` context and assumes the following `using` directives appear at the -top of the file, **before** any top-level statements (a `using` after the first statement is a `CS1529` -compile error). `ImplicitUsings` is disabled in this repository, so even `System` is listed explicitly: +Every snippet runs in an `async` context after these directives, which must come before any top-level statement. +`System` is listed because `ImplicitUsings` is disabled: ```csharp using System; @@ -26,10 +22,10 @@ var client = new ApifyClient( ## Run a store Actor and read its dataset ```csharp -// The third argument bounds the wait in seconds (120 here); pass null to wait indefinitely. +// The third argument bounds the wait in seconds; null waits indefinitely. var run = await client.Actor("apify/hello-world").CallAsync(null, null, 120); var items = await client.Dataset(run.DefaultDatasetId!).ListItemsAsync(new DatasetListItemsOptions()); -// Items.Count is the number of items in THIS page; Total is the dataset's full count across all pages. +// Items.Count is the number of items on this page; Total is the count across all pages. Console.WriteLine($"Items on this page: {items.Items.Count} (of {items.Total} total)"); ``` diff --git a/docs/runs.md b/docs/runs.md index 41f6621..040fd04 100644 --- a/docs/runs.md +++ b/docs/runs.md @@ -11,10 +11,10 @@ Access the account-wide run collection with `client.Runs()`, an Actor's or task' `ListOptions` fields: `Offset`, `Limit`, `Desc` (standard pagination; `Limit` `0` or unset means no cap) and `ChunkSize` (the page size `IterateAsync` requests; the smaller of it and the remaining `Limit` is used per call). `RunListOptions` fields: `Status` (`IReadOnlyList?`, filter by one or more run statuses such as `SUCCEEDED`/`RUNNING`), -`Deployment` (`IReadOnlyList?`, filters Actor Standby runs by the pool serving them: `SINGLE_TENANT`, +`Deployment` (`IReadOnlyList?`, filters Server Actor runs by the pool that serves them: `SINGLE_TENANT`, `MULTI_TENANT_FREE_USERS`, `MULTI_TENANT_PAYING_USERS` or `MULTI_TENANT_OWNER_PAID`; requires `Status` with only -non-terminal statuses and is honoured only by the account-wide and Actor-scoped run collections), -`StartedAfter` and `StartedBefore` (ISO 8601 bounds, honoured only by the account-wide and Actor-scoped run +non-terminal statuses and is honored only by the account-wide and Actor-scoped run collections), +`StartedAfter` and `StartedBefore` (ISO 8601 bounds, honored only by the account-wide and Actor-scoped run collections, not by task runs). ## Single run — `client.Run(runId)` diff --git a/docs/storages.md b/docs/storages.md index f81802f..76412d6 100644 --- a/docs/storages.md +++ b/docs/storages.md @@ -7,8 +7,7 @@ shape: `ListAsync(StorageListOptions? options = null)` (one page), optional `JsonNode? schema = null` to register a storage schema on creation). Storages can also be reached from a run (`client.Run(id).Dataset()`, `.KeyValueStore()`, `.RequestQueue()`). -> Snippets below run inside an `async` context. `ImplicitUsings` is disabled in this repository, so all -> `using` directives (including `System`) are shown explicitly and must precede any statements. +> Snippets run in an `async` context with `ImplicitUsings` disabled, so every `using`, `System` included, is listed. `StorageListOptions` fields: @@ -154,7 +153,7 @@ await client.KeyValueStore(store.Id!).SetRecordAsync("blob", new byte[] { 0x00, ### Uploading a pre-compressed record `SetRecordOptions.ContentEncoding` uploads a value you already compressed yourself (`gzip`, `br`, -`deflate`, or `identity`), sent verbatim as the `Content-Encoding` header: the client sends the bytes +`zstd`, `deflate`, or `identity`), sent verbatim as the `Content-Encoding` header: the client sends the bytes as-is and skips its own compression, so they are never compressed twice. ```csharp diff --git a/src/Apify.Client/Apify.Client.csproj b/src/Apify.Client/Apify.Client.csproj index 9d450ce..a2cc56c 100644 --- a/src/Apify.Client/Apify.Client.csproj +++ b/src/Apify.Client/Apify.Client.csproj @@ -6,7 +6,7 @@ Apify.Client - 0.9.0 + 0.10.0 Apify Apify Apify API client for .NET @@ -35,4 +35,8 @@ + + + + diff --git a/src/Apify.Client/ApifyClient.cs b/src/Apify.Client/ApifyClient.cs index 3f4bcde..6b28958 100644 --- a/src/Apify.Client/ApifyClient.cs +++ b/src/Apify.Client/ApifyClient.cs @@ -12,24 +12,13 @@ namespace Apify.Client; /// -/// The entry point for interacting with the Apify API. +/// The entry point of the client: create it with an API token (and optionally ), then +/// get resource clients from its accessors, such as , and . /// /// -/// -/// See the project's top-level README for the client's official-but-experimental status. -/// -/// -/// Construct it with an API token (and optional settings via ), then -/// obtain resource clients via the accessor methods, e.g. , , -/// . -/// -/// -/// Architecture. The public interface is this class and the resource clients it returns. The -/// replaceable transport is the (default ); -/// pass a custom one via . Cross-cutting behaviour (auth, -/// User-Agent, retries with exponential backoff, timeouts) lives in the internal HTTP client and is -/// applied to every request. -/// +/// Authentication, the User-Agent header, retries with exponential backoff and timeouts are applied to every +/// request. Pass a custom in to replace +/// the default . /// public sealed class ApifyClient { @@ -51,10 +40,10 @@ public sealed class ApifyClient /// Default duration of the Medium timeout tier, in seconds. public const int DefaultTimeoutMediumSecs = 30; - /// Environment variable that signals the client is running on the Apify platform. + /// Set to a non-empty value when the client runs on the Apify platform. private const string EnvIsAtHome = "APIFY_IS_AT_HOME"; - /// Environment variable holding the current Actor run's id (set on the platform). + /// The ID of the current Actor run, set on the platform. private const string EnvActorRunId = "ACTOR_RUN_ID"; /// Addresses the current user (/users/me). @@ -97,8 +86,8 @@ public ApifyClient(ApifyClientOptions options) } /// - /// The compressor for large request bodies: the explicit , or the - /// built-in one for with its default quality. + /// The compressor for large request bodies: , or the built-in one for + /// . /// private static HttpCompressor ResolveCompressor(ApifyClientOptions options) { @@ -107,18 +96,21 @@ private static HttpCompressor ResolveCompressor(ApifyClientOptions options) return options.Compressor; } - return options.RequestCompression == RequestCompression.Gzip ? new GzipHttpCompressor() : new BrotliHttpCompressor(); + return options.RequestCompression switch + { + RequestCompression.Gzip => new GzipHttpCompressor(), + RequestCompression.Zstd => new ZstdHttpCompressor(), + _ => new BrotliHttpCompressor(), + }; } - /// The /v2 API version path, appended to a base URL that does not already end with it. + /// The API version path, appended to a base URL that does not end with it. private const string ApiVersionPath = "/v2"; /// - /// Normalizes a caller-supplied base URL into one ending in the API version path: trailing slashes are - /// stripped, and is appended unless the URL already ends with it (so passing - /// https://api.apify.com/v2 does not double up into .../v2/v2). Only an exact /v2 - /// suffix counts; another version segment (e.g. /v3) is treated as part of the host path and still - /// gets /v2 appended, since the client's endpoints only exist under v2. + /// Normalizes a base URL to end with : trailing slashes are stripped and the path is + /// appended unless the URL already ends with /v2. Another version segment such as /v3 is kept as part of + /// the host path, since the endpoints only exist under v2. /// private static string ToApiBaseUrl(string url) { @@ -127,22 +119,17 @@ private static string ToApiBaseUrl(string url) } /// - /// The User-Agent header value this client sends: the built-in one, or the User-Agent entry of - /// (matched case-insensitively) when it overrides it. + /// The User-Agent this client sends: the built-in one, or the User-Agent entry of + /// when it overrides it. /// public string UserAgent => _http.UserAgent; - /// - /// Counters of the calls and requests this client has made and of the rate limit errors it has met, as in - /// the reference client's stats. - /// + /// Counters of the calls and requests this client made and the rate limit errors it met. public Statistics Stats => _stats; - /// The fully-qualified API base URL this client targets (including the /v2 suffix). + /// The API base URL this client targets, including the /v2 suffix. public string ApiBaseUrl => _baseUrl; - // ----- Actor accessors ----------------------------------------------------- - /// A client for the Actor collection (list & create Actors). public ActorCollectionClient Actors() => new(_http, _baseUrl); @@ -150,8 +137,6 @@ private static string ToApiBaseUrl(string url) /// The Actor ID or username~name. public ActorClient Actor(string id) => new(this, _http, _baseUrl, RequireNonEmptyId(id, nameof(id))); - // ----- Build accessors ----------------------------------------------------- - /// A client for the Actor build collection (list builds). public BuildCollectionClient Builds() => new(_http, _baseUrl, "actor-builds"); @@ -159,8 +144,6 @@ private static string ToApiBaseUrl(string url) /// The build ID. public BuildClient Build(string id) => new(_http, _baseUrl, RequireNonEmptyId(id, nameof(id))); - // ----- Run accessors ------------------------------------------------------- - /// A client for the Actor run collection (list runs). public RunCollectionClient Runs() => new(_http, _baseUrl, "actor-runs"); @@ -168,8 +151,6 @@ private static string ToApiBaseUrl(string url) /// The run ID. public RunClient Run(string id) => new(this, _http, _baseUrl, "actor-runs", RequireNonEmptyId(id, nameof(id))); - // ----- Dataset accessors --------------------------------------------------- - /// A client for the dataset collection (list & get-or-create datasets). public DatasetCollectionClient Datasets() => new(_http, _baseUrl); @@ -177,8 +158,6 @@ private static string ToApiBaseUrl(string url) /// The dataset ID or name. public DatasetClient Dataset(string id) => DatasetClient.ForId(_http, _baseUrl, RequireNonEmptyId(id, nameof(id))).WithPublicBase(_publicBaseUrl); - // ----- Key-value store accessors ------------------------------------------- - /// A client for the key-value store collection. public KeyValueStoreCollectionClient KeyValueStores() => new(_http, _baseUrl); @@ -186,23 +165,18 @@ private static string ToApiBaseUrl(string url) /// The store ID or name. public KeyValueStoreClient KeyValueStore(string id) => KeyValueStoreClient.ForId(_http, _baseUrl, RequireNonEmptyId(id, nameof(id))).WithPublicBase(_publicBaseUrl); - // ----- Request queue accessors --------------------------------------------- - /// A client for the request queue collection. public RequestQueueCollectionClient RequestQueues() => new(_http, _baseUrl); /// - /// A client for a specific request queue, addressed by ID or name. Must not be empty. Optionally pass - /// options to set a stable ClientKey and/or a per-request TimeoutSecs for this queue's - /// calls. + /// A client for a specific request queue, addressed by ID or name. Must not be empty. The options set a stable + /// ClientKey and a per-request TimeoutSecs for this queue's calls. /// /// The queue ID or name. /// Optional per-queue-client options. public RequestQueueClient RequestQueue(string id, Options.RequestQueueClientOptions? options = null) => RequestQueueClient.ForId(_http, _baseUrl, RequireNonEmptyId(id, nameof(id)), options); - // ----- Task accessors ------------------------------------------------------ - /// A client for the Actor task collection (list & create tasks). public TaskCollectionClient Tasks() => new(_http, _baseUrl); @@ -210,8 +184,6 @@ public RequestQueueClient RequestQueue(string id, Options.RequestQueueClientOpti /// The task ID. public TaskClient Task(string id) => new(this, _http, _baseUrl, RequireNonEmptyId(id, nameof(id))); - // ----- Schedule accessors -------------------------------------------------- - /// A client for the schedule collection (list & create schedules). public ScheduleCollectionClient Schedules() => new(_http, _baseUrl); @@ -219,8 +191,6 @@ public RequestQueueClient RequestQueue(string id, Options.RequestQueueClientOpti /// The schedule ID. public ScheduleClient Schedule(string id) => new(_http, _baseUrl, RequireNonEmptyId(id, nameof(id))); - // ----- Webhook accessors --------------------------------------------------- - /// A client for the webhook collection (list & create webhooks). public WebhookCollectionClient Webhooks() => new(_http, _baseUrl); @@ -235,8 +205,6 @@ public RequestQueueClient RequestQueue(string id, Options.RequestQueueClientOpti /// The dispatch ID. public WebhookDispatchClient WebhookDispatch(string id) => new(_http, _baseUrl, RequireNonEmptyId(id, nameof(id))); - // ----- Misc accessors ------------------------------------------------------ - /// A client for browsing the Apify Store. public StoreCollectionClient Store() => new(_http, _baseUrl); @@ -252,17 +220,13 @@ public RequestQueueClient RequestQueue(string id, Options.RequestQueueClientOpti public UserClient User(string id) => new(_http, _baseUrl, RequireNonEmptyId(id, nameof(id))); /// - /// Sets the status message of the current Actor run. + /// Sets the status message of the current Actor run, the one in the ACTOR_RUN_ID environment variable. A + /// terminal message is final and is not overwritten. /// - /// - /// This convenience method updates the run identified by the ACTOR_RUN_ID environment variable, - /// so it only works when called from inside an Actor run. If is true, the - /// message becomes final and won't be overwritten. Throws if - /// ACTOR_RUN_ID is not set. - /// /// The status message to set. /// Whether the message is final. /// A token to cancel the request. + /// ACTOR_RUN_ID is not set. public Task SetStatusMessageAsync(string message, bool isTerminal = false, CancellationToken cancellationToken = default) { var runId = Environment.GetEnvironmentVariable(EnvActorRunId); @@ -282,11 +246,8 @@ public Task SetStatusMessageAsync(string message, bool isTerminal = fa private static string TrimTrailingSlash(string value) => value.TrimEnd('/'); /// - /// Validates that an id/name passed to a resource accessor is non-empty, returning it unchanged so it - /// can be used inline at the call site. An empty id would otherwise flow through - /// into a collection-shaped URL (e.g. an empty run id - /// addressing .../actor-runs/) and silently mis-address the wrong resource instead of failing - /// loudly, matching the reference client's uniform id.min(1) validation on every accessor. + /// Rejects an empty ID, which would otherwise address a collection URL such as .../actor-runs/. Returns the + /// ID so it can be used inline. /// private static string RequireNonEmptyId(string id, string paramName) { @@ -298,10 +259,7 @@ private static string RequireNonEmptyId(string id, string paramName) return id; } - /// - /// Reports whether the client is running on the Apify platform, by reading the APIFY_IS_AT_HOME - /// environment variable (set to a non-empty value on the platform). - /// + /// Whether APIFY_IS_AT_HOME is set to a non-empty value. private static bool DefaultIsAtHome() { var value = Environment.GetEnvironmentVariable(EnvIsAtHome); @@ -309,8 +267,7 @@ private static bool DefaultIsAtHome() } /// - /// Builds the User-Agent header value mandated by the client requirements: - /// ApifyClient/{version} ({os}; .NET/{runtimeVersion}); isAtHome/{true|false}. + /// Builds the User-Agent value ApifyClient/{version} ({os}; .NET/{runtimeVersion}); isAtHome/{true|false}. /// private static string BuildUserAgent(string? suffix, Func isAtHomeFn) { @@ -332,15 +289,12 @@ private static string BuildUserAgent(string? suffix, Func isAtHomeFn) } /// - /// Additional Node os.platform() tokens for Unix platforms that .NET has no dedicated - /// helper for, matched via . - /// Solaris and illumos both report as sunos, matching Node. + /// OS tokens of Unix platforms that has no helper for. Solaris and illumos both + /// report sunos, as in Node. /// /// - /// These entries are deliberately kept even though .NET has no officially supported runtime on these - /// platforms today: the requirement is that every Apify client emit the exact same OS token as the - /// reference client's os.platform(), so should .NET ever run there the token stays aligned - /// rather than degrading to unknown. They are a forward-compatible superset, not live branches. + /// .NET has no supported runtime on these platforms today. They keep the token the same as the reference client's + /// os.platform() should one appear. /// private static readonly (OSPlatform Platform, string Token)[] ExtendedOsTokens = { @@ -351,7 +305,7 @@ private static readonly (OSPlatform Platform, string Token)[] ExtendedOsTokens = (OSPlatform.Create("AIX"), "aix"), }; - /// Resolves the current platform to its User-Agent OS token (see ). + /// The User-Agent OS token of the current platform. private static string CurrentOs() => ResolveOsToken( OperatingSystem.IsWindows(), OperatingSystem.IsMacOS(), @@ -361,15 +315,10 @@ private static string CurrentOs() => ResolveOsToken( RuntimeInformation.IsOSPlatform); /// - /// Maps the detected runtime platform to the short, lowercase token used in the User-Agent OS - /// field. The tokens are exactly the reference JS client's Node os.platform() values - /// (win32, darwin, android, linux, freebsd, openbsd, - /// netbsd, sunos, aix), so the token is identical across Apify clients. Android is - /// checked before Linux because an Android runtime is also Linux-based. The five platforms .NET exposes - /// dedicated helpers for are matched first; the remaining Unix platforms - /// Node names but .NET has no helper for are matched via . Platforms the - /// reference's Node runtime cannot run on at all (e.g. iOS, the browser) have no reference token and so - /// fall back to unknown. + /// Maps the platform to the OS token of the User-Agent: the values of Node's os.platform() (win32, + /// darwin, android, linux, freebsd, openbsd, netbsd, sunos, + /// aix), so the token is the same in every Apify client. Android is checked before Linux, which it is based on. + /// A platform Node does not run on has no token and maps to unknown. /// internal static string ResolveOsToken( bool isWindows, diff --git a/src/Apify.Client/ApifyClientOptions.cs b/src/Apify.Client/ApifyClientOptions.cs index 5fd87c0..fe3d1a6 100644 --- a/src/Apify.Client/ApifyClientOptions.cs +++ b/src/Apify.Client/ApifyClientOptions.cs @@ -4,27 +4,22 @@ namespace Apify.Client; -/// -/// Configuration for an . All fields have sensible defaults; set only the ones -/// you need to override. -/// +/// Configuration of an . Set only the fields you need to override. public sealed class ApifyClientOptions { /// API token, sent as a Bearer token. public string? Token { get; set; } /// - /// API base URL, with or without the /v2 API version path: the path is appended when not already - /// present, so https://api.apify.com and https://api.apify.com/v2 are both accepted and - /// resolve to the same . + /// API base URL, with or without the /v2 path (it is appended when missing), so https://api.apify.com and + /// https://api.apify.com/v2 give the same . /// public string BaseUrl { get; set; } = ApifyClient.DefaultBaseUrl; /// - /// Base URL for building public, shareable resource URLs (defaults to , - /// whatever is, as in the reference client, so a client behind an internal or proxied - /// does not hand out internal links). Like , accepted with or - /// without the /v2 API version path. + /// Base URL of public, shareable resource URLs, with or without /v2. Defaults to + /// whatever is, as in the reference client, so a client + /// behind an internal does not hand out internal links. /// public string? PublicBaseUrl { get; set; } @@ -38,30 +33,22 @@ public sealed class ApifyClientOptions public int? MaxDelayBetweenRetriesMillis { get; set; } /// - /// The overall per-request cap, in seconds (default 360), matching the reference client's - /// timeoutMaxSecs: every attempt's growing timeout, including one set with WithTimeout, is - /// clamped to it, regardless of tier. It is also the base of the Long tier unless - /// is set. + /// The overall per-request cap in seconds (default 360), as the reference client's timeoutMaxSecs: every + /// attempt's timeout, including one set with WithTimeout, is clamped to it. It is also the base of the + /// Long tier unless is set. /// public int TimeoutSecs { get; set; } = ApifyClient.DefaultTimeoutSecs; /// - /// Base duration of the Long timeout tier, in seconds: downloads, uploads and streaming. Defaults to - /// . Matches the reference client's timeoutLongSecs; the cap - /// still applies to it. + /// Base duration of the Long timeout tier in seconds: downloads, uploads and streaming. Defaults to + /// , which still caps it. /// public int? TimeoutLongSecs { get; set; } - /// - /// Base duration of the Short timeout tier, in seconds (default 5): simple metadata reads and - /// writes (get/update/delete a resource). Matches the reference client's timeoutShortSecs. - /// + /// Base duration of the Short timeout tier in seconds (default 5): metadata reads and writes. public int TimeoutShortSecs { get; set; } = ApifyClient.DefaultTimeoutShortSecs; - /// - /// Base duration of the Medium timeout tier, in seconds (default 30): listing, batch and trigger - /// calls. Matches the reference client's timeoutMediumSecs. - /// + /// Base duration of the Medium timeout tier in seconds (default 30): listing, batch and trigger calls. public int TimeoutMediumSecs { get; set; } = ApifyClient.DefaultTimeoutMediumSecs; /// Custom suffix appended to the User-Agent header. @@ -69,25 +56,23 @@ public sealed class ApifyClientOptions /// /// Algorithm used to compress large request bodies (default ) with - /// its default quality. Set it to to send gzip-compressed bodies - /// instead. Use to tune the quality or plug in another algorithm. + /// its default quality. Use to tune the quality or plug in another algorithm. /// public RequestCompression RequestCompression { get; set; } = RequestCompression.Brotli; /// - /// A custom request body compressor, for a non-default quality (, - /// ) or another algorithm (derive from ). - /// When set, it takes precedence over . Bodies are compressed only when - /// they are large enough to benefit and their content type does not already carry its own compression. + /// A custom request body compressor, for another quality (, + /// , ) or another algorithm (derive from + /// ). It takes precedence over . Only large bodies without + /// their own compression are compressed. /// public HttpCompressor? Compressor { get; set; } /// - /// Additional headers sent with every request, such as an integration platform marker. They win over the - /// client's built-in defaults (User-Agent, Authorization); header names are compared - /// case-insensitively. Only request headers are allowed: a content header such as Content-Type - /// makes the constructor throw , because the client - /// sets those per request. + /// Additional headers sent with every request, such as an integration marker. They win over the built-in + /// User-Agent and Authorization; names are case-insensitive. A content header such as Content-Type + /// makes the constructor throw , because the client sets + /// those per request. /// public IReadOnlyDictionary? Headers { get; set; } diff --git a/src/Apify.Client/ApifyClientVersion.cs b/src/Apify.Client/ApifyClientVersion.cs index a56fe8b..65cd9a0 100644 --- a/src/Apify.Client/ApifyClientVersion.cs +++ b/src/Apify.Client/ApifyClientVersion.cs @@ -14,11 +14,11 @@ public static class ApifyClientVersion /// 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 string ClientVersion = "0.9.0"; + public const string ClientVersion = "0.10.0"; /// /// The version of the Apify OpenAPI specification this client was generated and verified against. /// Corresponds to the info.version field of the Apify OpenAPI document. /// - public const string ApiSpecVersion = "v2-2026-11-04T151257Z"; + public const string ApiSpecVersion = "v2-2026-11-09T143926Z"; } diff --git a/src/Apify.Client/Exceptions/ActorServerException.cs b/src/Apify.Client/Exceptions/ActorServerException.cs index 4894cb4..df462b8 100644 --- a/src/Apify.Client/Exceptions/ActorServerException.cs +++ b/src/Apify.Client/Exceptions/ActorServerException.cs @@ -1,10 +1,9 @@ namespace Apify.Client.Exceptions; /// -/// Thrown by when the Actor cannot be used as a Server Actor: it does -/// not serve a web server, or the build that should serve its server interface does not exist. A missing Actor -/// is reported as the usual instead. Matches the reference client's -/// ApifyClientError for these cases. +/// Thrown by when the Actor cannot be used as a Server Actor: it has no web +/// server, or the build that should serve its server interface does not exist. A missing Actor throws an +/// instead. /// public sealed class ActorServerException : ApifyClientException { diff --git a/src/Apify.Client/Exceptions/ApifyApiException.cs b/src/Apify.Client/Exceptions/ApifyApiException.cs index 5a0628d..403038b 100644 --- a/src/Apify.Client/Exceptions/ApifyApiException.cs +++ b/src/Apify.Client/Exceptions/ApifyApiException.cs @@ -5,13 +5,11 @@ namespace Apify.Client.Exceptions; -/// -/// Thrown for HTTP requests that reach the Apify API but receive a non-success status code. -/// +/// Thrown when the Apify API answers with a non-success status. /// -/// It mirrors the ApifyApiError of the reference JavaScript client and exposes the parsed error -/// , the human-readable , the HTTP , -/// the number of the final , and the request /. +/// Mirrors the reference client's ApifyApiError: the error , , +/// , the final and the request and +/// . /// public class ApifyApiException : ApifyClientException { @@ -48,7 +46,7 @@ public ApifyApiException( /// The machine-readable error type returned by the API (e.g. record-not-found). public string? Type { get; } - /// The raw error message returned by the API, without the status/type prefix. + /// The error message returned by the API, without the status and type prefix. public string ApiMessage { get; } /// The number of the API call attempt that produced this error (1-based). @@ -60,10 +58,7 @@ public ApifyApiException( /// The path of the API endpoint (URL excluding origin). public string Path { get; } - /// - /// Additional structured data provided by the API about the error, if any. Named ErrorData - /// (not Data) to avoid hiding . - /// + /// Additional structured error data from the API. Not named Data, to avoid hiding . public JsonObject? ErrorData { get; } private static string FormatMessage(int statusCode, string? type, string message) @@ -99,12 +94,9 @@ private static string FormatMessage(int statusCode, string? type, string message private const int StatusServerErrorFloor = 500; /// - /// Creates the subclass matching (see the - /// per-status types in this namespace, e.g. ), so callers can branch with - /// a type check instead of comparing status codes or error-type strings. Any other status falls back to - /// the base ; every subclass still satisfies an - /// is ApifyApiException/catch (ApifyApiException) check, matching the reference client's - /// ApifyApiError subclass hierarchy. + /// Creates the subclass for , such as + /// , so callers can branch on the type. Other statuses give the base type, as in the + /// reference client's ApifyApiError hierarchy. /// internal static ApifyApiException Create( int statusCode, diff --git a/src/Apify.Client/Exceptions/ApifyApiExceptionTypes.cs b/src/Apify.Client/Exceptions/ApifyApiExceptionTypes.cs index 260ed84..3711896 100644 --- a/src/Apify.Client/Exceptions/ApifyApiExceptionTypes.cs +++ b/src/Apify.Client/Exceptions/ApifyApiExceptionTypes.cs @@ -2,11 +2,9 @@ namespace Apify.Client.Exceptions; -// Each subclass below matches one HTTP status group and carries no behavior of its own: construction and -// message formatting stay in the base ApifyApiException. They let a caller branch on the error kind with a -// type check (`catch (NotFoundException)`) instead of comparing StatusCode/Type, matching the reference -// client's and the Python client's ApifyApiError subclass hierarchy. See ApifyApiException.Create, which -// picks the subclass for a given status code. +// Each subclass matches one HTTP status group and adds no behavior; construction and message formatting stay in +// ApifyApiException. Callers can catch the error kind (`catch (NotFoundException)`) instead of comparing the status. +// ApifyApiException.Create picks the subclass. /// Thrown for a 400 Bad Request response: the request was malformed or failed validation. public sealed class InvalidRequestException : ApifyApiException @@ -39,12 +37,9 @@ public ForbiddenException(int statusCode, string? type, string message, int atte } /// -/// Thrown for a 404 Not Found response that could not be resolved to a single, unambiguous missing -/// resource (see remarks on the client's resources). Most resource clients -/// addressed by id resolve an ordinary 404 to null instead of throwing; this type still surfaces for -/// chained clients that have no id of their own (e.g. a run's default dataset/key-value -/// store/request queue/log, or a build's log) and for the sub-path endpoints that always require their -/// parent resource to exist. +/// Thrown for a 404 Not Found response that is not resolved to a missing resource. Clients addressed by ID +/// resolve an ordinary 404 to null. This type surfaces for clients without their own ID (a run's default +/// storages and log, a build's log) and for sub-path endpoints whose parent resource must exist. /// public sealed class NotFoundException : ApifyApiException { @@ -66,9 +61,8 @@ public ConflictException(int statusCode, string? type, string message, int attem } /// -/// Thrown for a 429 Too Many Requests response that exhausted the client's retries. The client -/// already retries a rate-limited request with backoff (see ); this -/// type reaches the caller only once that budget is spent. +/// Thrown for a 429 Too Many Requests response once the client's retries (see +/// ) are spent. /// public sealed class RateLimitException : ApifyApiException { diff --git a/src/Apify.Client/Exceptions/ApifyClientException.cs b/src/Apify.Client/Exceptions/ApifyClientException.cs index 9a65b6a..24aec44 100644 --- a/src/Apify.Client/Exceptions/ApifyClientException.cs +++ b/src/Apify.Client/Exceptions/ApifyClientException.cs @@ -3,15 +3,13 @@ namespace Apify.Client.Exceptions; /// -/// Base class of every error this client throws for a failed API call: -/// (and its per-status subclasses), , -/// and . A single -/// catch (ApifyClientException) catches all of them, matching the reference client's -/// ApifyClientError. +/// Base class of the errors this client throws for a failed API call: and its +/// subclasses, , and +/// . Mirrors the reference client's ApifyClientError. /// /// -/// An invalid argument throws a standard / -/// before any request is sent; neither of those extends this class. +/// An invalid argument throws or before any +/// request is sent; these do not derive from this class. /// public abstract class ApifyClientException : Exception { diff --git a/src/Apify.Client/Exceptions/ApifyTransportException.cs b/src/Apify.Client/Exceptions/ApifyTransportException.cs index c2ab392..e6c0252 100644 --- a/src/Apify.Client/Exceptions/ApifyTransportException.cs +++ b/src/Apify.Client/Exceptions/ApifyTransportException.cs @@ -2,12 +2,10 @@ namespace Apify.Client.Exceptions; -/// -/// Marks a transport-level (network/timeout) failure, which is retryable by the client. -/// +/// A network or timeout failure, which the client retries. /// -/// A custom should throw this for connection, DNS and -/// timeout failures; a non-2xx HTTP status must be returned as a normal response instead. +/// A custom throws this for connection, DNS and timeout failures, and +/// returns a non-2xx status as a response. /// public sealed class ApifyTransportException : ApifyClientException { diff --git a/src/Apify.Client/Exceptions/InvalidResponseBodyException.cs b/src/Apify.Client/Exceptions/InvalidResponseBodyException.cs index 712455d..647b51e 100644 --- a/src/Apify.Client/Exceptions/InvalidResponseBodyException.cs +++ b/src/Apify.Client/Exceptions/InvalidResponseBodyException.cs @@ -3,10 +3,9 @@ namespace Apify.Client.Exceptions; /// -/// Thrown when the body of a successful response does not parse as JSON, for example a proxy's HTML page -/// served with a success status. The response arrived in full, so the request is not retried. An error -/// status with an unparsable body throws the for its status code instead. -/// Matches the reference client's InvalidResponseBodyError. +/// Thrown when the body of a successful response is not JSON, for example a proxy's HTML page. The response arrived in +/// full, so it is not retried. An error status with an unparsable body throws the of its +/// status instead. Mirrors the reference client's InvalidResponseBodyError. /// public sealed class InvalidResponseBodyException : ApifyClientException { diff --git a/src/Apify.Client/Exceptions/ResponseValidationException.cs b/src/Apify.Client/Exceptions/ResponseValidationException.cs index 1b37d0d..58f0b22 100644 --- a/src/Apify.Client/Exceptions/ResponseValidationException.cs +++ b/src/Apify.Client/Exceptions/ResponseValidationException.cs @@ -3,11 +3,10 @@ namespace Apify.Client.Exceptions; /// -/// Thrown when a successful response is valid JSON but does not match the schema the Apify API -/// specification declares for its operation: the data envelope or a required property is missing, a -/// value has the wrong JSON type, is null where that is not allowed, or falls outside the documented -/// format, pattern or range. Unknown properties and unknown enum values are accepted. The response arrived -/// in full, so the request is not retried. Matches the reference client's ResponseValidationError. +/// Thrown when a successful response is valid JSON but does not match the schema the API specification declares for +/// its operation: a missing envelope or required property, a wrong type, a null where it is not allowed, or a +/// value outside the documented format, pattern or range. Unknown properties and enum values are accepted. The response +/// arrived in full, so it is not retried. Mirrors the reference client's ResponseValidationError. /// public sealed class ResponseValidationException : ApifyClientException { diff --git a/src/Apify.Client/Http/ApifyRequestOptions.cs b/src/Apify.Client/Http/ApifyRequestOptions.cs index 833c1dd..b968d55 100644 --- a/src/Apify.Client/Http/ApifyRequestOptions.cs +++ b/src/Apify.Client/Http/ApifyRequestOptions.cs @@ -2,22 +2,17 @@ namespace Apify.Client.Http; -/// -/// Keys of the per-request settings the client passes to an in -/// . -/// +/// Keys of the per-request settings the client passes to an in . /// -/// A custom transport has to honour them, or it can send the API token somewhere it must not go. The built-in +/// A custom transport has to honor them, or it can send the API token where it must not go. The built-in /// does. /// public static class ApifyRequestOptions { /// - /// Set to false on a request that must not follow redirects: the transport returns a redirect - /// response as it is. The client sets it on requests to an Actor's server (see - /// ), because they carry the API token in a header that an - /// automatic redirect would send to a host the Actor's code picked. A request without the option follows - /// redirects as usual. + /// Set to false on a request that must not follow redirects; the transport returns a redirect response as it is. + /// The client sets it on requests to an Actor's server (see ), which carry the + /// token in a header that a redirect would send to a host the Actor's code picked. Without it, redirects are followed. /// public static readonly HttpRequestOptionsKey FollowRedirects = new("Apify.FollowRedirects"); } diff --git a/src/Apify.Client/Http/GzipHttpCompressor.cs b/src/Apify.Client/Http/GzipHttpCompressor.cs index c87c37e..fa2aec6 100644 --- a/src/Apify.Client/Http/GzipHttpCompressor.cs +++ b/src/Apify.Client/Http/GzipHttpCompressor.cs @@ -16,11 +16,9 @@ public sealed class GzipHttpCompressor : HttpCompressor /// Creates a gzip compressor. /// - /// The compression level. .NET exposes zlib's levels as three named values: - /// is level 1, level 6 (the default, as in the reference client) and - /// level 9. + /// The compression level. is zlib level 1, + /// level 6 (the default, as in the reference client) and level 9. /// - /// is not a defined . public GzipHttpCompressor(CompressionLevel level = CompressionLevel.Optimal) { if (!Enum.IsDefined(level)) diff --git a/src/Apify.Client/Http/HttpClientTransport.cs b/src/Apify.Client/Http/HttpClientTransport.cs index bb7fb6f..1164df7 100644 --- a/src/Apify.Client/Http/HttpClientTransport.cs +++ b/src/Apify.Client/Http/HttpClientTransport.cs @@ -7,18 +7,15 @@ namespace Apify.Client.Http; -/// -/// The default , backed by . -/// +/// The default , backed by . /// -/// The per-attempt timeout is applied to each request by the orchestrating client via a linked -/// cancellation token, so the shared is left infinite. Non-2xx statuses -/// are returned as normal responses; only connection/timeout failures are thrown as +/// The client applies the per-attempt timeout with a linked cancellation token, so is +/// infinite. Non-2xx statuses are returned as responses; connection and timeout failures are thrown as /// . /// public sealed class HttpClientTransport : IHttpTransport, IDisposable { - /// Connection-establishment timeout (distinct from the per-request timeout the client applies). + /// Connection-establishment timeout. private static readonly TimeSpan ConnectTimeout = TimeSpan.FromSeconds(30); private readonly HttpClient _httpClient; @@ -27,30 +24,27 @@ public sealed class HttpClientTransport : IHttpTransport, IDisposable private readonly bool _ownsNoRedirectClient; /// - /// Creates a transport, optionally wrapping caller-supplied s (e.g. ones - /// configured with a proxy or custom TLS). For any client that is not supplied, an internal one is created - /// and disposed with this instance. + /// Creates a transport, optionally wrapping caller-supplied s, for example ones configured with + /// a proxy or custom TLS. A client that is not supplied is created and disposed with this instance. /// /// - /// A request that must not follow redirects (see , used for - /// requests to an Actor's server) is sent through the no-redirect client. A client supplied as - /// cannot be switched to that mode, so supply a second one with automatic - /// redirects disabled as , configured like the first one otherwise; - /// without it, such a request throws rather than risk following a - /// redirect with the API token. + /// A request that must not follow redirects (, used for requests to an + /// Actor's server) goes through the no-redirect client. A supplied cannot be switched to + /// that mode, so also supply , configured like it but with automatic redirects + /// disabled. Without it, such a request throws rather than follow a redirect + /// with the API token. /// /// An optional pre-configured HTTP client to use. /// - /// An optional pre-configured HTTP client whose handler does not follow redirects, used for requests that - /// must not follow them. Required for those requests when is supplied. + /// An optional pre-configured HTTP client that does not follow redirects. Required for requests that must not follow + /// them when is supplied. /// public HttpClientTransport(HttpClient? httpClient = null, HttpClient? noRedirectHttpClient = null) { _httpClient = httpClient ?? CreateOwnedClient(allowAutoRedirect: true); _ownsClient = httpClient is null; - // A transport that owns its main client can create the matching no-redirect one; one wrapping a - // caller's client has to be given it. + // An owned main client gets a matching no-redirect one; a caller's client has to be given it. _noRedirectClient = noRedirectHttpClient ?? (httpClient is null ? CreateOwnedClient(allowAutoRedirect: false) : null); _ownsNoRedirectClient = noRedirectHttpClient is null && httpClient is null; } @@ -61,13 +55,11 @@ private static HttpClient CreateOwnedClient(bool allowAutoRedirect) { ConnectTimeout = ConnectTimeout, AllowAutoRedirect = allowAutoRedirect, - // The API advertises brotli/gzip/deflate response compression on the dataset-items and - // key-value-store record endpoints. Enabling automatic decompression makes the handler send - // the matching `Accept-Encoding` request header and transparently inflate the response, so - // compressed payloads are handled the same way as in the reference JS client. + // The API compresses responses of the dataset-items and record endpoints. Automatic decompression sends the matching + // `Accept-Encoding` and inflates the response, like the reference client. .NET has no zstd decompression, so zstd is not advertised. AutomaticDecompression = DecompressionMethods.Brotli | DecompressionMethods.GZip | DecompressionMethods.Deflate, }; - // The client-side retry orchestrator owns the per-request timeout, so disable HttpClient's own. + // The client's retry loop owns the per-request timeout. return new HttpClient(handler) { Timeout = Timeout.InfiniteTimeSpan }; } diff --git a/src/Apify.Client/Http/HttpCompressor.cs b/src/Apify.Client/Http/HttpCompressor.cs index 53d2bd5..94832de 100644 --- a/src/Apify.Client/Http/HttpCompressor.cs +++ b/src/Apify.Client/Http/HttpCompressor.cs @@ -3,16 +3,13 @@ namespace Apify.Client.Http; -/// -/// Strategy for compressing HTTP request bodies. -/// +/// Strategy for compressing HTTP request bodies. /// -/// Derive from this class to plug in a custom compressor through : -/// set to the value sent in the Content-Encoding header and implement -/// . The client calls it only for bodies that are large enough to benefit from -/// compression and whose content type does not already carry its own compression, and never for a body the -/// caller already sent with a Content-Encoding of its own. The Apify API accepts the gzip, -/// br, deflate and identity encodings. Matches the reference client's HttpCompressor. +/// Derive from this class and pass it as : set +/// to the Content-Encoding value and implement . The client calls it only for large +/// bodies whose content type has no compression of its own, and never for a body the caller sent with a +/// Content-Encoding. The API accepts gzip, br, zstd, deflate and identity. +/// Matches the reference client's HttpCompressor. /// public abstract class HttpCompressor { diff --git a/src/Apify.Client/Http/IHttpTransport.cs b/src/Apify.Client/Http/IHttpTransport.cs index c6d7477..c994650 100644 --- a/src/Apify.Client/Http/IHttpTransport.cs +++ b/src/Apify.Client/Http/IHttpTransport.cs @@ -5,43 +5,35 @@ namespace Apify.Client.Http; -/// -/// The replaceable transport contract of the client. -/// +/// The replaceable transport of the client. /// /// -/// Implementations are responsible only for sending a single, fully-prepared -/// and returning the raw . Authentication, the User-Agent header, -/// retries and (de)serialization are handled by the client, so a backend only needs to perform one -/// network round-trip. +/// An implementation sends one fully prepared and returns the raw +/// . The client handles authentication, User-Agent, retries and +/// (de)serialization. /// /// -/// A non-2xx HTTP status is not an error at this layer — return it as a normal response. Only -/// transport-level failures (connection refused, DNS, timeout) should be thrown, as an -/// . +/// A non-2xx status is not an error at this layer: return it as a response. Throw only transport failures (connection +/// refused, DNS, timeout), as an . /// /// -/// A request that sets to false in +/// A request with set to false in /// carries the API token in a header that must not follow a redirect to -/// another host, so the implementation has to return a redirect response as it is. The client sets it on -/// requests to an Actor's server. +/// another host, so return a redirect response as it is. The client sets it on requests to an Actor's server. /// /// -/// Swap the default implementation () via -/// to share a connection pool, customize -/// TLS/proxy settings, or inject a mock in tests. +/// Replace the default through +/// to share a connection pool, configure TLS or a proxy, or mock the API in tests. /// /// public interface IHttpTransport { - /// - /// Sends a single request with a per-attempt timeout and returns the response. - /// - /// The fully-prepared request (headers and body already set). - /// The per-attempt timeout budget. + /// Sends one request with a per-attempt timeout and returns the response. + /// The prepared request. + /// The per-attempt timeout. /// - /// When true, return as soon as the response headers arrive so the body can be consumed as a - /// live stream (used by log streaming); otherwise the whole response may be buffered. + /// When true, return as soon as the headers arrive so the body can be read as a live stream; otherwise the + /// response may be buffered. /// /// A token to cancel the request. Task SendAsync( diff --git a/src/Apify.Client/Http/ZstdHttpCompressor.cs b/src/Apify.Client/Http/ZstdHttpCompressor.cs new file mode 100644 index 0000000..6d45151 --- /dev/null +++ b/src/Apify.Client/Http/ZstdHttpCompressor.cs @@ -0,0 +1,50 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using ZstdSharp; + +namespace Apify.Client.Http; + +/// +/// Compresses request bodies with zstd (Content-Encoding: zstd). Matches the reference client's +/// ZstdHttpCompressor. +/// +public sealed class ZstdHttpCompressor : HttpCompressor +{ + /// The lowest valid zstd quality: the fastest, with the least compression. + public const int MinQuality = 1; + + /// + /// The highest valid zstd quality. zstd itself goes up to 22, but levels 20 to 22 use a window above the 8 MB + /// that RFC 9659 requires HTTP decoders to support, so a record stored at those levels could fail to decode. + /// + public const int MaxQuality = 19; + + /// zstd's own default: fast, with a ratio close to the best gzip level. + public const int DefaultQuality = 3; + + private readonly int _quality; + + /// Creates a zstd compressor. + /// The compression level, from (the fastest) to (the best compression). + /// is outside the valid range. + public ZstdHttpCompressor(int quality = DefaultQuality) + { + ArgumentOutOfRangeException.ThrowIfLessThan(quality, MinQuality); + ArgumentOutOfRangeException.ThrowIfGreaterThan(quality, MaxQuality); + _quality = quality; + } + + /// + public override string ContentEncoding => "zstd"; + + /// + public override Task CompressAsync(byte[] data, CancellationToken cancellationToken = default) + => Task.Run(() => Compress(data), cancellationToken); + + private byte[] Compress(byte[] data) + { + using var compressor = new Compressor(_quality); + return compressor.Wrap(data).ToArray(); + } +} diff --git a/src/Apify.Client/Internal/HttpClientCore.cs b/src/Apify.Client/Internal/HttpClientCore.cs index e669417..c4e1114 100644 --- a/src/Apify.Client/Internal/HttpClientCore.cs +++ b/src/Apify.Client/Internal/HttpClientCore.cs @@ -14,26 +14,26 @@ namespace Apify.Client.Internal; /// -/// The orchestrating HTTP client shared by every resource client. It owns the transport, the optional -/// API token, the User-Agent, and the retry/timeout policy, and applies them to every request. +/// The HTTP client shared by every resource client. It owns the transport, token, User-Agent and the +/// retry and timeout policy, and applies them to every request. /// internal sealed class HttpClientCore { - /// Status returned when the per-resource rate limit is hit. + /// Status of a rate limited request. public const int RateLimitExceeded = 429; - /// Statuses at or above this value are treated as retryable internal server errors. + /// Statuses at or above this value are retried as server errors. private const int MinServerError = 500; - /// Responses with a status below this value are treated as success. + /// Statuses below this value are a success. public const int MaxSuccessStatus = 300; - /// Exponential-backoff multiplier applied to the inter-retry delay after each attempt. + /// Multiplier of the delay between retries. private const int BackoffFactor = 2; private const int NotFound = 404; - /// Prefix of the headers that describe a request body; they belong to the body, not to the request. + /// Prefix of the headers that describe a request body. private const string ContentHeaderPrefix = "Content-"; /// The header that carries the media type of a request body. @@ -42,19 +42,16 @@ internal sealed class HttpClientCore /// The header that carries the encoding (compression) of a request body. public const string ContentEncodingHeader = "Content-Encoding"; - /// Content headers the client derives from the body itself, so a caller-supplied value is ignored. + /// Content headers the client derives from the body, so a caller-supplied value is ignored. private static readonly string[] BodyDerivedContentHeaders = { ContentTypeHeader, ContentEncodingHeader, "Content-Length" }; - /// - /// Request bodies whose size in bytes is at or above this threshold are compressed before sending, - /// matching the reference client's minimum-compression size. - /// + /// Bodies of at least this many bytes are compressed, as in the reference client. private const int MinCompressBytes = 1024; - /// Media type prefixes whose payloads carry their own compression (e.g. image/png). + /// Media type prefixes whose payloads carry their own compression. private static readonly string[] AlreadyCompressedMediaTypePrefixes = { "audio/", "image/", "video/" }; - /// Exact media types whose payloads carry their own compression (archives, packages, fonts). + /// Media types whose payloads carry their own compression (archives, packages, fonts). private static readonly HashSet AlreadyCompressedMediaTypes = new(StringComparer.Ordinal) { "application/epub+zip", @@ -78,7 +75,7 @@ internal sealed class HttpClientCore "font/woff2", }; - /// Uncompressed media types that sit under an already-compressed prefix but still benefit from compression. + /// Media types under an already-compressed prefix that are uncompressed and still benefit from compression. private static readonly HashSet CompressibleMediaTypes = new(StringComparer.Ordinal) { "audio/aiff", @@ -99,7 +96,7 @@ internal sealed class HttpClientCore "image/x-ms-bmp", }; - /// Structured syntax suffixes marking a media type as text even under an already-compressed prefix (e.g. image/svg+xml). + /// Suffixes that mark a media type as text even under an already-compressed prefix, such as image/svg+xml. private static readonly string[] CompressibleMediaTypeSuffixes = { "+json", "+xml" }; private readonly IHttpTransport _transport; @@ -131,7 +128,7 @@ public HttpClientCore( /// The request statistics shared with . public Statistics Stats => _stats; - /// The User-Agent header value this client sends (a User-Agent default header wins over the built-in one). + /// The User-Agent this client sends; a User-Agent default header wins over the built-in one. public string UserAgent { get; } private static string? FindHeader(IReadOnlyDictionary? headers, string name) @@ -152,14 +149,13 @@ public HttpClientCore( return null; } - /// The configured overall per-request timeout budget, in seconds. + /// The overall per-request timeout budget, in seconds. public double RequestTimeoutSecs => _retry.TimeoutSecs; /// - /// Sends a request with auth, User-Agent and the retry policy applied, returning the successful - /// response (the caller owns and disposes it). A other than - /// changes how the call authenticates, which responses it retries and - /// whether an error response is returned instead of thrown. + /// Sends a request with auth, User-Agent and the retry policy, and returns the successful response, which the + /// caller disposes. A other than changes the + /// authentication, the retried responses and whether an error response is returned instead of thrown. /// public async Task CallAsync( HttpMethod method, @@ -180,17 +176,13 @@ public async Task CallAsync( var delayMillis = _retry.MinDelayMillis; var maxAttempts = _retry.MaxRetries + 1; var path = ExtractPath(url); - // An explicit timeout (e.g. a per-queue or per-call WithTimeout override) always wins as the base for - // the per-attempt doubling below; otherwise the tier's configured duration is the base. Either way, - // the retry-growth cap is always the configured overall budget (ApifyClientOptions.TimeoutSecs) — - // matching the reference client, an explicit override above that budget is capped back down to it - // rather than silently exceeding it. Raise TimeoutSecs itself to allow a longer per-call timeout. + // An explicit timeout (such as a WithTimeout override) is the base that doubles per attempt; otherwise it is the + // tier's duration. Either way the growth is capped at the overall budget (ApifyClientOptions.TimeoutSecs), as in the + // reference client. var baseTimeout = timeout ?? TimeSpan.FromSeconds(_retry.TierSecs(tier)); var overallCap = TimeSpan.FromSeconds(_retry.TimeoutSecs); - // Normalize (and, when large enough, compress) the body once up front so retries reuse the same - // prepared payload instead of re-encoding and re-compressing on every attempt. A caller-supplied - // explicitContentEncoding (e.g. SetRecordAsync's contentEncoding for an already-compressed value) - // skips this: the bytes are already in their final, encoded form and must reach the API unchanged. + // Prepare (and, when large enough, compress) the body once, so retries reuse it. An explicit content encoding skips + // this: the bytes are already encoded and must reach the API unchanged. var prepared = explicitContentEncoding is not null ? new PreparedBody(bodyBytes ?? (body is not null ? Encoding.UTF8.GetBytes(body) : null), contentType, explicitContentEncoding) : await PrepareBodyAsync(body, bodyBytes, contentType, policy, cancellationToken).ConfigureAwait(false); @@ -209,14 +201,12 @@ public async Task CallAsync( var status = (int)response.StatusCode; if (status < MaxSuccessStatus) { - // The sent request is disposed by now; keep a body-less copy of it so response readers can - // report the method and path in the errors they raise. + // The sent request is disposed; keep a body-less copy so response readers can report the method and path. response.RequestMessage = new HttpRequestMessage(method, url); return response; } - // The statistics track the API's rate limits; only API calls throw on an error status, so a call - // that returns error responses is not an API call and its 429s stay out of them. + // The statistics track the API's rate limits. Only API calls throw on an error status, so the 429s of other calls stay out. if (status == RateLimitExceeded && policy.ThrowOnErrorStatus) { _stats.AddRateLimitError(attempt); @@ -236,8 +226,7 @@ public async Task CallAsync( catch (ApifyTransportException ex) { lastError = ex; - // Network/timeout failures are retryable, unless the caller opted out of retrying timeouts or of - // retrying transport errors at all. + // Network and timeout failures are retried, unless the caller opted out of retrying timeouts or transport errors. retryable = policy.RetryTransportErrors && !(doNotRetryTimeouts && ex.IsTimeout); } @@ -251,11 +240,11 @@ await Task.Delay(TimeSpan.FromMilliseconds(RandomizedDelayMillis(delayMillis)), delayMillis = Math.Min(delayMillis * BackoffFactor, _retry.MaxDelayMillis); } - // Unreachable in practice (maxAttempts >= 1); defensive. + // Unreachable: there is at least one attempt. throw lastError ?? new ApifyTransportException("request failed with no attempts"); } - /// Opens a live streaming response (single attempt, no retry). Used by log streaming. + /// Opens a live streaming response with a single attempt and no retry, for log streaming. public Task StreamAsync(string url, CancellationToken cancellationToken) { _stats.AddCall(); @@ -278,9 +267,8 @@ private async Task SendOnceAsync( } /// - /// Rejects a default header that is not a valid request header. Content headers (Content-Type, - /// Content-Encoding, Content-Length, ...) describe the body, which the client sets per - /// request, so they cannot be defaults; any other rejected name is not a valid HTTP header name. + /// Rejects a default header that is a content header (Content-Type, Content-Length and so on), because + /// the client sets those per request, or not a valid header name. /// /// A header name is a content header or not a valid header name. private static void ValidateDefaultHeaders(IReadOnlyDictionary? headers) @@ -307,9 +295,9 @@ private static void ValidateDefaultHeaders(IReadOnlyDictionary? } /// - /// Moves the request's Authorization header (the token, or a default header of that name) to the header - /// named ; nothing changes for null. A header of that name set for the - /// single call is applied afterwards and still wins. + /// Moves the request's Authorization header (the token or a default header of that name) to + /// ; null changes nothing. A header of that name set for the single call is + /// applied afterwards and wins. /// private static void MoveAuthorization(HttpRequestMessage request, string? headerName) { @@ -324,7 +312,7 @@ private static void MoveAuthorization(HttpRequestMessage request, string? header request.Headers.TryAddWithoutValidation(headerName, authorization); } - /// Sets each header on the request, replacing any value already set under the same name. + /// Sets each header on the request, replacing a value already set under the same name. private static void SetHeaders(HttpRequestMessage request, IReadOnlyDictionary? headers) { if (headers is null) @@ -334,7 +322,7 @@ private static void SetHeaders(HttpRequestMessage request, IReadOnlyDictionary name.StartsWith(ContentHeaderPrefix, StringComparison.OrdinalIgnoreCase); - /// Builds a fully-prepared request with auth, User-Agent, content type and extra headers. + /// Builds a request with auth, User-Agent, content type and extra headers. private HttpRequestMessage BuildRequest( HttpMethod method, string url, @@ -362,8 +350,7 @@ private HttpRequestMessage BuildRequest( request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token); } - // The client-wide default headers win over the built-in ones above, and a header set for this single call - // wins over both. + // Client-wide default headers win over the built-in ones, and a header of this single call wins over both. SetHeaders(request, _defaultHeaders); MoveAuthorization(request, policy.AuthorizationHeader); SetHeaders(request, extraHeaders); @@ -375,7 +362,7 @@ private HttpRequestMessage BuildRequest( if (prepared.Bytes is not null) { var content = new ByteArrayContent(prepared.Bytes); - // Set the content type verbatim (no charset appended unless the caller added one). + // Set the content type as given, without appending a charset. content.Headers.ContentType = string.IsNullOrEmpty(prepared.ContentType) ? null : MediaTypeHeaderValue.Parse(prepared.ContentType); @@ -392,9 +379,8 @@ private HttpRequestMessage BuildRequest( } /// - /// Sets the caller's content headers on the body, except the ones the client derives from the body itself. A - /// request without a body has nothing to attach them to (for example Content-Disposition), so they are - /// not sent. + /// Sets the caller's content headers on the body, except the ones derived from the body. A request without a body has + /// nothing to attach them to, so they are not sent. /// private static void AddContentHeaders(HttpContent content, IReadOnlyDictionary? headers) { @@ -416,15 +402,12 @@ private static void AddContentHeaders(HttpContent content, IReadOnlyDictionary - /// Normalizes a request body to bytes and, when it is large enough, compresses it. Raw bytes take - /// precedence so binary records (e.g. images) are used as-is rather than re-encoded through a UTF-8 - /// string; a string body is UTF-8 encoded. Either kind of payload is then compressed once it reaches - /// the size threshold (see the remarks). + /// Converts a request body to bytes and compresses it once it reaches the size threshold. Raw bytes take precedence + /// over a string, which is UTF-8 encoded. /// /// - /// Bodies at or above whose content type is compressible are compressed with - /// the configured : brotli (Content-Encoding: br) by default, matching - /// the reference client's preference. + /// Bodies of at least with a compressible content type are compressed with the + /// configured , brotli by default. /// private async Task PrepareBodyAsync(string? body, byte[]? bodyBytes, string contentType, RequestPolicy policy, CancellationToken cancellationToken) { @@ -445,17 +428,14 @@ private async Task PrepareBodyAsync(string? body, byte[]? bodyByte } /// - /// Decides whether a request body with the given content type is worth compressing, matching the - /// reference client's isCompressibleContentType(). + /// Decides whether a body with this content type is worth compressing, as the reference client's + /// isCompressibleContentType() does. /// /// - /// Images, audio, video and archives already carry their own compression: running them through - /// brotli/gzip burns CPU, holds a second full copy of the body in memory, and usually produces output - /// slightly larger than the input. Formats that are raw despite such a media type (e.g. image/bmp, - /// audio/wav) are still compressed, as are +json/+xml structured-syntax suffixes - /// (e.g. image/svg+xml). application/octet-stream is deliberately left off the - /// already-compressed list: it is the catch-all for unknown binary data and SetRecordAsync's - /// fallback when no content type is given. No content type is assumed compressible. + /// Images, audio, video and archives already carry their own compression, so compressing them wastes CPU and memory + /// for no gain. Raw formats under such a type (image/bmp, audio/wav) and +json or +xml + /// types (image/svg+xml) are still compressed. application/octet-stream counts as compressible, since it + /// holds unknown binary data and is the fallback of SetRecordAsync. A missing content type is not compressible. /// internal static bool IsCompressibleContentType(string? contentType) { @@ -464,7 +444,7 @@ internal static bool IsCompressibleContentType(string? contentType) return true; } - // Content-Type may carry parameters, e.g. "text/plain; charset=utf-8". + // The content type may carry parameters, such as "; charset=utf-8". var semicolon = contentType.IndexOf(';', StringComparison.Ordinal); var mediaType = (semicolon >= 0 ? contentType.Substring(0, semicolon) : contentType) .Trim() @@ -499,19 +479,13 @@ internal static bool IsCompressibleContentType(string? contentType) return true; } - /// - /// A request body normalized to bytes, together with the content type and optional - /// Content-Encoding to send. A value carries no body. - /// + /// A request body as bytes, with the content type and optional Content-Encoding to send. private readonly record struct PreparedBody(byte[]? Bytes, string ContentType, string? ContentEncoding); /// - /// Returns min(overall, base * 2^(attempt-1)): the first attempt uses - /// as given (it is not pre-capped by the caller — capping happens only here); each retry doubles it (a - /// slow-but-progressing connection gets more time), and every attempt, including the first, is capped at - /// — the configured overall budget (ApifyClientOptions.TimeoutSecs). - /// This is what keeps an explicit WithTimeout override above that budget from exceeding it, - /// matching the reference client's timeoutMaxSecs cap. + /// Returns min(overall, base * 2^(attempt-1)): each retry doubles the timeout, and every attempt, the first + /// included, is capped at the overall budget (ApifyClientOptions.TimeoutSecs), as in the reference client's + /// timeoutMaxSecs. /// private static TimeSpan AttemptTimeout(TimeSpan baseTimeout, TimeSpan overall, int attempt) { @@ -533,7 +507,7 @@ private static bool IsStatusRetryable(int status, RequestPolicy policy) ? codes.Contains(status) : status == RateLimitExceeded || status >= MinServerError; - /// Returns a delay chosen randomly from [delay, 2*delay) (exponential backoff + jitter). + /// Returns a random delay from [delay, 2*delay): exponential backoff with jitter. private static double RandomizedDelayMillis(double delayMillis) { if (delayMillis <= 0) @@ -544,7 +518,7 @@ private static double RandomizedDelayMillis(double delayMillis) return delayMillis + (Random.Shared.NextDouble() * delayMillis); } - /// Builds an from an API error response body. + /// Builds an from an error response body. public static ApifyApiException BuildApiError(int status, string body, int attempt, string method, string path) { string? type = null; @@ -583,7 +557,7 @@ public static ApifyApiException BuildApiError(int status, string body, int attem return null; } - /// Returns the path+query portion of a URL, for error reporting. + /// Returns the path and query of a URL, for error messages. public static string ExtractPath(string url) { var rest = url; @@ -597,7 +571,7 @@ public static string ExtractPath(string url) return slash >= 0 ? rest.Substring(slash) : string.Empty; } - /// Reports whether an exception represents a "resource not found" API error. + /// Whether an exception is a "not found" API error. public static bool IsNotFound(Exception ex) { if (ex is not ApifyApiException apiError || apiError.StatusCode != NotFound) diff --git a/src/Apify.Client/Internal/Json.cs b/src/Apify.Client/Internal/Json.cs index 4b4288f..e7adba1 100644 --- a/src/Apify.Client/Internal/Json.cs +++ b/src/Apify.Client/Internal/Json.cs @@ -4,15 +4,10 @@ namespace Apify.Client.Internal; -/// -/// Shared JSON (de)serialization for the client. -/// +/// JSON serialization shared by the client. internal static class Json { - /// - /// Serialization options matching the reference client's output: slashes and non-ASCII characters - /// are left unescaped (like PHP's JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE). - /// + /// Serialization options of the reference client's output: slashes and non-ASCII characters are not escaped. private static readonly JsonSerializerOptions SerializerOptions = new() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, @@ -32,9 +27,8 @@ public static string Encode(JsonNode? value) => value?.ToJsonString(SerializerOptions) ?? "null"; /// - /// Decodes a JSON string into a (null for an empty body). Throws - /// when it is not valid JSON; response bodies go through - /// , which turns that into an . + /// Decodes a JSON string into a (null for an empty body). Throws + /// for invalid JSON; turns that into an . /// public static JsonNode? Decode(string body) { diff --git a/src/Apify.Client/Internal/JsonValues.cs b/src/Apify.Client/Internal/JsonValues.cs index 2893168..995b2a4 100644 --- a/src/Apify.Client/Internal/JsonValues.cs +++ b/src/Apify.Client/Internal/JsonValues.cs @@ -5,10 +5,7 @@ namespace Apify.Client.Internal; -/// -/// Small helpers for reading typed values out of a decoded with fallbacks, -/// used by the page/head models. Absent or mistyped fields fall back rather than throwing. -/// +/// Reads typed values out of a , falling back instead of throwing on absent or mistyped fields. internal static class JsonValues { /// The object as a , or an empty object if it is not one. diff --git a/src/Apify.Client/Internal/PageSizing.cs b/src/Apify.Client/Internal/PageSizing.cs index a438bd4..8011cc9 100644 --- a/src/Apify.Client/Internal/PageSizing.cs +++ b/src/Apify.Client/Internal/PageSizing.cs @@ -3,9 +3,8 @@ namespace Apify.Client.Internal; /// -/// Shared handling of the client-side ChunkSize iteration option: the maximum number of items -/// requested per API call while iterating across pages. It never reaches the query string itself; it only -/// sizes each page's limit, matching the reference client's chunkSize. +/// Handling of the client-side ChunkSize iteration option, the most items requested per API call. It only sizes +/// each page's limit and is never sent itself, as in the reference client. /// internal static class PageSizing { @@ -21,9 +20,8 @@ public static void ValidateChunkSize(int? chunkSize, string optionName) } /// - /// The smaller of two page limits, for the limit query parameter. The API reads a limit of - /// 0 as unset, so 0 (like null) means "no limit" here and loses to any other value; - /// the result is null when neither is set. Matches the reference client's minForLimitParam. + /// The smaller of two page limits. The API reads 0 as unset, so 0 and null mean "no limit" and lose + /// to any other value; the result is null when neither is set. As the reference client's minForLimitParam. /// public static int? MinForLimit(int? a, int? b) { diff --git a/src/Apify.Client/Internal/PagedIteration.cs b/src/Apify.Client/Internal/PagedIteration.cs index 4cf97a6..e245588 100644 --- a/src/Apify.Client/Internal/PagedIteration.cs +++ b/src/Apify.Client/Internal/PagedIteration.cs @@ -6,40 +6,30 @@ namespace Apify.Client.Internal; -/// One fetched page: its items and, when the API reports it, the number of rows it scanned. +/// A fetched page: its items and, when the API reports it, the number of rows it scanned. /// The items on the page. -/// The rows the API scanned to produce the page (null when not reported). +/// The rows the API scanned for the page (null when not reported). /// The item type. internal readonly record struct FetchedPage(IReadOnlyList Items, long? ScannedRows); -/// -/// The offset/limit paging loop shared by every list iterator, ported from the reference client's -/// listPaginatedFromCallback. -/// +/// The offset/limit paging loop shared by the list iterators, ported from the reference client's listPaginatedFromCallback. internal static class PagedIteration { - /// - /// Lazily yields every item from on, fetching pages on demand. - /// + /// Lazily yields every item from on, fetching pages on demand. /// - /// caps the items yielded, or the rows scanned when - /// is set (null or 0 for no cap); a negative offset or - /// limit is rejected. - /// The next offset and the stop condition follow the rows a page scanned, not the items it returned: - /// advancing by the item count would re-scan rows after a filter dropped some and skip rows after - /// unwind multiplied them, and stopping at an empty page would end the iteration in front of rows - /// a filter hid. The scanned count is derived from a dataset's item count, which lags a fresh push, so - /// the larger of it and the item count is used, capped at the rows the request asked for so an unwound - /// page does not advance past rows the next request would then never read. The total a page reports is - /// never consulted, because it changes when the listed resource grows during the iteration; iteration - /// stops once a page scans no rows, which costs one extra request at the end. + /// caps the items yielded, or the rows scanned with + /// (null or 0 for no cap). A negative offset or limit is rejected. The next offset and the stop condition + /// follow the rows a page scanned, not the items it returned: the item count would re-scan rows after a filter dropped + /// some, skip rows after unwind multiplied them and stop in front of rows a filter hid. The scanned count lags a + /// fresh push, so the larger of it and the item count is used, capped at the requested rows. The reported total is + /// ignored because it changes while the resource grows. Iteration stops once a page scans no rows, which costs one + /// extra request. /// /// The offset of the first row. /// The most items (or rows, see ) across all pages, or null/0 for no cap. /// - /// Whether counts the rows scanned (dataset items) rather than the items - /// returned (listings, where the API can scan more rows than it returns, e.g. the Store dropping Actors - /// the caller cannot run). + /// Whether counts the rows scanned (dataset items) rather than the items returned + /// (listings, where the API can scan more rows than it returns, such as the Store dropping Actors the caller cannot run). /// /// The most rows to request per call, or null for the API default. /// Fetches one page given its offset and limit (null limit: leave it out). diff --git a/src/Apify.Client/Internal/QueryParams.cs b/src/Apify.Client/Internal/QueryParams.cs index 6307fa7..b3d1e46 100644 --- a/src/Apify.Client/Internal/QueryParams.cs +++ b/src/Apify.Client/Internal/QueryParams.cs @@ -5,10 +5,7 @@ namespace Apify.Client.Internal; -/// -/// An ordered collection of query parameters that omits absent (null) values and encodes -/// booleans as 1/0, matching the Apify API conventions. -/// +/// An ordered set of query parameters that omits null values and encodes booleans as 1/0. internal sealed class QueryParams { private readonly List> _pairs = new(); @@ -48,10 +45,7 @@ public QueryParams AddDouble(string key, double? value) return this; } - /// - /// Adds a boolean parameter, encoded as 1/0, if the value is non-null. This matches the - /// JS reference client, whose axios paramsSerializer converts booleans via Number(value). - /// + /// Adds a boolean as 1/0, like the reference client's paramsSerializer, if the value is non-null. public QueryParams AddBool(string key, bool? value) { if (value is not null) @@ -80,11 +74,7 @@ public QueryParams AddRaw(string key, string value) return this; } - /// - /// Sets an integer parameter, replacing any existing occurrences of . Used by - /// auto-paging iteration to overwrite the per-page offset/limit without emitting the - /// parameter twice. - /// + /// Sets an integer parameter, replacing existing ones, so iteration can overwrite the per-page offset and limit. public QueryParams Set(string key, long value) { _pairs.RemoveAll(pair => string.Equals(pair.Key, key, StringComparison.Ordinal)); @@ -92,10 +82,7 @@ public QueryParams Set(string key, long value) return this; } - /// - /// Returns a copy with the page's offset and limit set, the latter left out when - /// is null (so a caller's own limit does not leak into every page). - /// + /// Returns a copy with the page's offset and limit; a null limit is left out, so a caller's limit does not leak into every page. public QueryParams PageQuery(int offset, int? limit) { var page = Copy().Set("offset", offset).Remove("limit"); diff --git a/src/Apify.Client/Internal/RequestPolicy.cs b/src/Apify.Client/Internal/RequestPolicy.cs index aa159f0..6f4698d 100644 --- a/src/Apify.Client/Internal/RequestPolicy.cs +++ b/src/Apify.Client/Internal/RequestPolicy.cs @@ -4,9 +4,9 @@ namespace Apify.Client.Internal; /// -/// Per-request overrides of the shared HTTP behaviour, for calls that are not plain API calls. The default -/// instance is the behaviour of every API call; overrides it to -/// talk to an Actor's own server (the reference client's per-request ApifyRequestConfig options). +/// Per-request overrides of the shared HTTP behavior for calls that are not plain API calls. The default is the +/// behavior of an API call; overrides it to call an Actor's server, as the +/// reference client's per-request ApifyRequestConfig does. /// internal sealed class RequestPolicy { @@ -14,16 +14,14 @@ internal sealed class RequestPolicy public static readonly RequestPolicy Default = new(); /// - /// The header that carries the API token, or null for the standard Authorization header. Actor - /// servers accept X-Apify-Authorization, which the platform removes before the request reaches the - /// Actor's code, so Authorization stays free for the Actor's own authentication. + /// The header that carries the token, or null for Authorization. Actor servers accept + /// X-Apify-Authorization, which the platform removes, so Authorization stays free for the Actor's own authentication. /// public string? AuthorizationHeader { get; init; } /// - /// Whether an error status throws . When false, the error - /// response is returned once no retry follows, and a rate limit (429) is not counted in the client's - /// statistics, which track the API's rate limits. + /// Whether an error status throws . When false, the error response is + /// returned once no retry follows, and a 429 is not counted in the client's statistics of the API's rate limits. /// public bool ThrowOnErrorStatus { get; init; } = true; @@ -31,15 +29,14 @@ internal sealed class RequestPolicy public IReadOnlyCollection? RetryStatusCodes { get; init; } /// - /// Whether transport errors, timeouts included, are retried. When false, the first one fails the call, - /// since the request may have reached the server before it failed. + /// Whether transport errors, timeouts included, are retried. When false, the first one fails the call, since the + /// request may have reached the server. /// public bool RetryTransportErrors { get; init; } = true; /// - /// Whether the transport follows redirects. When false, a redirect response is returned like an error - /// status (see ), so a header that carries the token never - /// goes to a host the server picked. + /// Whether the transport follows redirects. When false, a redirect is returned like an error status (see + /// ), so the token never goes to a host the server picked. /// public bool FollowRedirects { get; init; } = true; diff --git a/src/Apify.Client/Internal/RequestTier.cs b/src/Apify.Client/Internal/RequestTier.cs index b85c84c..a99a5db 100644 --- a/src/Apify.Client/Internal/RequestTier.cs +++ b/src/Apify.Client/Internal/RequestTier.cs @@ -1,11 +1,8 @@ namespace Apify.Client.Internal; /// -/// The timeout tier a request falls into, matching the reference client's TimeoutTier -/// ('short'/'medium'/'long'): each tier's duration is configured once on -/// and every method that sends a request picks the tier that -/// fits its expected duration, so the default wait before giving up is consistent across the client instead -/// of a single one-size-fits-all budget. +/// The timeout tier of a request, as the reference client's TimeoutTier. Each tier's duration is configured on +/// , and each method picks the tier that fits its expected duration. /// internal enum RequestTier { diff --git a/src/Apify.Client/Internal/ResourceContext.cs b/src/Apify.Client/Internal/ResourceContext.cs index c3ae434..aa8ae4c 100644 --- a/src/Apify.Client/Internal/ResourceContext.cs +++ b/src/Apify.Client/Internal/ResourceContext.cs @@ -11,8 +11,7 @@ namespace Apify.Client.Internal; /// -/// The resolved context for a resource client: its base URL and the shared HTTP client. The methods -/// here implement the CRUD primitives once, so each resource client stays small and consistent (DRY). +/// The base URL and shared HTTP client of a resource client, with the CRUD primitives the resource clients build on. /// internal sealed class ResourceContext { @@ -28,28 +27,24 @@ internal sealed class ResourceContext /// How long to wait between polls while waiting for a run/build to finish, in seconds. private const double WaitPollIntervalSecs = 0.25; - /// Server-side waitForFinish chunk size (the API caps server waiting at 60 seconds). + /// Longest server-side waitForFinish the API accepts, in seconds. private const int WaitRequestSecs = 60; /// - /// Safety margin subtracted from the configured per-request timeout when choosing the server-side - /// waitForFinish value, so the server responds before the client's socket timeout fires. + /// Subtracted from the request timeout when choosing the server-side waitForFinish, so the server answers + /// before the client times out. /// private const int WaitTimeoutMarginSecs = 5; /// - /// Finite upper bound used when the caller asks to wait "indefinitely" (waitSecs == null). The - /// API will not accept "Infinity" and an unbounded loop can spin forever on a transient 404; 999999s - /// (~11.5 days) is effectively indefinite while guaranteeing termination. + /// Upper bound for waiting indefinitely (waitSecs is null), about 11.5 days: the API rejects "Infinity", + /// and an unbounded loop could spin forever on a transient 404. /// private const int MaxWaitForFinishSecs = 999999; /// - /// How long keeps polling a resource that keeps answering 404 (to ride - /// out database replica lag just after creation) before giving up and throwing, instead of running out - /// the full wait budget. Matches the reference client's MAX_WAIT_WHEN_JOB_NOT_EXIST_MILLIS — a - /// job that never existed (e.g. a typo'd run id) would otherwise hang for up to - /// (~11.5 days) when waitSecs is left unset. + /// How long keeps polling a resource that keeps answering 404 (replica lag right + /// after creation) before it reports the resource as missing. As in the reference client. /// private const int MaxWaitWhenJobNotExistMillis = 3000; @@ -80,10 +75,9 @@ private ResourceContext(HttpClientCore http, string url, string baseUrl) public TimeSpan? RequestTimeout => _requestTimeout; /// - /// Creates a context for a collection endpoint: {base}/{resourcePath}. Any - /// are copied into so they are applied to - /// every call on the nested resource (used to forward the last-run status/origin filters - /// to a run's nested storage and log clients, matching the reference client). + /// Creates a context for a collection endpoint, {base}/{resourcePath}. are + /// applied to every call, which is how a run's nested storages and log get the last-run status and + /// origin filters. /// public static ResourceContext Collection(HttpClientCore http, string baseUrl, string resourcePath, QueryParams? inheritedParams = null) { @@ -97,12 +91,10 @@ public static ResourceContext Single(HttpClientCore http, string baseUrl, string => new(http, baseUrl + "/" + resourcePath + "/" + ToSafeId(id, paramName), baseUrl); /// - /// Creates a context for a resource addressed without its own id: a run/build's nested default dataset, - /// key-value store, request queue or log ({base}/{resourcePath}, same shape as - /// ). A 404 through / - /// on such a context is ambiguous — it could mean the parent resource (e.g. the run) or the nested one is - /// missing — so those methods propagate it instead of resolving to null, matching the reference - /// client's catchNotFoundForResourceOrThrow() distinction between id-addressed and chained clients. + /// Creates a context for a resource addressed without its own id, such as a run's default dataset or log. A 404 for + /// such a resource is ambiguous (the parent or the nested resource may be missing), so + /// and propagate it. The reference client makes the + /// same distinction. /// public static ResourceContext NestedSingleton(HttpClientCore http, string baseUrl, string resourcePath, QueryParams? inheritedParams = null) { @@ -145,14 +137,9 @@ public string PublicUrl(string subPath) /// Merges the inherited base params with per-call params. public QueryParams MergedParams(QueryParams? p) => BaseParams.Copy().Extend(p); - // ---- CRUD primitives ------------------------------------------------------ - /// - /// GET a single resource, returning its decoded data. A 404 resolves to null, unless - /// addresses this context's own resource (empty) and the context was - /// built with — then the 404 is ambiguous (it could mean the parent - /// resource or the nested one is missing) and propagates instead. A non-empty - /// (e.g. a request looked up by its own id) is never ambiguous regardless of how this context was built. + /// GET a single resource and return its decoded data. A 404 resolves to null, except for the context's + /// own resource (empty ) on a , where it is ambiguous and propagates. /// public async Task GetResourceAsync(string subPath, QueryParams p, RequestTier tier, CancellationToken ct) { @@ -188,10 +175,8 @@ public async Task UpdateResourceAsync(string subPath, object? body, } /// - /// Performs a DELETE. A not-found is treated as a successful no-op, unless - /// addresses this context's own resource (empty) and the context was built with - /// — then the 404 is ambiguous (the parent resource or the nested one could - /// be missing) and propagates instead. + /// DELETE a resource. A 404 is a no-op, except for the context's own resource (empty ) on a + /// , where it is ambiguous and propagates. /// public async Task DeleteResourceAsync(string subPath, RequestTier tier, CancellationToken ct) { @@ -220,11 +205,9 @@ public async Task> ListResourceAsync(string subPath, QueryP } /// - /// Lazily iterates every item of an offset/limit-paginated listing, fetching pages on demand (see - /// ). It starts at ; - /// caps the total number of items (null or 0 for no cap) and - /// sizes each page request. carries the caller's filters; its - /// offset/limit are replaced per page. + /// Lazily iterates an offset/limit-paginated listing, fetching pages on demand. caps the + /// total number of items (null or 0 for no cap), sizes each page and + /// carries the filters. /// public IAsyncEnumerable IterateListAsync( string subPath, @@ -258,8 +241,7 @@ public async Task CreateResourceAsync(QueryParams p, object? body, R } /// - /// POST that gets-or-creates a named resource (POST {collection}?name=...). An optional - /// is sent as {"schema": ...}, matching the reference client. + /// POST that gets or creates a named resource (POST {collection}?name=...), with an optional schema. /// public async Task GetOrCreateNamedAsync(string? name, JsonNode? schema, CancellationToken ct) { @@ -277,9 +259,8 @@ public async Task GetOrCreateNamedAsync(string? name, JsonNode? sche } /// - /// POST with an optional raw body and content type, unwrapping the data envelope. Pass - /// instead of to send raw bytes (e.g. an Actor's raw - /// byte input, see ) without re-encoding them as a JSON string. + /// POST with an optional raw body, returning the decoded data. sends raw bytes + /// instead of . /// public async Task PostWithBodyAsync(string subPath, QueryParams p, string? body, string contentType, RequestTier tier, CancellationToken ct, byte[]? bodyBytes = null) { @@ -289,10 +270,8 @@ public async Task PostWithBodyAsync(string subPath, QueryParams p, s } /// - /// POST with a raw body, parsing the response directly without unwrapping a data envelope. - /// Used by endpoints (e.g. actor input validation) whose response is a plain object. Pass - /// instead of to send raw bytes (see - /// ) without re-encoding them as a JSON string. + /// POST with an optional raw body, returning the response without unwrapping a data envelope, for endpoints + /// (such as Actor input validation) that answer with a plain object. /// public async Task PostWithBodyNoEnvelopeAsync(string subPath, QueryParams p, string? body, string contentType, RequestTier tier, CancellationToken ct, byte[]? bodyBytes = null) { @@ -302,9 +281,7 @@ public async Task PostWithBodyAsync(string subPath, QueryParams p, s } /// - /// Encodes an Actor/task run input for the request body: raw bytes (e.g. a pre-serialized or binary - /// payload, typically paired with an explicit content type) are sent as-is, and anything else is - /// JSON-encoded, matching the reference client's ActorInput (a plain object/array, or raw bytes). + /// Encodes a run input: byte[] is sent as it is and anything else as JSON, as in the reference client. /// /// The input value, or null for no input. /// The JSON-encoded body, or null when is used. @@ -322,7 +299,7 @@ public static void EncodeInputBody(object? input, out string? body, out byte[]? bodyBytes = null; } - /// DELETE with a JSON body (used for batch request deletion), unwrapping the data envelope. + /// DELETE with a JSON body, returning the decoded data. public async Task DeleteWithBodyAsync(string subPath, QueryParams p, object? body, RequestTier tier, CancellationToken ct) { var url = MergedParams(p).ApplyToUrl(SubUrl(subPath)); @@ -330,9 +307,7 @@ public async Task DeleteWithBodyAsync(string subPath, QueryParams p, return await response.ReadDataObjectAsync(ct).ConfigureAwait(false); } - /// - /// GET returning the raw response body (no data envelope). Returns null on not-found. - /// + /// GET the raw response body; null on 404. public async Task GetRawAsync(string subPath, QueryParams p, RequestTier tier, CancellationToken ct) { var url = MergedParams(p).ApplyToUrl(SubUrl(subPath)); @@ -348,10 +323,8 @@ public async Task DeleteWithBodyAsync(string subPath, QueryParams p, } /// - /// GET returning the raw response body (no data envelope); propagates a 404 instead of swallowing it. - /// Used by sub-path endpoints whose parent resource must already exist for the call to make sense (e.g. - /// dataset statistics, a schedule's log, a task's input), so a missing parent surfaces as an error rather - /// than as an ambiguous empty result. + /// GET the raw response body, propagating a 404. For sub-paths of a resource that must exist, such as dataset + /// statistics or a task's input. /// public async Task GetRawRequiredAsync(string subPath, QueryParams p, RequestTier tier, CancellationToken ct) { @@ -360,10 +333,7 @@ public async Task GetRawRequiredAsync(string subPath, QueryParams p, Req return await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false); } - /// - /// GET returning a JSON body that is not wrapped in a data envelope; propagates a 404 (see - /// for why). Returns null for an empty body. - /// + /// GET a JSON body without a data envelope, propagating a 404; null for an empty body. public async Task GetJsonRequiredAsync(string subPath, QueryParams p, RequestTier tier, CancellationToken ct) { var url = MergedParams(p).ApplyToUrl(SubUrl(subPath)); @@ -371,9 +341,7 @@ public async Task GetRawRequiredAsync(string subPath, QueryParams p, Req return await response.ReadJsonAsync(ct).ConfigureAwait(false); } - /// - /// GET returning a JSON body that is not wrapped in a data envelope. Returns null on not-found. - /// + /// GET a JSON body without a data envelope; null on 404. public async Task GetJsonAsync(string subPath, QueryParams p, RequestTier tier, CancellationToken ct) { try @@ -386,7 +354,7 @@ public async Task GetRawRequiredAsync(string subPath, QueryParams p, Req } } - /// GET returning a data-enveloped object; propagates a 404 (see for why). + /// GET a data object, propagating a 404. public async Task GetDataObjectRequiredAsync(string subPath, QueryParams p, RequestTier tier, CancellationToken ct) { var url = MergedParams(p).ApplyToUrl(SubUrl(subPath)); @@ -394,10 +362,7 @@ public async Task GetDataObjectRequiredAsync(string subPath, QueryPa return await response.ReadDataObjectAsync(ct).ConfigureAwait(false); } - /// - /// GET returning the raw response body as bytes (no data envelope, no UTF-8 decoding — useful when the - /// body is not necessarily text). Returns null on not-found. - /// + /// GET the raw response body as bytes; null on 404. public async Task GetRawBytesAsync(string subPath, QueryParams p, RequestTier tier, CancellationToken ct) { var url = MergedParams(p).ApplyToUrl(SubUrl(subPath)); @@ -412,11 +377,7 @@ public async Task GetDataObjectRequiredAsync(string subPath, QueryPa } } - /// - /// GET returning the raw response body as bytes; propagates a 404 instead of swallowing it. The byte - /// counterpart of (see its remarks on when a missing parent should - /// surface as an error rather than an ambiguous empty result). - /// + /// GET the raw response body as bytes, propagating a 404. public async Task GetRawBytesRequiredAsync(string subPath, QueryParams p, RequestTier tier, CancellationToken ct) { var url = MergedParams(p).ApplyToUrl(SubUrl(subPath)); @@ -440,9 +401,8 @@ public async Task HeadExistsAsync(string subPath, QueryParams p, RequestTi } /// - /// PUT with raw bytes and a content type, with an explicit per-request timeout and retry control. - /// , when given, is sent verbatim as the Content-Encoding - /// header and skips the client's own compression — used for a value that is already compressed. + /// PUT raw bytes with a content type. A is sent as the Content-Encoding + /// header and skips the client's own compression, for an already compressed value. /// public async Task PutRawAsync(string subPath, QueryParams p, byte[] body, string contentType, TimeSpan? timeout, bool doNotRetryTimeouts, RequestTier tier, CancellationToken ct, string? contentEncoding = null) { @@ -450,11 +410,8 @@ public async Task PutRawAsync(string subPath, QueryParams p, byte[] body, string using var response = await Http.CallAsync(HttpMethod.Put, url, null, contentType, timeout ?? _requestTimeout, doNotRetryTimeouts, bodyBytes: body, tier: tier, explicitContentEncoding: contentEncoding, cancellationToken: ct).ConfigureAwait(false); } - // ---- Wait-for-finish ------------------------------------------------------ - /// - /// The largest server-side waitForFinish value that is safe to send: below the configured - /// per-request timeout by a safety margin (or the API's 60s cap when no finite timeout is set). + /// The largest waitForFinish to send: below the request timeout by a margin, or the API's cap when the timeout is not finite. /// private int ServerWaitCapSecs() { @@ -463,9 +420,7 @@ private int ServerWaitCapSecs() } /// - /// Clamps a caller-supplied server-side waitForFinish value (seconds) to the server wait cap, - /// so a synchronous get/wait never asks the server to hold the connection longer than the client's own - /// per-request timeout. Returns null for a null input. + /// Clamps a server-side waitForFinish (seconds) to ; null stays null. /// public int? ClampServerWait(int? waitForFinishSecs) { @@ -478,12 +433,9 @@ private int ServerWaitCapSecs() } /// - /// Polls a GET endpoint with waitForFinish until the resource reaches a terminal state or the - /// wait budget elapses. == null means "wait indefinitely", - /// implemented as a finite but very large bound so the loop always terminates. A 404 is treated as "not - /// yet available" (database replica lag just after creation) for up to - /// of continuous 404s, after which it is reported as a - /// genuine not-found rather than retried for the rest of the (possibly multi-day) wait budget. + /// Polls a resource with waitForFinish until it reaches a terminal state or runs + /// out (null waits for ). A 404 is retried for up to + /// , then reported as a missing resource. /// public async Task WaitForFinishAsync(int? waitSecs, string resourceName, Func isTerminal, CancellationToken ct) { @@ -510,9 +462,7 @@ public async Task WaitForFinishAsync(int? waitSecs, string resourceN var isNotFoundNow = false; try { - // Long tier (the overall per-request budget): the server may hold the connection open for - // up to requestSecs (derived from that same budget via ServerWaitCapSecs), so the HTTP - // timeout for this call must cover the full budget, not a shorter tier. + // Long tier: the server may hold the connection for up to the whole wait budget, so the HTTP timeout must cover it. var data = await GetResourceRequiredAsync(string.Empty, p, RequestTier.Long, ct).ConfigureAwait(false); if (data is JsonObject obj) { @@ -559,12 +509,9 @@ public async Task WaitForFinishAsync(int? waitSecs, string resourceN $"waiting for {resourceName} to finish failed: cannot fetch {resourceName} details from the server"); } - // ---- URL / id helpers ----------------------------------------------------- - /// - /// Turns a resource id into a single URL path segment. Apify uses the username~resourcename form, - /// so every / of an id is replaced with ~; the result is then percent-encoded by - /// , so a hostile id cannot restructure the request path. + /// Turns a resource id into one URL path segment: username/name becomes username~name, then the id is + /// percent-encoded by . /// /// The raw resource id. /// The name of the caller's parameter, reported by the argument exception. @@ -572,10 +519,8 @@ public async Task WaitForFinishAsync(int? waitSecs, string resourceN public static string ToSafeId(string id, string paramName = "id") => EncodePathSegment(id.Replace('/', '~'), paramName); /// - /// Percent-encodes a single URL path segment, so values interpolated into the path (resource ids, record - /// keys, request IDs) cannot break out of the segment. Empty values and the dot segments . and - /// .. are rejected instead of encoded, because URL parsers resolve dot segments after decoding, so - /// %2E%2E would collapse to the parent endpoint just like ... + /// Percent-encodes a URL path segment so a resource id, record key or request ID cannot break out of it. Empty + /// values and the dot segments . and .. are rejected, because URL parsers resolve %2E%2E like ... /// /// The raw, caller-supplied path segment. /// The name of the caller's parameter, reported by the argument exception. diff --git a/src/Apify.Client/Internal/ResourceLimitRetry.cs b/src/Apify.Client/Internal/ResourceLimitRetry.cs index 585239c..1fe5127 100644 --- a/src/Apify.Client/Internal/ResourceLimitRetry.cs +++ b/src/Apify.Client/Internal/ResourceLimitRetry.cs @@ -7,37 +7,30 @@ namespace Apify.Client.Internal; /// -/// Implements /: -/// retries a run start while the API rejects it for lack of resources. Matches the reference client's +/// Implements and : +/// retries a run start while the API rejects it for lack of resources, as the reference client's /// startWaitingForResources(). /// internal static class ResourceLimitRetry { - /// - /// Error types the API rejects a run start with while the account has no free memory or concurrent-run - /// slot for it. Both clear as other runs or builds finish. - /// + /// Error types of a start rejected because no memory or concurrent-run slot is free; both clear as other runs finish. private const string MemoryLimitErrorType = "actor-memory-limit-exceeded"; private const string ConcurrentRunsLimitErrorType = "concurrent-runs-limit-exceeded"; - /// Cooldown between two attempts to start a run that was rejected for lack of resources. + /// Cooldown between attempts. private const int CooldownMillis = 10_000; /// - /// Calls , retrying it every while it fails - /// with a resource-limit error. null disables retrying - /// entirely (the first error is thrown as-is); otherwise - /// null retries until the call succeeds, and a value bounds the retrying, after which the last - /// error is thrown. Any other error is thrown right away. + /// Calls and retries it every while it fails with a + /// resource-limit error. A null throws the first error; + /// null retries until success, and a value bounds the retrying before + /// the last error is thrown. Other errors are thrown at once. /// /// Makes one start attempt. /// The retry configuration, or null to disable retrying. /// A token to cancel the wait between attempts. - /// - /// The cooldown between attempts; defaults to . Overridable only so unit - /// tests can exercise multi-attempt retries without a real 10-second wait per attempt. - /// + /// The cooldown between attempts; a parameter so unit tests need not wait 10 seconds. public static async Task StartAsync( Func> start, WaitForResources? waitForResources, diff --git a/src/Apify.Client/Internal/ResponseOwningStream.cs b/src/Apify.Client/Internal/ResponseOwningStream.cs index b5b16a4..e667aac 100644 --- a/src/Apify.Client/Internal/ResponseOwningStream.cs +++ b/src/Apify.Client/Internal/ResponseOwningStream.cs @@ -7,9 +7,8 @@ namespace Apify.Client.Internal; /// -/// A read-only stream over an HTTP response body that also owns the , -/// disposing it (and thus releasing the connection) when the stream is disposed. Used for live log -/// streaming, where the response must stay open while the caller reads the body incrementally. +/// A read-only stream over a response body that owns the and disposes it, releasing +/// the connection, with the stream. For live log streaming. /// internal sealed class ResponseOwningStream : Stream { diff --git a/src/Apify.Client/Internal/ResponseReader.cs b/src/Apify.Client/Internal/ResponseReader.cs index ef6d0c1..d5870c5 100644 --- a/src/Apify.Client/Internal/ResponseReader.cs +++ b/src/Apify.Client/Internal/ResponseReader.cs @@ -9,11 +9,9 @@ namespace Apify.Client.Internal; /// -/// Reads a successful JSON response into a and reports a body that is unparsable -/// () or does not match the schema the API specification -/// declares for the operation (, see ), -/// with the request's method and path as context. Every successful JSON response is decoded through here, -/// matching the reference client's parseResponse. +/// Reads a successful JSON response and reports a body that is not JSON () or +/// does not match the schema of its operation (, see +/// ), with the request's method and path. As the reference client's parseResponse. /// internal static class ResponseReader { @@ -44,10 +42,7 @@ public static async Task ReadDataObjectAsync(this HttpResponseMessag return UnwrapData(response, body) as JsonObject ?? throw Invalid(response, body, "the response's `data` is not an object"); } - /// - /// Parses an enveloped body, validates it against the specification's schema for the operation, and - /// returns the unwrapped non-null data. - /// + /// Parses an enveloped body, validates it against the operation's schema and returns its non-null data. private static JsonNode UnwrapData(HttpResponseMessage response, string body) { var node = Parse(response, body); diff --git a/src/Apify.Client/Internal/ResponseSchemas.cs b/src/Apify.Client/Internal/ResponseSchemas.cs index 5f43e24..c2cec7e 100644 --- a/src/Apify.Client/Internal/ResponseSchemas.cs +++ b/src/Apify.Client/Internal/ResponseSchemas.cs @@ -12,17 +12,15 @@ namespace Apify.Client.Internal; /// -/// Validates a successful JSON response against the schema the Apify OpenAPI specification declares for -/// its operation, the way the reference client's parseResponse does with its generated schemas. +/// Validates a successful JSON response against the schema the OpenAPI specification declares for its operation, as the +/// reference client's parseResponse does. /// /// -/// The schemas come from the embedded ResponseSchemas.json, generated from the specification by -/// tools/generate_response_schemas.py. Validation covers JSON types, required properties (including -/// nullability), nested properties and array items, the allOf/anyOf/oneOf combinators, -/// and value constraints: the date-time and uri formats, pattern, -/// minimum/maximum and minLength/maxLength. Like the reference client's -/// schemas, it lets unknown properties and unknown enum values through, so an API that grows new fields or -/// values does not break existing clients. A request that matches no documented operation is not validated. +/// The schemas are in the embedded ResponseSchemas.json, generated by tools/generate_response_schemas.py. +/// Validation covers JSON types, required properties and nullability, nested properties and items, +/// allOf/anyOf/oneOf, the date-time and uri formats, pattern, bounds and +/// lengths. Unknown properties and enum values pass, so new API fields do not break existing clients. A request that +/// matches no documented operation is not validated. /// internal static class ResponseSchemas { @@ -49,9 +47,8 @@ internal static class ResponseSchemas public static string SpecVersion => Loaded.Value.SpecVersion; /// - /// Validates against the schema documented for the operation identified by - /// and (the request path, optionally with a query). - /// Returns the problems found, empty when the body is valid or the operation is not documented. + /// Validates against the schema of the operation identified by and + /// . Returns the problems found; empty when the body is valid or the operation is undocumented. /// public static IReadOnlyList Validate(string method, string path, JsonNode? body) { diff --git a/src/Apify.Client/Internal/ResponseSchemas.json b/src/Apify.Client/Internal/ResponseSchemas.json index ab88daf..8eafc10 100644 --- a/src/Apify.Client/Internal/ResponseSchemas.json +++ b/src/Apify.Client/Internal/ResponseSchemas.json @@ -1 +1 @@ -{"operations":{"DELETE /v2/actor-runs/{runId}/request-queue/requests/batch":{"$ref":"#/components/schemas/BatchDeleteResponse"},"DELETE /v2/actor-tasks/{actorTaskId}":{"type":"object"},"DELETE /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/requests/batch":{"$ref":"#/components/schemas/BatchDeleteResponse"},"DELETE /v2/actors/{actorId}":{"type":"object"},"DELETE /v2/actors/{actorId}/runs/last/request-queue/requests/batch":{"$ref":"#/components/schemas/BatchDeleteResponse"},"DELETE /v2/actors/{actorId}/versions/{versionNumber}":{"type":"object"},"DELETE /v2/actors/{actorId}/versions/{versionNumber}/env-vars/{envVarName}":{"type":"object"},"DELETE /v2/browser-info":{"$ref":"#/components/schemas/BrowserInfoResponse"},"DELETE /v2/request-queues/{queueId}/requests/batch":{"$ref":"#/components/schemas/BatchDeleteResponse"},"DELETE /v2/schedules/{scheduleId}":{"type":"object"},"DELETE /v2/webhooks/{webhookId}":{"type":"object"},"GET /v2/actor-builds":{"$ref":"#/components/schemas/ListOfBuildsResponse"},"GET /v2/actor-builds/{buildId}":{"allOf":[{"$ref":"#/components/schemas/BuildResponse"}]},"GET /v2/actor-builds/{buildId}/openapi.json":{"type":"object"},"GET /v2/actor-oauth-accounts/{accountId}":{"$ref":"#/components/schemas/ActorOAuthAccountResponse"},"GET /v2/actor-oauth-accounts/{connectionId}/accounts":{"$ref":"#/components/schemas/ListOfActorOAuthAccountsResponse"},"GET /v2/actor-runs":{"allOf":[{"$ref":"#/components/schemas/ListOfRunsResponse"}]},"GET /v2/actor-runs/{runId}":{"allOf":[{"$ref":"#/components/schemas/RunResponse"}]},"GET /v2/actor-runs/{runId}/dataset":{"$ref":"#/components/schemas/DatasetResponse"},"GET /v2/actor-runs/{runId}/dataset/items":{"items":{"type":"object"},"type":"array"},"GET /v2/actor-runs/{runId}/dataset/statistics":{"$ref":"#/components/schemas/DatasetStatisticsResponse"},"GET /v2/actor-runs/{runId}/key-value-store":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"GET /v2/actor-runs/{runId}/key-value-store/keys":{"$ref":"#/components/schemas/ListOfKeysResponse"},"GET /v2/actor-runs/{runId}/key-value-store/records/{recordKey}":{"$ref":"#/components/schemas/RecordResponse"},"GET /v2/actor-runs/{runId}/request-queue":{"$ref":"#/components/schemas/RequestQueueResponse"},"GET /v2/actor-runs/{runId}/request-queue/head":{"$ref":"#/components/schemas/RequestQueueHeadResponse"},"GET /v2/actor-runs/{runId}/request-queue/requests":{"$ref":"#/components/schemas/ListOfRequestsResponse"},"GET /v2/actor-runs/{runId}/request-queue/requests/{requestId}":{"$ref":"#/components/schemas/RequestResponse"},"GET /v2/actor-tasks":{"$ref":"#/components/schemas/ListOfTasksResponse"},"GET /v2/actor-tasks/{actorTaskId}":{"properties":{"data":{"$ref":"#/components/schemas/Task"}},"required":["data"],"type":"object"},"GET /v2/actor-tasks/{actorTaskId}/input":{"type":"object"},"GET /v2/actor-tasks/{actorTaskId}/run-sync":{"type":"object"},"GET /v2/actor-tasks/{actorTaskId}/run-sync-get-dataset-items":{"type":"object"},"GET /v2/actor-tasks/{actorTaskId}/runs":{"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/RunListItem"},"type":"array"}},"required":["items"],"type":"object"}]}},"required":["data"],"type":"object"},"GET /v2/actor-tasks/{actorTaskId}/runs/last":{"properties":{"data":{"$ref":"#/components/schemas/Run"}},"required":["data"],"type":"object"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/dataset":{"$ref":"#/components/schemas/DatasetResponse"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/dataset/items":{"items":{"type":"object"},"type":"array"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/dataset/statistics":{"$ref":"#/components/schemas/DatasetStatisticsResponse"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/key-value-store":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/key-value-store/keys":{"$ref":"#/components/schemas/ListOfKeysResponse"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/key-value-store/records/{recordKey}":{"$ref":"#/components/schemas/RecordResponse"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/request-queue":{"$ref":"#/components/schemas/RequestQueueResponse"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/head":{"$ref":"#/components/schemas/RequestQueueHeadResponse"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/requests":{"$ref":"#/components/schemas/ListOfRequestsResponse"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/requests/{requestId}":{"$ref":"#/components/schemas/RequestResponse"},"GET /v2/actor-tasks/{actorTaskId}/webhooks":{"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/WebhookResource"},"type":"array"}},"required":["items"],"type":"object"}]}},"required":["data"],"type":"object"},"GET /v2/actors":{"$ref":"#/components/schemas/ListOfActorsResponse"},"GET /v2/actors/{actorId}":{"$ref":"#/components/schemas/ActorResponse"},"GET /v2/actors/{actorId}/builds":{"$ref":"#/components/schemas/ListOfBuildsResponse"},"GET /v2/actors/{actorId}/builds/default":{"$ref":"#/components/schemas/BuildResponse"},"GET /v2/actors/{actorId}/builds/{buildId}":{"$ref":"#/components/schemas/BuildResponse"},"GET /v2/actors/{actorId}/builds/{buildId}/openapi.json":{"type":"object"},"GET /v2/actors/{actorId}/oauth-connections":{"$ref":"#/components/schemas/ListOfActorOAuthConnectionsResponse"},"GET /v2/actors/{actorId}/run-sync":{"type":"object"},"GET /v2/actors/{actorId}/run-sync-get-dataset-items":{"items":{"type":"object"},"type":"array"},"GET /v2/actors/{actorId}/runs":{"$ref":"#/components/schemas/ListOfRunsResponse"},"GET /v2/actors/{actorId}/runs/last":{"$ref":"#/components/schemas/RunResponse"},"GET /v2/actors/{actorId}/runs/last/dataset":{"$ref":"#/components/schemas/DatasetResponse"},"GET /v2/actors/{actorId}/runs/last/dataset/items":{"items":{"type":"object"},"type":"array"},"GET /v2/actors/{actorId}/runs/last/dataset/statistics":{"$ref":"#/components/schemas/DatasetStatisticsResponse"},"GET /v2/actors/{actorId}/runs/last/key-value-store":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"GET /v2/actors/{actorId}/runs/last/key-value-store/keys":{"$ref":"#/components/schemas/ListOfKeysResponse"},"GET /v2/actors/{actorId}/runs/last/key-value-store/records/{recordKey}":{"$ref":"#/components/schemas/RecordResponse"},"GET /v2/actors/{actorId}/runs/last/request-queue":{"$ref":"#/components/schemas/RequestQueueResponse"},"GET /v2/actors/{actorId}/runs/last/request-queue/head":{"$ref":"#/components/schemas/RequestQueueHeadResponse"},"GET /v2/actors/{actorId}/runs/last/request-queue/requests":{"$ref":"#/components/schemas/ListOfRequestsResponse"},"GET /v2/actors/{actorId}/runs/last/request-queue/requests/{requestId}":{"$ref":"#/components/schemas/RequestResponse"},"GET /v2/actors/{actorId}/runs/{runId}":{"$ref":"#/components/schemas/RunResponse"},"GET /v2/actors/{actorId}/versions":{"$ref":"#/components/schemas/ListOfVersionsResponse"},"GET /v2/actors/{actorId}/versions/{versionNumber}":{"$ref":"#/components/schemas/VersionResponse"},"GET /v2/actors/{actorId}/versions/{versionNumber}/env-vars":{"$ref":"#/components/schemas/ListOfEnvVarsResponse"},"GET /v2/actors/{actorId}/versions/{versionNumber}/env-vars/{envVarName}":{"$ref":"#/components/schemas/EnvVarResponse"},"GET /v2/actors/{actorId}/webhooks":{"$ref":"#/components/schemas/ListOfWebhookResourcesResponse"},"GET /v2/browser-info":{"$ref":"#/components/schemas/BrowserInfoResponse"},"GET /v2/datasets":{"$ref":"#/components/schemas/ListOfDatasetsResponse"},"GET /v2/datasets/{datasetId}":{"$ref":"#/components/schemas/DatasetResponse"},"GET /v2/datasets/{datasetId}/items":{"items":{"type":"object"},"type":"array"},"GET /v2/datasets/{datasetId}/statistics":{"$ref":"#/components/schemas/DatasetStatisticsResponse"},"GET /v2/integrations/git":{"$ref":"#/components/schemas/ListOfGitProviderIntegrationsResponse"},"GET /v2/key-value-stores":{"$ref":"#/components/schemas/ListOfKeyValueStoresResponse"},"GET /v2/key-value-stores/{storeId}":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"GET /v2/key-value-stores/{storeId}/keys":{"$ref":"#/components/schemas/ListOfKeysResponse"},"GET /v2/key-value-stores/{storeId}/records/{recordKey}":{"$ref":"#/components/schemas/RecordResponse"},"GET /v2/request-queues":{"$ref":"#/components/schemas/ListOfRequestQueuesResponse"},"GET /v2/request-queues/{queueId}":{"$ref":"#/components/schemas/RequestQueueResponse"},"GET /v2/request-queues/{queueId}/head":{"$ref":"#/components/schemas/RequestQueueHeadResponse"},"GET /v2/request-queues/{queueId}/requests":{"$ref":"#/components/schemas/ListOfRequestsResponse"},"GET /v2/request-queues/{queueId}/requests/{requestId}":{"$ref":"#/components/schemas/RequestResponse"},"GET /v2/schedules":{"$ref":"#/components/schemas/ListOfSchedulesResponse"},"GET /v2/schedules/{scheduleId}":{"$ref":"#/components/schemas/ScheduleResponse"},"GET /v2/schedules/{scheduleId}/log":{"$ref":"#/components/schemas/ScheduleLogResponse"},"GET /v2/store":{"$ref":"#/components/schemas/ListOfStoreActorsResponse"},"GET /v2/users/me":{"$ref":"#/components/schemas/UserPrivateInfoResponse"},"GET /v2/users/me/limits":{"$ref":"#/components/schemas/AccountLimitsResponse"},"GET /v2/users/me/usage/monthly":{"$ref":"#/components/schemas/MonthlyUsageResponse"},"GET /v2/users/{userId}":{"anyOf":[{"$ref":"#/components/schemas/UserPublicInfoResponse"},{"$ref":"#/components/schemas/UserPrivateInfoResponse"}]},"GET /v2/users/{userId}/limits":{"$ref":"#/components/schemas/AccountLimitsResponse"},"GET /v2/users/{userId}/usage/monthly":{"$ref":"#/components/schemas/MonthlyUsageResponse"},"GET /v2/webhook-dispatches":{"$ref":"#/components/schemas/ListOfWebhookDispatchesResponse"},"GET /v2/webhook-dispatches/{dispatchId}":{"$ref":"#/components/schemas/WebhookDispatchResponse"},"GET /v2/webhooks":{"$ref":"#/components/schemas/ListOfWebhooksResponse"},"GET /v2/webhooks/{webhookId}":{"$ref":"#/components/schemas/WebhookResponse"},"GET /v2/webhooks/{webhookId}/dispatches":{"$ref":"#/components/schemas/ListOfWebhookDispatchesResponse"},"POST /v2/actor-builds/{buildId}/abort":{"allOf":[{"$ref":"#/components/schemas/BuildResponse"}]},"POST /v2/actor-runs/{runId}/abort":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actor-runs/{runId}/charge":{"type":"object"},"POST /v2/actor-runs/{runId}/dataset/items":{"type":"object"},"POST /v2/actor-runs/{runId}/key-value-store/records/{recordKey}":{"type":"object"},"POST /v2/actor-runs/{runId}/metamorph":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actor-runs/{runId}/reboot":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actor-runs/{runId}/request-queue/head/lock":{"$ref":"#/components/schemas/LockedRequestQueueHeadResponse"},"POST /v2/actor-runs/{runId}/request-queue/requests":{"$ref":"#/components/schemas/AddRequestResponse"},"POST /v2/actor-runs/{runId}/request-queue/requests/batch":{"$ref":"#/components/schemas/BatchAddResponse"},"POST /v2/actor-runs/{runId}/request-queue/requests/unlock":{"$ref":"#/components/schemas/UnlockRequestsResponse"},"POST /v2/actor-runs/{runId}/resurrect":{"allOf":[{"$ref":"#/components/schemas/RunResponse"}]},"POST /v2/actor-tasks":{"$ref":"#/components/schemas/TaskResponse"},"POST /v2/actor-tasks/{actorTaskId}/run-sync":{"type":"object"},"POST /v2/actor-tasks/{actorTaskId}/run-sync-get-dataset-items":{"type":"object"},"POST /v2/actor-tasks/{actorTaskId}/runs":{"properties":{"data":{"$ref":"#/components/schemas/Run"}},"required":["data"],"type":"object"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/abort":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/dataset/items":{"type":"object"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/key-value-store/records/{recordKey}":{"type":"object"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/metamorph":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/reboot":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/head/lock":{"$ref":"#/components/schemas/LockedRequestQueueHeadResponse"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/requests":{"$ref":"#/components/schemas/AddRequestResponse"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/requests/batch":{"$ref":"#/components/schemas/BatchAddResponse"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/requests/unlock":{"$ref":"#/components/schemas/UnlockRequestsResponse"},"POST /v2/actors":{"$ref":"#/components/schemas/ActorResponse"},"POST /v2/actors/{actorId}/builds":{"$ref":"#/components/schemas/BuildResponse"},"POST /v2/actors/{actorId}/builds/{buildId}/abort":{"$ref":"#/components/schemas/BuildResponse"},"POST /v2/actors/{actorId}/run-sync":{"type":"object"},"POST /v2/actors/{actorId}/run-sync-get-dataset-items":{"items":{"type":"object"},"type":"array"},"POST /v2/actors/{actorId}/runs":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actors/{actorId}/runs/last/abort":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actors/{actorId}/runs/last/dataset/items":{"type":"object"},"POST /v2/actors/{actorId}/runs/last/key-value-store/records/{recordKey}":{"type":"object"},"POST /v2/actors/{actorId}/runs/last/metamorph":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actors/{actorId}/runs/last/reboot":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actors/{actorId}/runs/last/request-queue/head/lock":{"$ref":"#/components/schemas/LockedRequestQueueHeadResponse"},"POST /v2/actors/{actorId}/runs/last/request-queue/requests":{"$ref":"#/components/schemas/AddRequestResponse"},"POST /v2/actors/{actorId}/runs/last/request-queue/requests/batch":{"$ref":"#/components/schemas/BatchAddResponse"},"POST /v2/actors/{actorId}/runs/last/request-queue/requests/unlock":{"$ref":"#/components/schemas/UnlockRequestsResponse"},"POST /v2/actors/{actorId}/runs/{runId}/abort":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actors/{actorId}/runs/{runId}/metamorph":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actors/{actorId}/runs/{runId}/resurrect":{"allOf":[{"$ref":"#/components/schemas/RunResponse"}]},"POST /v2/actors/{actorId}/validate-input":{"properties":{"valid":{"type":"boolean"}},"required":["valid"],"type":"object"},"POST /v2/actors/{actorId}/versions":{"$ref":"#/components/schemas/VersionResponse"},"POST /v2/actors/{actorId}/versions/{versionNumber}":{"$ref":"#/components/schemas/VersionResponse"},"POST /v2/actors/{actorId}/versions/{versionNumber}/env-vars":{"$ref":"#/components/schemas/EnvVarResponse"},"POST /v2/actors/{actorId}/versions/{versionNumber}/env-vars/{envVarName}":{"$ref":"#/components/schemas/EnvVarResponse"},"POST /v2/browser-info":{"$ref":"#/components/schemas/BrowserInfoResponse"},"POST /v2/datasets":{"$ref":"#/components/schemas/DatasetResponse"},"POST /v2/datasets/{datasetId}/items":{"type":"object"},"POST /v2/integrations/git/{providerId}/create-repo":{"$ref":"#/components/schemas/GitRepoResponse"},"POST /v2/integrations/git/{providerId}/register-deployment-key":{"$ref":"#/components/schemas/RegisterDeploymentKeyResponse"},"POST /v2/key-value-stores":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"POST /v2/key-value-stores/{storeId}/records/{recordKey}":{"type":"object"},"POST /v2/request-queues":{"$ref":"#/components/schemas/RequestQueueResponse"},"POST /v2/request-queues/{queueId}/head/lock":{"$ref":"#/components/schemas/LockedRequestQueueHeadResponse"},"POST /v2/request-queues/{queueId}/requests":{"$ref":"#/components/schemas/AddRequestResponse"},"POST /v2/request-queues/{queueId}/requests/batch":{"$ref":"#/components/schemas/BatchAddResponse"},"POST /v2/request-queues/{queueId}/requests/unlock":{"$ref":"#/components/schemas/UnlockRequestsResponse"},"POST /v2/schedules":{"$ref":"#/components/schemas/ScheduleResponse"},"POST /v2/schedules/{scheduleId}/invoke":{"type":"object"},"POST /v2/tools/decode-and-verify":{"$ref":"#/components/schemas/DecodeAndVerifyResponse"},"POST /v2/tools/encode-and-sign":{"$ref":"#/components/schemas/EncodeAndSignResponse"},"POST /v2/webhooks":{"$ref":"#/components/schemas/WebhookResponse"},"POST /v2/webhooks/{webhookId}/test":{"$ref":"#/components/schemas/WebhookDispatchResponse"},"PUT /v2/actor-runs/{runId}":{"$ref":"#/components/schemas/RunResponse"},"PUT /v2/actor-runs/{runId}/dataset":{"$ref":"#/components/schemas/DatasetResponse"},"PUT /v2/actor-runs/{runId}/key-value-store":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"PUT /v2/actor-runs/{runId}/key-value-store/records/{recordKey}":{"type":"object"},"PUT /v2/actor-runs/{runId}/request-queue":{"$ref":"#/components/schemas/RequestQueueResponse"},"PUT /v2/actor-runs/{runId}/request-queue/requests/{requestId}":{"$ref":"#/components/schemas/UpdateRequestResponse"},"PUT /v2/actor-runs/{runId}/request-queue/requests/{requestId}/lock":{"$ref":"#/components/schemas/ProlongRequestLockResponse"},"PUT /v2/actor-tasks/{actorTaskId}":{"properties":{"data":{"$ref":"#/components/schemas/Task"}},"required":["data"],"type":"object"},"PUT /v2/actor-tasks/{actorTaskId}/input":{"type":"object"},"PUT /v2/actor-tasks/{actorTaskId}/runs/last/dataset":{"$ref":"#/components/schemas/DatasetResponse"},"PUT /v2/actor-tasks/{actorTaskId}/runs/last/key-value-store":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"PUT /v2/actor-tasks/{actorTaskId}/runs/last/key-value-store/records/{recordKey}":{"type":"object"},"PUT /v2/actor-tasks/{actorTaskId}/runs/last/request-queue":{"$ref":"#/components/schemas/RequestQueueResponse"},"PUT /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/requests/{requestId}":{"$ref":"#/components/schemas/UpdateRequestResponse"},"PUT /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/requests/{requestId}/lock":{"$ref":"#/components/schemas/ProlongRequestLockResponse"},"PUT /v2/actors/{actorId}":{"$ref":"#/components/schemas/ActorResponse"},"PUT /v2/actors/{actorId}/runs/last/dataset":{"$ref":"#/components/schemas/DatasetResponse"},"PUT /v2/actors/{actorId}/runs/last/key-value-store":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"PUT /v2/actors/{actorId}/runs/last/key-value-store/records/{recordKey}":{"type":"object"},"PUT /v2/actors/{actorId}/runs/last/request-queue":{"$ref":"#/components/schemas/RequestQueueResponse"},"PUT /v2/actors/{actorId}/runs/last/request-queue/requests/{requestId}":{"$ref":"#/components/schemas/UpdateRequestResponse"},"PUT /v2/actors/{actorId}/runs/last/request-queue/requests/{requestId}/lock":{"$ref":"#/components/schemas/ProlongRequestLockResponse"},"PUT /v2/actors/{actorId}/versions/{versionNumber}":{"$ref":"#/components/schemas/VersionResponse"},"PUT /v2/actors/{actorId}/versions/{versionNumber}/env-vars/{envVarName}":{"$ref":"#/components/schemas/EnvVarResponse"},"PUT /v2/browser-info":{"$ref":"#/components/schemas/BrowserInfoResponse"},"PUT /v2/datasets/{datasetId}":{"$ref":"#/components/schemas/DatasetResponse"},"PUT /v2/key-value-stores/{storeId}":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"PUT /v2/key-value-stores/{storeId}/records/{recordKey}":{"type":"object"},"PUT /v2/request-queues/{queueId}":{"$ref":"#/components/schemas/RequestQueueResponse"},"PUT /v2/request-queues/{queueId}/requests/{requestId}":{"$ref":"#/components/schemas/UpdateRequestResponse"},"PUT /v2/request-queues/{queueId}/requests/{requestId}/lock":{"$ref":"#/components/schemas/ProlongRequestLockResponse"},"PUT /v2/schedules/{scheduleId}":{"$ref":"#/components/schemas/ScheduleResponse"},"PUT /v2/users/me/limits":{"type":"object"},"PUT /v2/users/{userId}/limits":{"type":"object"},"PUT /v2/webhooks/{webhookId}":{"$ref":"#/components/schemas/WebhookResponse"}},"schemas":{"AccountLimits":{"properties":{"current":{"$ref":"#/components/schemas/CurrentUsage"},"limits":{"$ref":"#/components/schemas/Limits"},"monthlyUsageCycle":{"$ref":"#/components/schemas/UsageCycle"}},"required":["monthlyUsageCycle","limits","current"],"type":"object"},"AccountLimitsResponse":{"properties":{"data":{"$ref":"#/components/schemas/AccountLimits"}},"required":["data"],"type":"object"},"ActorDefinition":{"properties":{"actorSpecification":{"type":"integer"},"buildTag":{"type":"string"},"changelog":{"type":["string","null"]},"defaultMemoryMbytes":{"oneOf":[{"type":"string"},{"type":"integer"}]},"description":{"type":"string"},"dockerContextDir":{"type":"string"},"dockerfile":{"type":"string"},"environmentVariables":{"type":"object"},"input":{"type":["object","null"]},"maxMemoryMbytes":{"minimum":128,"type":"integer"},"meta":{"$ref":"#/components/schemas/ActorDefinitionMeta"},"minMemoryMbytes":{"minimum":128,"type":"integer"},"name":{"type":"string"},"output":{"type":["object","null"]},"readme":{"type":["string","null"]},"storages":{"properties":{"dataset":{"type":"object"},"datasets":{"type":"object"},"keyValueStore":{"type":"object"}},"type":"object"},"title":{"type":"string"},"usesStandbyMode":{"type":"boolean"},"version":{"pattern":"^[0-9]+(\\.[0-9]+)+$","type":"string"},"webServerMcpPath":{"type":"string"},"webServerSchema":{"type":"object"}},"type":"object"},"ActorDefinitionMeta":{"properties":{"generatedBy":{"type":"string"},"templateId":{"type":"string"}},"type":"object"},"ActorJobStatus":{"type":"string"},"ActorListItem":{"properties":{"createdAt":{"format":"date-time","type":"string"},"id":{"type":"string"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":"string"},"stats":{"anyOf":[{"$ref":"#/components/schemas/ActorStats"},{"type":"null"}]},"title":{"type":"string"},"username":{"type":"string"}},"required":["id","createdAt","modifiedAt","name","username"],"type":"object"},"ActorNotice":{"type":["string","null"]},"ActorOAuthAccount":{"properties":{"_id":{"type":"string"},"authorizedAt":{"format":"date-time","type":["string","null"]},"connectionId":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"data":{"type":"object"},"description":{"type":["string","null"]},"modifiedAt":{"format":"date-time","type":"string"},"userId":{"type":"string"}},"required":["_id","userId","connectionId","createdAt","modifiedAt","data"],"type":"object"},"ActorOAuthAccountResponse":{"properties":{"data":{"$ref":"#/components/schemas/ActorOAuthAccount"}},"required":["data"],"type":"object"},"ActorOAuthConnection":{"properties":{"_id":{"type":"string"},"actorId":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"credentials":{"properties":{"clientId":{"type":"string"},"imageUrl":{"type":"string"},"label":{"type":"string"},"scope":{"type":"string"}},"type":"object"},"modifiedAt":{"format":"date-time","type":"string"},"oAuthProviderType":{"type":"string"}},"required":["_id","actorId","oAuthProviderType","createdAt","modifiedAt"],"type":"object"},"ActorPermissionLevel":{"type":"string"},"ActorResource":{"properties":{"actorPermissionLevel":{"$ref":"#/components/schemas/ActorPermissionLevel"},"actorStandby":{"anyOf":[{"$ref":"#/components/schemas/ActorStandby"},{"type":"null"}]},"categories":{"items":{"type":"string"},"type":"array"},"createdAt":{"format":"date-time","type":"string"},"defaultRunOptions":{"$ref":"#/components/schemas/DefaultRunOptions"},"deploymentKey":{"type":"string"},"description":{"type":["string","null"]},"exampleRunInput":{"anyOf":[{"$ref":"#/components/schemas/ExampleRunInput"},{"type":"null"}]},"hasNoDataset":{"type":"boolean"},"id":{"type":"string"},"isCritical":{"type":"boolean"},"isDeprecated":{"type":["boolean","null"]},"isGeneric":{"type":"boolean"},"isPublic":{"type":"boolean"},"isSourceCodeHidden":{"type":"boolean"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":"string"},"notice":{"$ref":"#/components/schemas/ActorNotice"},"pictureUrl":{"type":["string","null"]},"pricingInfos":{"items":{"$ref":"#/components/schemas/ActorRunPricingInfo"},"type":"array"},"readmeSummary":{"type":"string"},"restartOnError":{"type":["boolean","null"]},"seoDescription":{"type":["string","null"]},"seoTitle":{"type":["string","null"]},"standbyUrl":{"type":["string","null"]},"stats":{"$ref":"#/components/schemas/ActorStats"},"taggedBuilds":{"anyOf":[{"$ref":"#/components/schemas/TaggedBuilds"},{"type":"null"}]},"title":{"type":["string","null"]},"userId":{"type":"string"},"username":{"type":"string"},"versions":{"items":{"$ref":"#/components/schemas/Version"},"type":"array"}},"required":["id","userId","name","username","isPublic","createdAt","modifiedAt","stats","versions","defaultRunOptions"],"type":"object"},"ActorResponse":{"properties":{"data":{"$ref":"#/components/schemas/ActorResource"}},"required":["data"],"type":"object"},"ActorRunPricingInfo":{"oneOf":[{"$ref":"#/components/schemas/PayPerEventActorPricingInfo"},{"$ref":"#/components/schemas/PricePerDatasetItemActorPricingInfo"},{"$ref":"#/components/schemas/FlatPricePerMonthActorPricingInfo"},{"$ref":"#/components/schemas/FreeActorPricingInfo"}]},"ActorStandby":{"properties":{"build":{"type":["string","null"]},"desiredRequestsPerActorRun":{"type":["integer","null"]},"disableStandbyFieldsOverride":{"type":["boolean","null"]},"idleTimeoutSecs":{"type":["integer","null"]},"isConsoleAuthEnabled":{"type":["boolean","null"]},"isEnabled":{"type":["boolean","null"]},"isTokenlessEnabled":{"type":["boolean","null"]},"maxRequestsPerActorRun":{"type":["integer","null"]},"memoryMbytes":{"type":["integer","null"]},"shouldPassActorInput":{"type":["boolean","null"]},"tenancy":{"type":["string","null"]}},"type":"object"},"ActorStats":{"properties":{"actorReviewCount":{"type":"integer"},"actorReviewRating":{"type":"number"},"bookmarkCount":{"type":"integer"},"lastRunStartedAt":{"format":"date-time","type":"string"},"publicActorRunStats30Days":{"properties":{"ABORTED":{"type":"integer"},"FAILED":{"type":"integer"},"SUCCEEDED":{"type":"integer"},"TIMED-OUT":{"type":"integer"},"TOTAL":{"type":"integer"}},"type":"object"},"totalBuilds":{"type":"integer"},"totalMetamorphs":{"type":"integer"},"totalRuns":{"type":"integer"},"totalUsers":{"type":"integer"},"totalUsers30Days":{"type":"integer"},"totalUsers7Days":{"type":"integer"},"totalUsers90Days":{"type":"integer"}},"type":"object"},"AddRequestResponse":{"properties":{"data":{"$ref":"#/components/schemas/RequestRegistration"}},"required":["data"],"type":"object"},"AddedRequest":{"properties":{"requestId":{"$ref":"#/components/schemas/RequestId"},"uniqueKey":{"$ref":"#/components/schemas/UniqueKey"},"wasAlreadyHandled":{"$ref":"#/components/schemas/WasAlreadyHandled"},"wasAlreadyPresent":{"$ref":"#/components/schemas/WasAlreadyPresent"}},"required":["requestId","uniqueKey","wasAlreadyHandled","wasAlreadyPresent"],"type":"object"},"AvailableProxyGroups":{"type":"object"},"BatchAddResponse":{"properties":{"data":{"$ref":"#/components/schemas/BatchAddResult"}},"required":["data"],"type":"object"},"BatchAddResult":{"properties":{"processedRequests":{"items":{"$ref":"#/components/schemas/AddedRequest"},"type":"array"},"unprocessedRequests":{"items":{"$ref":"#/components/schemas/UnprocessedRequest"},"type":"array"}},"required":["processedRequests","unprocessedRequests"],"type":"object"},"BatchDeleteResponse":{"properties":{"data":{"$ref":"#/components/schemas/BatchDeleteResult"}},"required":["data"],"type":"object"},"BatchDeleteResult":{"properties":{"processedRequests":{"items":{"$ref":"#/components/schemas/DeletedRequest"},"type":"array"},"unprocessedRequests":{"items":{"$ref":"#/components/schemas/UnprocessedRequest"},"type":"array"}},"required":["processedRequests","unprocessedRequests"],"type":"object"},"BrowserInfoResponse":{"properties":{"bodyLength":{"type":"integer"},"clientIp":{"type":["string","null"]},"countryCode":{"type":["string","null"]},"headers":{"type":"object"},"method":{"type":"string"},"rawHeaders":{"items":{"type":"string"},"type":"array"}},"required":["method","clientIp","countryCode","bodyLength"],"type":"object"},"Build":{"properties":{"actId":{"type":"string"},"actVersion":{"properties":{"actorPath":{"type":["string","null"]},"buildTag":{"type":["string","null"]},"gitHubGistUrl":{"type":"string"},"gitRepoUrl":{"type":"string"},"sourceFiles":{"items":{"$ref":"#/components/schemas/SourceCodeFile"},"type":"array"},"sourceType":{"$ref":"#/components/schemas/VersionSourceType"},"tarballUrl":{"type":"string"},"versionNumber":{"pattern":"^([0-9]|[1-9][0-9])\\.([0-9]|[1-9][0-9])$","type":"string"}},"type":"object"},"actorDefinition":{"anyOf":[{"$ref":"#/components/schemas/ActorDefinition"},{"type":"null"}]},"buildNumber":{"pattern":"^([0-9]|[1-9][0-9])\\.([0-9]|[1-9][0-9])(\\.[1-9][0-9]{0,4})$","type":"string"},"changelog":{"type":["string","null"]},"createdByOrganizationMemberUserId":{"type":"string"},"exitCode":{"type":["integer","null"]},"finishedAt":{"format":"date-time","type":["string","null"]},"gitBranchName":{"type":"string"},"gitCommitId":{"type":"string"},"id":{"type":"string"},"imageDigest":{"type":["string","null"]},"inputSchema":{"type":["string","null"]},"meta":{"$ref":"#/components/schemas/BuildMeta"},"options":{"anyOf":[{"$ref":"#/components/schemas/BuildOptions"},{"type":"null"}]},"readme":{"type":["string","null"]},"startedAt":{"format":"date-time","type":"string"},"stats":{"anyOf":[{"$ref":"#/components/schemas/BuildStats"},{"type":"null"}]},"status":{"$ref":"#/components/schemas/ActorJobStatus"},"usage":{"anyOf":[{"$ref":"#/components/schemas/BuildUsage"},{"type":"null"}]},"usageTotalUsd":{"type":["number","null"]},"usageUsd":{"anyOf":[{"$ref":"#/components/schemas/BuildUsage"},{"type":"null"}]},"userId":{"type":"string"}},"required":["id","actId","userId","startedAt","status","meta","buildNumber"],"type":"object"},"BuildListItem":{"properties":{"actId":{"type":"string"},"buildNumber":{"pattern":"^([0-9]|[1-9][0-9])\\.([0-9]|[1-9][0-9])(\\.[1-9][0-9]{0,4})$","type":"string"},"buildNumberInt":{"type":"integer"},"finishedAt":{"format":"date-time","type":["string","null"]},"id":{"type":"string"},"meta":{"$ref":"#/components/schemas/BuildMeta"},"startedAt":{"format":"date-time","type":"string"},"status":{"$ref":"#/components/schemas/ActorJobStatus"},"usageTotalUsd":{"type":"number"},"userId":{"type":"string"}},"required":["id","status","startedAt","buildNumber"],"type":"object"},"BuildMeta":{"properties":{"actorRunId":{"type":"string"},"clientIp":{"type":["string"]},"githubActionsRunUrl":{"type":"string"},"origin":{"$ref":"#/components/schemas/RunOrigin"},"userAgent":{"type":["string"]},"webhookDispatchId":{"type":"string"}},"required":["origin"],"type":"object"},"BuildOptions":{"properties":{"betaPackages":{"type":["boolean","null"]},"debug":{"type":["boolean","null"]},"diskMbytes":{"type":["integer","null"]},"memoryMbytes":{"type":["integer","null"]},"tag":{"type":["string","null"]},"useCache":{"type":["boolean","null"]}},"type":"object"},"BuildResponse":{"properties":{"data":{"$ref":"#/components/schemas/Build"}},"required":["data"],"type":"object"},"BuildStats":{"properties":{"computeUnits":{"type":"number"},"durationMillis":{"type":"integer"},"imageSizeBytes":{"type":["integer","null"]},"runTimeSecs":{"type":"number"}},"type":"object"},"BuildUsage":{"properties":{"ACTOR_COMPUTE_UNITS":{"type":["number","null"]}},"type":"object"},"CommonActorPricingInfo":{"properties":{"apifyMarginPercentage":{"type":"number"},"createdAt":{"format":"date-time","type":"string"},"forceContainsSignificantPriceChange":{"type":"boolean"},"isPriceChangeNotificationSuppressed":{"type":"boolean"},"notifiedAboutChangeAt":{"format":"date-time","type":["string","null"]},"notifiedAboutFutureChangeAt":{"format":"date-time","type":["string","null"]},"notifiedAboutRentalToPpePriceChangeAt":{"format":"date-time","type":["string","null"]},"reasonForChange":{"type":["string","null"]},"startedAt":{"format":"date-time","type":"string"}},"required":["apifyMarginPercentage","createdAt","startedAt"],"type":"object"},"CurrentPricingInfo":{"properties":{"apifyMarginPercentage":{"type":"number"},"createdAt":{"format":"date-time","type":"string"},"forceContainsSignificantPriceChange":{"type":"boolean"},"isPPEPlatformUsagePaidByUser":{"type":"boolean"},"isPriceChangeNotificationSuppressed":{"type":"boolean"},"minimalMaxTotalChargeUsd":{"type":["number","null"]},"notifiedAboutChangeAt":{"format":"date-time","type":["string","null"]},"notifiedAboutFutureChangeAt":{"format":"date-time","type":["string","null"]},"notifiedAboutRentalToPpePriceChangeAt":{"format":"date-time","type":["string","null"]},"pricePerUnitUsd":{"type":"number"},"pricingModel":{"type":"string"},"pricingPerEvent":{"type":["object","null"]},"reasonForChange":{"type":["string","null"]},"startedAt":{"format":"date-time","type":"string"},"tieredPricing":{"type":"object"},"trialMinutes":{"type":"integer"},"unitName":{"type":"string"}},"required":["pricingModel"],"type":"object"},"CurrentUsage":{"properties":{"activeActorJobCount":{"type":"integer"},"actorCount":{"type":"integer"},"actorMemoryGbytes":{"type":"number"},"actorTaskCount":{"type":"integer"},"monthlyActorComputeUnits":{"type":"number"},"monthlyExternalDataTransferGbytes":{"type":"number"},"monthlyProxySerps":{"type":"integer"},"monthlyResidentialProxyGbytes":{"type":"number"},"monthlyUnblockerUnits":{"type":"number"},"monthlyUsageUsd":{"type":"number"},"scheduleCount":{"type":"integer"},"teamAccountSeatCount":{"type":"integer"}},"required":["monthlyUsageUsd","monthlyActorComputeUnits","monthlyExternalDataTransferGbytes","monthlyProxySerps","monthlyResidentialProxyGbytes","actorMemoryGbytes","actorCount","actorTaskCount","activeActorJobCount","teamAccountSeatCount"],"type":"object"},"DailyServiceUsage":{"properties":{"date":{"format":"date-time","type":"string"},"serviceUsage":{"$ref":"#/components/schemas/ServiceUsage"},"totalUsageCreditsUsd":{"type":"number"}},"required":["date","serviceUsage","totalUsageCreditsUsd"],"type":"object"},"DatasetListItem":{"properties":{"accessedAt":{"format":"date-time","type":"string"},"actId":{"type":["string","null"]},"actRunId":{"type":["string","null"]},"cleanItemCount":{"type":"integer"},"createdAt":{"format":"date-time","type":"string"},"generalAccess":{"anyOf":[{"$ref":"#/components/schemas/GeneralAccess"},{"type":"null"}]},"id":{"type":"string"},"itemCount":{"type":"integer"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":["string","null"]},"stats":{"$ref":"#/components/schemas/DatasetStats"},"title":{"type":["string","null"]},"userId":{"type":"string"},"username":{"type":"string"}},"required":["id","userId","createdAt","modifiedAt","accessedAt","itemCount","cleanItemCount"],"type":"object"},"DatasetResource":{"properties":{"accessedAt":{"format":"date-time","type":"string"},"actId":{"type":["string","null"]},"actRunId":{"type":["string","null"]},"cleanItemCount":{"minimum":0,"type":"integer"},"consoleUrl":{"format":"uri","type":"string"},"createdAt":{"format":"date-time","type":"string"},"fields":{"items":{"type":"string"},"type":["array","null"]},"generalAccess":{"anyOf":[{"$ref":"#/components/schemas/GeneralAccess"},{"type":"null"}]},"id":{"type":"string"},"itemCount":{"minimum":0,"type":"integer"},"itemsPublicUrl":{"format":"uri","type":"string"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":["string","null"]},"schema":{"type":["object","null"]},"stats":{"$ref":"#/components/schemas/DatasetStats"},"title":{"type":["string","null"]},"urlSigningSecretKey":{"type":["string","null"]},"userId":{"type":"string"}},"required":["id","userId","createdAt","modifiedAt","accessedAt","itemCount","cleanItemCount","consoleUrl"],"type":"object"},"DatasetResponse":{"properties":{"data":{"$ref":"#/components/schemas/DatasetResource"}},"required":["data"],"type":"object"},"DatasetStatistics":{"properties":{"fieldStatistics":{"type":["object","null"]}},"type":"object"},"DatasetStatisticsResponse":{"properties":{"data":{"$ref":"#/components/schemas/DatasetStatistics"}},"required":["data"],"type":"object"},"DatasetStats":{"properties":{"inflatedBytes":{"type":"integer"},"readCount":{"type":"integer"},"storageBytes":{"type":"integer"},"writeCount":{"type":"integer"}},"type":"object"},"DecodeAndVerifyResponse":{"properties":{"data":{"$ref":"#/components/schemas/DecodeAndVerifyResult"}},"required":["data"],"type":"object"},"DecodeAndVerifyResult":{"properties":{"decoded":{},"encodedByUserId":{"type":["string","null"]},"isVerifiedUser":{"type":"boolean"}},"required":["decoded","encodedByUserId","isVerifiedUser"],"type":"object"},"DefaultRunOptions":{"properties":{"build":{"type":"string"},"forcePermissionLevel":{"anyOf":[{"$ref":"#/components/schemas/ActorPermissionLevel"},{"type":"null"}]},"isMaxTotalChargeUsdSetByUser":{"type":["boolean","null"]},"maxItems":{"type":["integer","null"]},"maxTotalChargeUsd":{"type":["number","null"]},"memoryMbytes":{"type":"integer"},"restartOnError":{"type":["boolean","null"]},"timeoutSecs":{"type":"integer"}},"type":"object"},"DeletedRequest":{"anyOf":[{"$ref":"#/components/schemas/DeletedRequestById"},{"$ref":"#/components/schemas/DeletedRequestByUniqueKey"}]},"DeletedRequestById":{"properties":{"id":{"$ref":"#/components/schemas/RequestId"},"uniqueKey":{"$ref":"#/components/schemas/UniqueKey"}},"required":["id"],"type":"object"},"DeletedRequestByUniqueKey":{"properties":{"id":{"$ref":"#/components/schemas/RequestId"},"uniqueKey":{"$ref":"#/components/schemas/UniqueKey"}},"required":["uniqueKey"],"type":"object"},"EffectivePlatformFeature":{"properties":{"disabledReason":{"type":["string","null"]},"disabledReasonType":{"type":["string","null"]},"isEnabled":{"type":"boolean"},"isTrial":{"type":"boolean"},"trialExpirationAt":{"format":"date-time","type":["string","null"]}},"required":["isEnabled","disabledReason","disabledReasonType","isTrial","trialExpirationAt"],"type":"object"},"EffectivePlatformFeatures":{"properties":{"ACTORS":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"ACTORS_PUBLIC_ALL":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"ACTORS_PUBLIC_DEVELOPER":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"PROXY":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"PROXY_EXTERNAL_ACCESS":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"PROXY_RESIDENTIAL":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"PROXY_SERPS":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"SCHEDULER":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"STORAGE":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"WEBHOOKS":{"$ref":"#/components/schemas/EffectivePlatformFeature"}},"required":["ACTORS","STORAGE","SCHEDULER","PROXY","PROXY_EXTERNAL_ACCESS","PROXY_RESIDENTIAL","PROXY_SERPS","WEBHOOKS","ACTORS_PUBLIC_ALL","ACTORS_PUBLIC_DEVELOPER"],"type":"object"},"EncodeAndSignResponse":{"properties":{"data":{"$ref":"#/components/schemas/EncodeAndSignResult"}},"required":["data"],"type":"object"},"EncodeAndSignResult":{"properties":{"encoded":{"type":"string"}},"required":["encoded"],"type":"object"},"EnvVar":{"properties":{"isSecret":{"type":["boolean","null"]},"name":{"type":"string"},"value":{"type":"string"},"valueHash":{"type":"string"}},"required":["name"],"type":"object"},"EnvVarResponse":{"properties":{"data":{"$ref":"#/components/schemas/EnvVar"}},"required":["data"],"type":"object"},"ExampleRunInput":{"properties":{"body":{"type":"string"},"contentType":{"type":"string"}},"type":"object"},"FlatPricePerMonthActorPricingInfo":{"allOf":[{"$ref":"#/components/schemas/CommonActorPricingInfo"},{"properties":{"pricePerUnitUsd":{"type":"number"},"pricingModel":{"type":"string"},"trialMinutes":{"type":"integer"}},"required":["pricingModel","pricePerUnitUsd","trialMinutes"],"type":"object"}]},"FreeActorPricingInfo":{"allOf":[{"$ref":"#/components/schemas/CommonActorPricingInfo"},{"properties":{"pricingModel":{"type":"string"}},"required":["pricingModel"],"type":"object"}]},"GeneralAccess":{"type":"string"},"GitProviderIntegration":{"properties":{"addWorkspaceUrl":{"type":"string"},"id":{"type":"string"},"provider":{"type":"string"},"workspaces":{"items":{"$ref":"#/components/schemas/GitProviderWorkspace"},"type":"array"}},"required":["id","provider","workspaces"],"type":"object"},"GitProviderWorkspace":{"properties":{"id":{"type":"string"},"label":{"type":"string"}},"required":["id","label"],"type":"object"},"GitRepo":{"properties":{"htmlUrl":{"type":"string"},"httpsUrl":{"type":"string"},"sshUrl":{"type":"string"}},"required":["sshUrl","httpsUrl","htmlUrl"],"type":"object"},"GitRepoResponse":{"properties":{"data":{"$ref":"#/components/schemas/GitRepo"}},"required":["data"],"type":"object"},"HadMultipleClients":{"type":"boolean"},"HandledRequestCount":{"minimum":0,"type":"integer"},"HeadLimit":{"type":"integer"},"HttpMethod":{"type":"string"},"IntegrationTracking":{"properties":{"appId":{"type":"string"},"isAiTool":{"type":"boolean"},"platform":{"type":"string"}},"type":"object"},"KeyValueStoreKey":{"properties":{"key":{"type":"string"},"recordPublicUrl":{"format":"uri","type":"string"},"size":{"type":"integer"}},"required":["key","size","recordPublicUrl"],"type":"object"},"KeyValueStoreListItem":{"properties":{"accessedAt":{"format":"date-time","type":"string"},"actId":{"type":["string","null"]},"actRunId":{"type":["string","null"]},"createdAt":{"format":"date-time","type":"string"},"generalAccess":{"anyOf":[{"$ref":"#/components/schemas/GeneralAccess"},{"type":"null"}]},"id":{"type":"string"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":["string","null"]},"schema":{"type":["object","string","null"]},"stats":{"$ref":"#/components/schemas/KeyValueStoreListStats"},"title":{"type":["string","null"]},"userId":{"type":["string","null"]},"username":{"type":["string","null"]}},"required":["id","createdAt","modifiedAt","accessedAt"],"type":"object"},"KeyValueStoreListStats":{"properties":{"deleteCount":{"type":"integer"},"listCount":{"type":"integer"},"readCount":{"type":"integer"},"s3StorageBytes":{"type":"integer"},"writeCount":{"type":"integer"}},"type":"object"},"KeyValueStoreResource":{"properties":{"accessedAt":{"format":"date-time","type":"string"},"actId":{"type":["string","null"]},"actRunId":{"type":["string","null"]},"consoleUrl":{"format":"uri","type":"string"},"createdAt":{"format":"date-time","type":"string"},"generalAccess":{"anyOf":[{"$ref":"#/components/schemas/GeneralAccess"},{"type":"null"}]},"id":{"type":"string"},"keysPublicUrl":{"format":"uri","type":"string"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":["string","null"]},"recordsPublicUrl":{"format":"uri","type":"string"},"schema":{"type":["object","null"]},"stats":{"$ref":"#/components/schemas/KeyValueStoreStats"},"title":{"type":["string","null"]},"urlSigningSecretKey":{"type":["string","null"]},"userId":{"type":["string","null"]},"username":{"type":["string","null"]}},"required":["id","createdAt","modifiedAt","accessedAt"],"type":"object"},"KeyValueStoreResponse":{"properties":{"data":{"$ref":"#/components/schemas/KeyValueStoreResource"}},"required":["data"],"type":"object"},"KeyValueStoreStats":{"properties":{"deleteCount":{"type":"integer"},"listCount":{"type":"integer"},"readCount":{"type":"integer"},"s3StorageBytes":{"type":"integer"},"storageBytes":{"type":"integer"},"writeCount":{"type":"integer"}},"type":"object"},"Limits":{"properties":{"dataRetentionDays":{"type":"integer"},"maxActorCount":{"type":"integer"},"maxActorMemoryGbytes":{"type":"number"},"maxActorTaskCount":{"type":"integer"},"maxConcurrentActorJobs":{"type":"integer"},"maxMonthlyActorComputeUnits":{"type":"number"},"maxMonthlyExternalDataTransferGbytes":{"type":"number"},"maxMonthlyProxySerps":{"type":"integer"},"maxMonthlyResidentialProxyGbytes":{"type":"number"},"maxMonthlyUnblockerUnits":{"type":"number"},"maxMonthlyUsageUsd":{"type":"number"},"maxScheduleCount":{"type":"integer"},"maxTeamAccountSeatCount":{"type":"integer"}},"required":["maxMonthlyUsageUsd","maxMonthlyActorComputeUnits","maxMonthlyExternalDataTransferGbytes","maxMonthlyProxySerps","maxMonthlyResidentialProxyGbytes","maxActorMemoryGbytes","maxActorCount","maxActorTaskCount","maxConcurrentActorJobs","maxTeamAccountSeatCount","dataRetentionDays"],"type":"object"},"ListOfActorOAuthAccounts":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ActorOAuthAccount"},"type":"array"},"total":{"type":"integer"}},"required":["total","items"],"type":"object"},"ListOfActorOAuthAccountsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfActorOAuthAccounts"}},"required":["data"],"type":"object"},"ListOfActorOAuthConnections":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ActorOAuthConnection"},"type":"array"},"total":{"type":"integer"}},"required":["total","items"],"type":"object"},"ListOfActorOAuthConnectionsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfActorOAuthConnections"}},"required":["data"],"type":"object"},"ListOfActors":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/ActorListItem"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfActorsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfActors"}},"required":["data"],"type":"object"},"ListOfBuilds":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/BuildListItem"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfBuildsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfBuilds"}},"required":["data"],"type":"object"},"ListOfDatasets":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/DatasetListItem"},"type":"array"},"unnamed":{"type":"boolean"}},"required":["items"],"type":"object"}]},"ListOfDatasetsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfDatasets"}},"required":["data"],"type":"object"},"ListOfEnvVars":{"properties":{"items":{"items":{"$ref":"#/components/schemas/EnvVar"},"type":"array"},"total":{"type":"integer"}},"required":["total","items"],"type":"object"},"ListOfEnvVarsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfEnvVars"}},"required":["data"],"type":"object"},"ListOfGitProviderIntegrationsResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/GitProviderIntegration"},"type":"array"}},"required":["data"],"type":"object"},"ListOfKeyValueStores":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/KeyValueStoreListItem"},"type":"array"},"unnamed":{"type":"boolean"}},"required":["items"],"type":"object"}]},"ListOfKeyValueStoresResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfKeyValueStores"}},"required":["data"],"type":"object"},"ListOfKeys":{"properties":{"count":{"type":"integer"},"exclusiveStartKey":{"type":["string","null"]},"isTruncated":{"type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/KeyValueStoreKey"},"type":"array"},"limit":{"type":"integer"},"nextExclusiveStartKey":{"type":["string","null"]}},"required":["items","count","limit","isTruncated"],"type":"object"},"ListOfKeysResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfKeys"}},"required":["data"],"type":"object"},"ListOfRequestQueues":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/RequestQueueListItem"},"type":"array"},"unnamed":{"type":"boolean"}},"required":["items"],"type":"object"}]},"ListOfRequestQueuesResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfRequestQueues"}},"required":["data"],"type":"object"},"ListOfRequests":{"properties":{"cursor":{"type":"string"},"exclusiveStartId":{"type":"string"},"items":{"items":{"$ref":"#/components/schemas/RequestResource"},"type":"array"},"limit":{"type":"integer"},"nextCursor":{"type":"string"}},"required":["items","limit"],"type":"object"},"ListOfRequestsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfRequests"}},"required":["data"],"type":"object"},"ListOfRuns":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/RunListItem"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfRunsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfRuns"}},"required":["data"],"type":"object"},"ListOfSchedules":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/ScheduleListItem"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfSchedulesResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfSchedules"}},"required":["data"],"type":"object"},"ListOfStoreActors":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/StoreActor"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfStoreActorsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfStoreActors"}},"required":["data"],"type":"object"},"ListOfTasks":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/TaskListItem"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfTasksResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfTasks"}},"required":["data"],"type":"object"},"ListOfVersions":{"properties":{"items":{"items":{"$ref":"#/components/schemas/Version"},"type":"array"},"total":{"type":"integer"}},"required":["total","items"],"type":"object"},"ListOfVersionsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfVersions"}},"required":["data"],"type":"object"},"ListOfWebhookDispatches":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/WebhookDispatch"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfWebhookDispatchesResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfWebhookDispatches"}},"required":["data"],"type":"object"},"ListOfWebhookResources":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/WebhookResource"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfWebhookResourcesResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfWebhookResources"}},"required":["data"],"type":"object"},"ListOfWebhooks":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/WebhookListItem"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfWebhooksResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfWebhooks"}},"required":["data"],"type":"object"},"LockExpiresAt":{"format":"date-time","type":"string"},"LockedRequestQueueHead":{"properties":{"clientKey":{"type":"string"},"hadMultipleClients":{"$ref":"#/components/schemas/HadMultipleClients"},"items":{"items":{"$ref":"#/components/schemas/LockedRequestQueueHeadItem"},"type":"array"},"limit":{"$ref":"#/components/schemas/HeadLimit"},"lockSecs":{"type":"integer"},"queueHasLockedRequests":{"type":"boolean"},"queueModifiedAt":{"$ref":"#/components/schemas/QueueModifiedAt"}},"required":["limit","queueModifiedAt","hadMultipleClients","lockSecs","items"],"type":"object"},"LockedRequestQueueHeadItem":{"properties":{"id":{"$ref":"#/components/schemas/RequestId"},"lockExpiresAt":{"$ref":"#/components/schemas/LockExpiresAt"},"method":{"$ref":"#/components/schemas/RequestMethod"},"retryCount":{"$ref":"#/components/schemas/RetryCount"},"uniqueKey":{"$ref":"#/components/schemas/UniqueKey"},"url":{"$ref":"#/components/schemas/RequestUrl"}},"required":["id","uniqueKey","url","lockExpiresAt"],"type":"object"},"LockedRequestQueueHeadResponse":{"properties":{"data":{"$ref":"#/components/schemas/LockedRequestQueueHead"}},"required":["data"],"type":"object"},"MonthlyServiceUsage":{"type":"object"},"MonthlyUsage":{"properties":{"dailyServiceUsages":{"items":{"$ref":"#/components/schemas/DailyServiceUsage"},"type":"array"},"monthlyServiceUsage":{"$ref":"#/components/schemas/MonthlyServiceUsage"},"totalUsageCreditsUsdAfterVolumeDiscount":{"type":"number"},"totalUsageCreditsUsdBeforeVolumeDiscount":{"type":"number"},"usageCycle":{"$ref":"#/components/schemas/UsageCycle"}},"required":["usageCycle","monthlyServiceUsage","dailyServiceUsages","totalUsageCreditsUsdBeforeVolumeDiscount","totalUsageCreditsUsdAfterVolumeDiscount"],"type":"object"},"MonthlyUsageResponse":{"properties":{"data":{"$ref":"#/components/schemas/MonthlyUsage"}},"required":["data"],"type":"object"},"PaginationResponse":{"properties":{"count":{"minimum":0,"type":"integer"},"desc":{"type":"boolean"},"limit":{"minimum":1,"type":"integer"},"offset":{"minimum":0,"type":"integer"},"total":{"minimum":0,"type":"integer"}},"required":["total","offset","limit","desc","count"],"type":"object"},"PayPerEventActorPricingInfo":{"allOf":[{"$ref":"#/components/schemas/CommonActorPricingInfo"},{"properties":{"isPPEPlatformUsagePaidByUser":{"type":["boolean","null"]},"minimalMaxTotalChargeUsd":{"type":["number","null"]},"pricingModel":{"type":"string"},"pricingPerEvent":{"properties":{"actorChargeEvents":{"type":"object"}},"type":"object"}},"required":["pricingModel","pricingPerEvent"],"type":"object"}]},"PendingRequestCount":{"type":"integer"},"PricePerDatasetItemActorPricingInfo":{"allOf":[{"$ref":"#/components/schemas/CommonActorPricingInfo"},{"properties":{"pricePerUnitUsd":{"type":"number"},"pricingModel":{"type":"string"},"tieredPricing":{"$ref":"#/components/schemas/TieredPricingPerDatasetItem"},"unitName":{"type":"string"}},"required":["pricingModel","unitName"],"type":"object"}]},"ProlongRequestLockResponse":{"properties":{"data":{"$ref":"#/components/schemas/RequestLockInfo"}},"required":["data"],"type":"object"},"ProxyGroup":{"properties":{"availableCount":{"type":"integer"},"description":{"type":["string","null"]},"id":{"type":"string"},"name":{"type":"string"}},"required":["name","description"],"type":"object"},"ProxyResource":{"properties":{"groups":{"items":{"$ref":"#/components/schemas/ProxyGroup"},"type":"array"},"password":{"type":["string","null"]}},"required":["groups"],"type":"object"},"QueueAccessedAt":{"format":"date-time","type":"string"},"QueueCreatedAt":{"format":"date-time","type":"string"},"QueueId":{"type":"string"},"QueueModifiedAt":{"format":"date-time","type":"string"},"QueueUserId":{"type":"string"},"RecordResponse":{"type":"object"},"RegisterDeploymentKeyResponse":{"properties":{"data":{"properties":{"repoName":{"type":"string"}},"required":["repoName"],"type":"object"}},"required":["data"],"type":"object"},"RequestBase":{"properties":{"errorMessages":{"items":{"type":"string"},"type":["array","null"]},"handledAt":{"format":"date-time","type":["string","null"]},"headers":{"type":["object","null"]},"loadedUrl":{"type":["string","null"]},"method":{"$ref":"#/components/schemas/RequestMethod"},"noRetry":{"type":["boolean","null"]},"payload":{"type":["string","object","null"]},"retryCount":{"$ref":"#/components/schemas/RetryCount"},"uniqueKey":{"$ref":"#/components/schemas/UniqueKey"},"url":{"$ref":"#/components/schemas/RequestUrl"},"userData":{"$ref":"#/components/schemas/RequestUserData"}},"required":["uniqueKey","url"],"type":"object"},"RequestId":{"type":"string"},"RequestLockInfo":{"properties":{"lockExpiresAt":{"$ref":"#/components/schemas/LockExpiresAt"}},"required":["lockExpiresAt"],"type":"object"},"RequestMethod":{"$ref":"#/components/schemas/HttpMethod"},"RequestQueueHead":{"properties":{"hadMultipleClients":{"$ref":"#/components/schemas/HadMultipleClients"},"items":{"items":{"$ref":"#/components/schemas/RequestQueueHeadItem"},"type":"array"},"limit":{"$ref":"#/components/schemas/HeadLimit"},"queueModifiedAt":{"$ref":"#/components/schemas/QueueModifiedAt"}},"required":["limit","queueModifiedAt","hadMultipleClients","items"],"type":"object"},"RequestQueueHeadItem":{"properties":{"id":{"$ref":"#/components/schemas/RequestId"},"method":{"$ref":"#/components/schemas/RequestMethod"},"retryCount":{"$ref":"#/components/schemas/RetryCount"},"uniqueKey":{"$ref":"#/components/schemas/UniqueKey"},"url":{"$ref":"#/components/schemas/RequestUrl"}},"required":["id","uniqueKey","url"],"type":"object"},"RequestQueueHeadResponse":{"properties":{"data":{"$ref":"#/components/schemas/RequestQueueHead"}},"required":["data"],"type":"object"},"RequestQueueListItem":{"properties":{"accessedAt":{"$ref":"#/components/schemas/QueueAccessedAt"},"actId":{"type":["string","null"]},"actRunId":{"type":["string","null"]},"createdAt":{"$ref":"#/components/schemas/QueueCreatedAt"},"expireAt":{"format":"date-time","type":"string"},"generalAccess":{"anyOf":[{"$ref":"#/components/schemas/GeneralAccess"},{"type":"null"}]},"hadMultipleClients":{"$ref":"#/components/schemas/HadMultipleClients"},"handledRequestCount":{"$ref":"#/components/schemas/HandledRequestCount"},"id":{"$ref":"#/components/schemas/QueueId"},"modifiedAt":{"$ref":"#/components/schemas/QueueModifiedAt"},"name":{"type":["string","null"]},"pendingRequestCount":{"$ref":"#/components/schemas/PendingRequestCount"},"stats":{"$ref":"#/components/schemas/RequestQueueListStats"},"title":{"type":["string","null"]},"totalRequestCount":{"$ref":"#/components/schemas/TotalRequestCount"},"userId":{"$ref":"#/components/schemas/QueueUserId"},"username":{"type":"string"}},"required":["id","userId","createdAt","modifiedAt","accessedAt","totalRequestCount","handledRequestCount","pendingRequestCount","hadMultipleClients"],"type":"object"},"RequestQueueListStats":{"properties":{"deleteCount":{"type":"integer"},"dynamodbStorageBytes":{"type":"integer"},"headItemReadCount":{"type":"integer"},"readCount":{"type":"integer"},"s3StorageBytes":{"type":"integer"},"writeCount":{"type":"integer"}},"type":"object"},"RequestQueueResource":{"properties":{"accessedAt":{"$ref":"#/components/schemas/QueueAccessedAt"},"actId":{"type":["string","null"]},"actRunId":{"type":["string","null"]},"consoleUrl":{"format":"uri","type":"string"},"createdAt":{"$ref":"#/components/schemas/QueueCreatedAt"},"generalAccess":{"anyOf":[{"$ref":"#/components/schemas/GeneralAccess"},{"type":"null"}]},"hadMultipleClients":{"$ref":"#/components/schemas/HadMultipleClients"},"handledRequestCount":{"$ref":"#/components/schemas/HandledRequestCount"},"id":{"$ref":"#/components/schemas/QueueId"},"modifiedAt":{"$ref":"#/components/schemas/QueueModifiedAt"},"name":{"type":["string","null"]},"pendingRequestCount":{"$ref":"#/components/schemas/PendingRequestCount"},"stats":{"$ref":"#/components/schemas/RequestQueueStats"},"title":{"type":["string","null"]},"totalRequestCount":{"$ref":"#/components/schemas/TotalRequestCount"},"userId":{"$ref":"#/components/schemas/QueueUserId"}},"required":["id","userId","createdAt","modifiedAt","accessedAt","totalRequestCount","handledRequestCount","pendingRequestCount","hadMultipleClients","consoleUrl"],"type":"object"},"RequestQueueResponse":{"properties":{"data":{"$ref":"#/components/schemas/RequestQueueResource"}},"required":["data"],"type":"object"},"RequestQueueStats":{"properties":{"deleteCount":{"type":"integer"},"headItemReadCount":{"type":"integer"},"readCount":{"type":"integer"},"storageBytes":{"type":"integer"},"writeCount":{"type":"integer"}},"type":"object"},"RequestRegistration":{"properties":{"requestId":{"$ref":"#/components/schemas/RequestId"},"wasAlreadyHandled":{"$ref":"#/components/schemas/WasAlreadyHandled"},"wasAlreadyPresent":{"$ref":"#/components/schemas/WasAlreadyPresent"}},"required":["requestId","wasAlreadyPresent","wasAlreadyHandled"],"type":"object"},"RequestResource":{"allOf":[{"$ref":"#/components/schemas/RequestBase","required":["id"]},{"properties":{"id":{"$ref":"#/components/schemas/RequestId"},"lockByClient":{"type":"string"},"lockExpiresAt":{"$ref":"#/components/schemas/LockExpiresAt"}}}]},"RequestResponse":{"properties":{"data":{"$ref":"#/components/schemas/RequestResource"}},"required":["data"],"type":"object"},"RequestUrl":{"type":"string"},"RequestUserData":{"type":"object"},"RetryCount":{"type":"integer"},"Run":{"properties":{"accountedChargedEventCounts":{"type":["object","null"]},"actId":{"type":"string"},"actorTaskId":{"type":["string","null"]},"buildId":{"type":"string"},"buildNumber":{"pattern":"^([0-9]|[1-9][0-9])\\.([0-9]|[1-9][0-9])(\\.[1-9][0-9]{0,4})$","type":["string","null"]},"chargedEventCounts":{"type":"object"},"consoleUrl":{"format":"uri","type":"string"},"containerUrl":{"format":"uri","type":"string"},"createdByOrganizationMemberUserId":{"type":"string"},"defaultDatasetId":{"type":"string"},"defaultKeyValueStoreId":{"type":"string"},"defaultRequestQueueId":{"type":"string"},"eventUsage":{"type":"object"},"exitCode":{"type":["integer","null"]},"externalChargedTotalUsd":{"type":"number"},"finishedAt":{"format":"date-time","type":["string","null"]},"generalAccess":{"anyOf":[{"$ref":"#/components/schemas/GeneralAccess"},{"type":"null"}]},"gitBranchName":{"type":["string","null"]},"id":{"type":"string"},"integrationTracking":{"$ref":"#/components/schemas/IntegrationTracking"},"isContainerServerReady":{"type":["boolean","null"]},"isStatusMessageTerminal":{"type":["boolean","null"]},"meta":{"$ref":"#/components/schemas/RunMeta"},"metamorphs":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunMetamorphEvent"},"type":"array"},{"type":"null"}]},"options":{"$ref":"#/components/schemas/RunOptions"},"output":{"type":"object"},"platformUsageBillingModel":{"type":"string"},"pricingInfo":{"$ref":"#/components/schemas/ActorRunPricingInfo"},"standby":{"anyOf":[{"$ref":"#/components/schemas/RunStandby"},{"type":"null"}]},"startedAt":{"format":"date-time","type":"string"},"stats":{"$ref":"#/components/schemas/RunStats"},"status":{"$ref":"#/components/schemas/ActorJobStatus"},"statusMessage":{"type":["string","null"]},"storageIds":{"properties":{"datasets":{"properties":{"default":{"type":"string"}},"type":"object"},"keyValueStores":{"properties":{"default":{"type":"string"}},"type":"object"},"requestQueues":{"properties":{"default":{"type":"string"}},"type":"object"}},"type":"object"},"usage":{"anyOf":[{"$ref":"#/components/schemas/RunUsage"},{"type":"null"}]},"usageTotalUsd":{"type":["number","null"]},"usageUsd":{"anyOf":[{"$ref":"#/components/schemas/RunUsageUsd"},{"type":"null"}]},"userId":{"type":"string"}},"required":["id","actId","userId","startedAt","status","meta","stats","options","buildId","defaultKeyValueStoreId","defaultDatasetId","defaultRequestQueueId"],"type":"object"},"RunListItem":{"properties":{"actId":{"type":"string"},"actorTaskId":{"type":["string","null"]},"buildId":{"type":"string"},"buildNumber":{"pattern":"^([0-9]|[1-9][0-9])\\.([0-9]|[1-9][0-9])(\\.[1-9][0-9]{0,4})$","type":"string"},"buildNumberInt":{"type":"integer"},"defaultDatasetId":{"type":"string"},"defaultKeyValueStoreId":{"type":"string"},"defaultRequestQueueId":{"type":"string"},"finishedAt":{"format":"date-time","type":["string","null"]},"id":{"type":"string"},"meta":{"$ref":"#/components/schemas/RunMeta"},"standby":{"anyOf":[{"$ref":"#/components/schemas/RunStandby"},{"type":"null"}]},"startedAt":{"format":"date-time","type":"string"},"status":{"$ref":"#/components/schemas/ActorJobStatus"},"usageTotalUsd":{"type":"number"},"userId":{"type":"string"}},"required":["id","actId","status","startedAt","buildId","meta","usageTotalUsd","defaultKeyValueStoreId","defaultDatasetId","defaultRequestQueueId"],"type":"object"},"RunMeta":{"properties":{"actorRunId":{"type":"string"},"clientIp":{"type":["string","null"]},"origin":{"$ref":"#/components/schemas/RunOrigin"},"scheduleId":{"type":["string","null"]},"scheduledAct2Id":{"type":"string"},"scheduledActorTaskId":{"type":"string"},"scheduledAt":{"format":"date-time","type":["string","null"]},"userAgent":{"type":["string","null"]},"webhookDispatchId":{"type":"string"}},"required":["origin"],"type":"object"},"RunMetamorphEvent":{"properties":{"actorId":{"type":"string"},"buildId":{"type":"string"},"buildTags":{"items":{"type":"string"},"type":"array"},"createdAt":{"format":"date-time","type":"string"},"inputKey":{"type":["string","null"]}},"required":["createdAt","actorId","buildId"],"type":"object"},"RunOptions":{"properties":{"build":{"type":"string"},"diskMbytes":{"minimum":0,"type":"integer"},"isMaxTotalChargeUsdSetByUser":{"type":"boolean"},"maxItems":{"minimum":0,"type":["integer","null"]},"maxTotalChargeUsd":{"minimum":0,"type":["number","null"]},"memoryMbytes":{"maximum":32768,"minimum":128,"type":"integer"},"restartOnError":{"type":["boolean","null"]},"timeoutSecs":{"minimum":0,"type":"integer"}},"required":["build","timeoutSecs","memoryMbytes","diskMbytes"],"type":"object"},"RunOrigin":{"type":"string"},"RunResponse":{"properties":{"data":{"$ref":"#/components/schemas/Run"}},"required":["data"],"type":"object"},"RunStandby":{"properties":{"deployment":{"type":"string"}},"required":["deployment"],"type":"object"},"RunStats":{"properties":{"computeUnits":{"minimum":0,"type":"number"},"cpuAvgUsage":{"type":"number"},"cpuCurrentUsage":{"type":"number"},"cpuMaxUsage":{"type":"number"},"durationMillis":{"minimum":0,"type":"integer"},"inputBodyLen":{"minimum":0,"type":["integer","null"]},"memAvgBytes":{"type":"number"},"memCurrentBytes":{"minimum":0,"type":"integer"},"memMaxBytes":{"minimum":0,"type":"integer"},"metamorph":{"minimum":0,"type":"integer"},"migrationCount":{"minimum":0,"type":"integer"},"netRxBytes":{"minimum":0,"type":"integer"},"netTxBytes":{"minimum":0,"type":"integer"},"rebootCount":{"minimum":0,"type":"integer"},"restartCount":{"minimum":0,"type":"integer"},"resurrectCount":{"minimum":0,"type":"integer"},"runTimeSecs":{"minimum":0,"type":"number"}},"type":"object"},"RunUsage":{"properties":{"ACTOR_COMPUTE_UNITS":{"type":["number","null"]},"DATASET_READS":{"type":["integer","null"]},"DATASET_WRITES":{"type":["integer","null"]},"DATA_TRANSFER_EXTERNAL_GBYTES":{"type":["number","null"]},"DATA_TRANSFER_INTERNAL_GBYTES":{"type":["number","null"]},"KEY_VALUE_STORE_LISTS":{"type":["integer","null"]},"KEY_VALUE_STORE_READS":{"type":["integer","null"]},"KEY_VALUE_STORE_WRITES":{"type":["integer","null"]},"PROXY_RESIDENTIAL_TRANSFER_GBYTES":{"type":["number","null"]},"PROXY_SERPS":{"type":["integer","null"]},"PROXY_UNBLOCKER_UNITS":{"type":["number","null"]},"REQUEST_QUEUE_READS":{"type":["integer","null"]},"REQUEST_QUEUE_WRITES":{"type":["integer","null"]}},"type":"object"},"RunUsageUsd":{"properties":{"ACTOR_COMPUTE_UNITS":{"type":["number","null"]},"DATASET_READS":{"type":["number","null"]},"DATASET_WRITES":{"type":["number","null"]},"DATA_TRANSFER_EXTERNAL_GBYTES":{"type":["number","null"]},"DATA_TRANSFER_INTERNAL_GBYTES":{"type":["number","null"]},"KEY_VALUE_STORE_LISTS":{"type":["number","null"]},"KEY_VALUE_STORE_READS":{"type":["number","null"]},"KEY_VALUE_STORE_WRITES":{"type":["number","null"]},"PROXY_RESIDENTIAL_TRANSFER_GBYTES":{"type":["number","null"]},"PROXY_SERPS":{"type":["number","null"]},"PROXY_UNBLOCKER_UNITS":{"type":["number","null"]},"REQUEST_QUEUE_READS":{"type":["number","null"]},"REQUEST_QUEUE_WRITES":{"type":["number","null"]}},"type":"object"},"Schedule":{"allOf":[{"$ref":"#/components/schemas/ScheduleBase"},{"properties":{"actions":{"items":{"$ref":"#/components/schemas/ScheduleAction"},"type":"array"},"description":{"type":["string","null"]},"integrationTracking":{"$ref":"#/components/schemas/IntegrationTracking"},"notifications":{"$ref":"#/components/schemas/ScheduleNotifications"},"title":{"type":["string","null"]},"uiState":{"anyOf":[{"$ref":"#/components/schemas/ScheduleUiState"},{"type":"null"}]}},"required":["actions"],"type":"object"}]},"ScheduleAction":{"oneOf":[{"$ref":"#/components/schemas/ScheduleActionRunActor"},{"$ref":"#/components/schemas/ScheduleActionRunActorTask"}]},"ScheduleActionRunActor":{"properties":{"actorId":{"type":"string"},"id":{"type":"string"},"runInput":{"anyOf":[{"$ref":"#/components/schemas/ScheduleActionRunInput"},{"type":"null"}]},"runOptions":{"anyOf":[{"$ref":"#/components/schemas/TaskOptions"},{"type":"null"}]},"type":{}},"required":["id","type","actorId"],"type":"object"},"ScheduleActionRunActorTask":{"properties":{"actorTaskId":{"type":"string"},"id":{"type":"string"},"input":{"anyOf":[{"type":"object"},{"type":"null"}]},"type":{}},"required":["id","type","actorTaskId"],"type":"object"},"ScheduleActionRunInput":{"properties":{"body":{"type":["string","null"]},"contentType":{"type":["string","null"]}},"type":"object"},"ScheduleBase":{"properties":{"createdAt":{"format":"date-time","type":"string"},"cronExpression":{"type":"string"},"id":{"type":"string"},"isEnabled":{"type":"boolean"},"isExclusive":{"type":"boolean"},"lastRunAt":{"format":"date-time","type":["string","null"]},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":"string"},"nextRunAt":{"format":"date-time","type":["string","null"]},"timezone":{"type":"string"},"userId":{"type":"string"}},"required":["id","userId","name","cronExpression","timezone","isEnabled","isExclusive","createdAt","modifiedAt"],"type":"object"},"ScheduleListItem":{"allOf":[{"$ref":"#/components/schemas/ScheduleBase"},{"properties":{"actions":{"items":{"$ref":"#/components/schemas/ScheduleListItemAction"},"type":"array"},"integrationTracking":{"$ref":"#/components/schemas/IntegrationTracking"},"notifications":{"$ref":"#/components/schemas/ScheduleNotifications"},"title":{"type":["string","null"]}},"required":["actions"],"type":"object"}]},"ScheduleListItemAction":{"oneOf":[{"$ref":"#/components/schemas/ScheduleListItemActionRunActor"},{"$ref":"#/components/schemas/ScheduleListItemActionRunActorTask"}]},"ScheduleListItemActionRunActor":{"properties":{"actorId":{"type":"string"},"id":{"type":"string"},"type":{}},"required":["id","type","actorId"],"type":"object"},"ScheduleListItemActionRunActorTask":{"properties":{"actorTaskId":{"type":"string"},"id":{"type":"string"},"type":{}},"required":["id","type","actorTaskId"],"type":"object"},"ScheduleLogEntry":{"properties":{"createdAt":{"format":"date-time","type":"string"},"level":{"type":"string"},"message":{"type":"string"}},"required":["message","level","createdAt"],"type":"object"},"ScheduleLogResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ScheduleLogEntry"},"type":"array"}},"required":["data"],"type":"object"},"ScheduleNotifications":{"properties":{"email":{"type":"boolean"}},"type":"object"},"ScheduleResponse":{"properties":{"data":{"$ref":"#/components/schemas/Schedule"}},"required":["data"],"type":"object"},"ScheduleUiState":{"properties":{"data":{"properties":{"cronExpression":{"type":"string"},"timezone":{"type":"string"}},"type":"object"},"version":{"type":"integer"}},"type":"object"},"ServiceUsage":{"type":"object"},"SourceCodeFile":{"properties":{"content":{"type":"string"},"format":{"$ref":"#/components/schemas/SourceCodeFileFormat"},"name":{"type":"string"}},"required":["name"],"type":"object"},"SourceCodeFileFormat":{"type":"string"},"SourceCodeFolder":{"properties":{"folder":{"type":"boolean"},"name":{"type":"string"}},"required":["name","folder"],"type":"object"},"StoreActor":{"properties":{"actorReviewCount":{"type":"integer"},"actorReviewRating":{"type":"number"},"badge":{"type":["string","null"]},"bookmarkCount":{"type":"integer"},"categories":{"items":{"type":"string"},"type":"array"},"currentPricingInfo":{"$ref":"#/components/schemas/CurrentPricingInfo"},"description":{"type":["string","null"]},"id":{"type":"string"},"inputSchema":{"properties":{"properties":{"type":"object"},"required":{"items":{"type":"string"},"type":"array"},"type":{"type":"string"}},"required":["type","properties"],"type":"object"},"isWhiteListedForAgenticPayments":{"type":["boolean","null"]},"name":{"type":"string"},"notice":{"$ref":"#/components/schemas/ActorNotice"},"pictureUrl":{"type":["string","null"]},"readmeSummary":{"type":"string"},"stats":{"$ref":"#/components/schemas/ActorStats"},"title":{"type":"string"},"url":{"format":"uri","type":["string","null"]},"userFullName":{"type":["string","null"]},"userPictureUrl":{"format":"uri","type":["string","null"]},"username":{"type":"string"}},"required":["id","title","name","username","stats"],"type":"object"},"TaggedBuilds":{"type":"object"},"Task":{"properties":{"actId":{"type":"string"},"actorStandby":{"anyOf":[{"$ref":"#/components/schemas/ActorStandby"},{"type":"null"}]},"createdAt":{"format":"date-time","type":"string"},"description":{"type":["string","null"]},"id":{"type":"string"},"input":{"anyOf":[{"$ref":"#/components/schemas/TaskInput"},{"items":{"$ref":"#/components/schemas/TaskInput"},"type":"array"},{"type":"null"}]},"isPublic":{"type":"boolean"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":"string"},"nameLowerCase":{"type":"string"},"options":{"anyOf":[{"$ref":"#/components/schemas/TaskOptions"},{"type":"null"}]},"publicConfig":{"anyOf":[{"$ref":"#/components/schemas/TaskPublicConfig"},{"type":"null"}]},"removedAt":{"format":"date-time","type":["string","null"]},"standbyUrl":{"format":"uri","type":["string","null"]},"stats":{"anyOf":[{"$ref":"#/components/schemas/TaskStats"},{"type":"null"}]},"title":{"type":["string","null"]},"userId":{"type":"string"},"username":{"type":["string","null"]}},"required":["id","userId","actId","name","createdAt","modifiedAt"],"type":"object"},"TaskInput":{"type":"object"},"TaskListItem":{"properties":{"actId":{"type":"string"},"actName":{"type":["string","null"]},"actUsername":{"type":["string","null"]},"createdAt":{"format":"date-time","type":"string"},"id":{"type":"string"},"isPublic":{"type":"boolean"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":"string"},"stats":{"anyOf":[{"$ref":"#/components/schemas/TaskStats"},{"type":"null"}]},"title":{"type":["string","null"]},"userId":{"type":"string"},"username":{"type":["string","null"]}},"required":["id","userId","actId","name","createdAt","modifiedAt"],"type":"object"},"TaskOptions":{"properties":{"build":{"type":["string","null"]},"forcePermissionLevel":{"anyOf":[{"$ref":"#/components/schemas/ActorPermissionLevel"},{"type":"null"}]},"isMaxTotalChargeUsdSetByUser":{"type":["boolean","null"]},"maxItems":{"type":["integer","null"]},"maxTotalChargeUsd":{"type":["number","null"]},"memoryMbytes":{"type":["integer","null"]},"restartOnError":{"type":["boolean","null"]},"timeoutSecs":{"type":["integer","null"]}},"type":"object"},"TaskPublicConfig":{"properties":{"categorization":{"type":["string","null"]},"datasetName":{"type":["string","null"]},"datasetView":{"type":["string","null"]},"faq":{"items":{"properties":{"answer":{"type":"string"},"question":{"type":"string"}},"required":["question","answer"],"type":"object"},"type":["array","null"]},"highlightedFields":{"items":{"type":"string"},"type":["array","null"]},"inputSchemaFields":{"items":{"type":"string"},"type":["array","null"]},"publishedAt":{"format":"date-time","type":["string","null"]},"seoDescription":{"maxLength":160,"type":["string","null"]},"seoTitle":{"maxLength":60,"type":["string","null"]}},"type":"object"},"TaskResponse":{"properties":{"data":{"$ref":"#/components/schemas/Task"}},"required":["data"],"type":"object"},"TaskStats":{"properties":{"lastRunStartedAt":{"format":"date-time","type":["string","null"]},"totalRuns":{"type":"integer"}},"type":"object"},"TieredPricingPerDatasetItem":{"type":"object"},"TotalRequestCount":{"minimum":0,"type":"integer"},"UniqueKey":{"type":"string"},"UnlockRequestsResponse":{"properties":{"data":{"$ref":"#/components/schemas/UnlockRequestsResult"}},"required":["data"],"type":"object"},"UnlockRequestsResult":{"properties":{"unlockedCount":{"type":"integer"}},"required":["unlockedCount"],"type":"object"},"UnprocessedRequest":{"properties":{"id":{"$ref":"#/components/schemas/RequestId"},"method":{"$ref":"#/components/schemas/RequestMethod"},"uniqueKey":{"$ref":"#/components/schemas/UniqueKey"},"url":{"$ref":"#/components/schemas/RequestUrl"}},"required":["uniqueKey","url"],"type":"object"},"UpdateRequestResponse":{"properties":{"data":{"$ref":"#/components/schemas/RequestRegistration"}},"required":["data"],"type":"object"},"UsageCycle":{"properties":{"endAt":{"format":"date-time","type":"string"},"startAt":{"format":"date-time","type":"string"}},"required":["startAt","endAt"],"type":"object"},"UserPlan":{"properties":{"apiRateLimitBoosts":{"type":"integer"},"availableAddOns":{"items":{"type":"string"},"type":"array"},"availableProxyGroups":{"$ref":"#/components/schemas/AvailableProxyGroups"},"dataRetentionDays":{"type":"integer"},"description":{"type":"string"},"enabledPlatformFeatures":{"items":{"type":"string"},"type":"array"},"forceBillingInterval":{"type":"string"},"id":{"type":"string"},"isEnabled":{"type":"boolean"},"isPayingCustomer":{"type":"boolean"},"legacyPlanId":{"type":"string"},"maxActorCount":{"type":"integer"},"maxActorMemoryGbytes":{"type":"number"},"maxActorTaskCount":{"type":"integer"},"maxConcurrentActorRuns":{"type":"integer"},"maxMonthlyActorComputeUnits":{"type":"number"},"maxMonthlyExternalDataTransferGbytes":{"type":"number"},"maxMonthlyProxySerps":{"type":"integer"},"maxMonthlyResidentialProxyGbytes":{"type":"number"},"maxMonthlyUnblockerUnits":{"type":"integer"},"maxMonthlyUsageUsd":{"type":"number"},"maxPublicActorTaskCount":{"type":"integer"},"maxPublicActorTaskCountPerActor":{"type":"integer"},"maxScheduleCount":{"type":"integer"},"monitoringLevel":{"type":["string","null"]},"monthlyBasePriceUsd":{"type":"number"},"monthlyUsageCreditsUsd":{"type":"number"},"overageThresholdUsd":{"type":"number"},"personalizedSupport":{"type":["string","null"]},"planPricing":{"type":"object"},"planTags":{"items":{"type":"string"},"type":"array"},"planVersion":{"type":"number"},"supportLevel":{"type":"string"},"teamAccountSeatCount":{"type":"integer"},"tier":{"type":"string"},"usageDiscountPercent":{"type":"number"}},"required":["availableProxyGroups"],"type":"object"},"UserPrivateInfo":{"properties":{"createdAt":{"format":"date-time","type":"string"},"effectivePlatformFeatures":{"$ref":"#/components/schemas/EffectivePlatformFeatures"},"email":{"format":"email","type":["string","null"]},"id":{"type":"string"},"isPaying":{"type":"boolean"},"organizationOwnerUserId":{"type":["string","null"]},"plan":{"$ref":"#/components/schemas/UserPlan"},"profile":{"$ref":"#/components/schemas/UserProfile"},"proxy":{"$ref":"#/components/schemas/ProxyResource"},"username":{"type":"string"}},"required":["username"],"type":"object"},"UserPrivateInfoResponse":{"properties":{"data":{"$ref":"#/components/schemas/UserPrivateInfo"}},"required":["data"],"type":"object"},"UserProfile":{"properties":{"bio":{"type":["string","null"]},"githubUsername":{"type":["string","null"]},"name":{"type":"string"},"pictureUrl":{"format":"uri","type":["string","null"]},"readme":{"type":["string","null"]},"twitterUsername":{"type":["string","null"]},"websiteUrl":{"format":"uri","type":["string","null"]}},"type":"object"},"UserPublicInfo":{"properties":{"profile":{"$ref":"#/components/schemas/UserProfile"},"username":{"type":"string"}},"required":["username"],"type":"object"},"UserPublicInfoResponse":{"properties":{"data":{"$ref":"#/components/schemas/UserPublicInfo"}},"required":["data"],"type":"object"},"Version":{"properties":{"actorPath":{"maxLength":255,"minLength":1,"type":["string","null"]},"applyEnvVarsToBuild":{"type":["boolean","null"]},"bitbucketWebhookId":{"type":["string","null"]},"buildTag":{"type":["string","null"]},"envVars":{"anyOf":[{"items":{"$ref":"#/components/schemas/EnvVar"},"type":"array"},{"type":"null"}]},"gitHubGistUrl":{"type":["string","null"]},"gitRepoUrl":{"type":["string","null"]},"githubWebhookId":{"type":["integer","null"]},"gitlabWebhookId":{"type":["integer","null"]},"sourceFiles":{"$ref":"#/components/schemas/VersionSourceFiles"},"sourceType":{"anyOf":[{"$ref":"#/components/schemas/VersionSourceType"},{"type":"null"}]},"tarballUrl":{"type":["string","null"]},"versionNumber":{"pattern":"^([0-9]|[1-9][0-9])\\.([0-9]|[1-9][0-9])$","type":"string"}},"required":["versionNumber","sourceType"],"type":"object"},"VersionResponse":{"properties":{"data":{"$ref":"#/components/schemas/Version"}},"required":["data"],"type":"object"},"VersionSourceFiles":{"items":{"anyOf":[{"$ref":"#/components/schemas/SourceCodeFile"},{"$ref":"#/components/schemas/SourceCodeFolder"}]},"type":"array"},"VersionSourceType":{"type":"string"},"WasAlreadyHandled":{"type":"boolean"},"WasAlreadyPresent":{"type":"boolean"},"WebhookCondition":{"properties":{"actorId":{"type":["string","null"]},"actorRunId":{"type":["string","null"]},"actorTaskId":{"type":["string","null"]}},"type":"object"},"WebhookDatasetTransformation":{"properties":{"fields":{"items":{"type":"string"},"type":"array"},"omit":{"items":{"type":"string"},"type":"array"}},"type":"object"},"WebhookDispatch":{"properties":{"calls":{"items":{"properties":{"debugPostMessage":{"type":"object"},"errorMessage":{"type":["string","null"]},"finishedAt":{"format":"date-time","type":["string","null"]},"responseBody":{"type":["string","null"]},"responseStatus":{"type":["integer","null"]},"startedAt":{"format":"date-time","type":["string","null"]}},"type":"object"},"type":"array"},"createdAt":{"format":"date-time","type":"string"},"eventData":{"properties":{"actorBuildId":{"type":"string"},"actorId":{"type":"string"},"actorRunId":{"type":"string"},"actorTaskId":{"type":["string","null"]}},"required":["actorId"],"type":["object","null"]},"eventType":{"$ref":"#/components/schemas/WebhookEventType"},"id":{"type":"string"},"status":{"$ref":"#/components/schemas/WebhookDispatchStatus"},"userId":{"type":"string"},"webhook":{"anyOf":[{"$ref":"#/components/schemas/WebhookDispatchWebhookSummary"},{"type":"null"}]},"webhookId":{"type":"string"}},"required":["id","userId","webhookId","createdAt","status","eventType"],"type":"object"},"WebhookDispatchResponse":{"properties":{"data":{"$ref":"#/components/schemas/WebhookDispatch"}},"required":["data"],"type":"object"},"WebhookDispatchStatus":{"type":"string"},"WebhookDispatchWebhookSummary":{"properties":{"_id":{"type":"string"},"actionType":{"type":"string"},"condition":{"$ref":"#/components/schemas/WebhookCondition"},"githubIssue":{"$ref":"#/components/schemas/WebhookGithubIssue"},"googleDrive":{"$ref":"#/components/schemas/WebhookGoogleDrive"},"googleMail":{"$ref":"#/components/schemas/WebhookGoogleMail"},"isAdHoc":{"type":"boolean"},"isApifyIntegration":{"type":["boolean","null"]},"requestUrl":{"format":"uri","type":["string","null"]},"slackMessage":{"$ref":"#/components/schemas/WebhookSlackMessage"}},"type":"object"},"WebhookEventType":{"type":"string"},"WebhookGithubIssue":{"properties":{"body":{"type":"string"},"repository":{"type":"string"},"title":{"type":"string"},"userIntegrationId":{"type":"string"}},"type":"object"},"WebhookGoogleDrive":{"properties":{"filename":{"type":"string"},"format":{"type":["string","null"]},"transformation":{"$ref":"#/components/schemas/WebhookDatasetTransformation"},"userIntegrationId":{"type":"string"}},"type":"object"},"WebhookGoogleMail":{"properties":{"attachmentFormat":{"type":"string"},"attachmentTransformation":{"$ref":"#/components/schemas/WebhookDatasetTransformation"},"body":{"type":"string"},"emailTo":{"items":{"type":"string"},"type":"array"},"subject":{"type":"string"},"userIntegrationId":{"type":"string"}},"type":"object"},"WebhookLastDispatch":{"properties":{"finishedAt":{"format":"date-time","type":["string","null"]},"removedAt":{"format":"date-time","type":["string","null"]},"status":{"$ref":"#/components/schemas/WebhookDispatchStatus"}},"required":["status"],"type":"object"},"WebhookListItem":{"properties":{"actionType":{"type":"string"},"condition":{"$ref":"#/components/schemas/WebhookCondition"},"createdAt":{"format":"date-time","type":"string"},"description":{"type":["string","null"]},"doNotRetry":{"type":"boolean"},"eventTypes":{"items":{"$ref":"#/components/schemas/WebhookEventType"},"type":"array"},"githubIssue":{"$ref":"#/components/schemas/WebhookGithubIssue"},"googleDrive":{"$ref":"#/components/schemas/WebhookGoogleDrive"},"googleMail":{"$ref":"#/components/schemas/WebhookGoogleMail"},"headersTemplate":{"type":["string","null"]},"id":{"type":"string"},"ignoreSslErrors":{"type":"boolean"},"isAdHoc":{"type":["boolean","null"]},"isApifyIntegration":{"type":"boolean"},"isEnabled":{"type":"boolean"},"lastDispatch":{"anyOf":[{"$ref":"#/components/schemas/WebhookLastDispatch"},{"type":"null"}]},"modifiedAt":{"format":"date-time","type":"string"},"requestUrl":{"format":"uri","type":["string","null"]},"shouldInterpolateStrings":{"type":["boolean","null"]},"slackMessage":{"$ref":"#/components/schemas/WebhookSlackMessage"},"stats":{"anyOf":[{"$ref":"#/components/schemas/WebhookStats"},{"type":"null"}]},"title":{"type":["string","null"]},"userId":{"type":"string"}},"required":["id","createdAt","modifiedAt","userId","eventTypes","condition","ignoreSslErrors","doNotRetry"],"type":"object"},"WebhookResource":{"properties":{"actionType":{"type":"string"},"condition":{"$ref":"#/components/schemas/WebhookCondition"},"createdAt":{"format":"date-time","type":"string"},"description":{"type":["string","null"]},"doNotRetry":{"type":["boolean","null"]},"eventTypes":{"items":{"$ref":"#/components/schemas/WebhookEventType"},"type":"array"},"githubIssue":{"$ref":"#/components/schemas/WebhookGithubIssue"},"googleDrive":{"$ref":"#/components/schemas/WebhookGoogleDrive"},"googleMail":{"$ref":"#/components/schemas/WebhookGoogleMail"},"headersTemplate":{"type":["string","null"]},"id":{"type":"string"},"ignoreSslErrors":{"type":"boolean"},"isAdHoc":{"type":["boolean","null"]},"isApifyIntegration":{"type":["boolean","null"]},"isEnabled":{"type":"boolean"},"lastDispatch":{"anyOf":[{"$ref":"#/components/schemas/WebhookLastDispatch"},{"type":"null"}]},"modifiedAt":{"format":"date-time","type":"string"},"payloadTemplate":{"type":["string","null"]},"requestUrl":{"format":"uri","type":["string","null"]},"shouldInterpolateStrings":{"type":["boolean","null"]},"slackMessage":{"$ref":"#/components/schemas/WebhookSlackMessage"},"stats":{"anyOf":[{"$ref":"#/components/schemas/WebhookStats"},{"type":"null"}]},"title":{"type":["string","null"]},"userId":{"type":"string"}},"required":["id","createdAt","modifiedAt","userId","eventTypes","condition","ignoreSslErrors"],"type":"object"},"WebhookResponse":{"properties":{"data":{"$ref":"#/components/schemas/WebhookResource"}},"required":["data"],"type":"object"},"WebhookSlackMessage":{"properties":{"channelId":{"type":"string"},"messageTemplate":{"type":"string"},"userIntegrationId":{"type":"string"}},"type":"object"},"WebhookStats":{"properties":{"totalDispatches":{"type":"integer"}},"type":"object"}},"specVersion":"v2-2026-11-04T151257Z"} +{"operations":{"DELETE /v2/actor-runs/{runId}/request-queue/requests/batch":{"$ref":"#/components/schemas/BatchDeleteResponse"},"DELETE /v2/actor-tasks/{actorTaskId}":{"type":"object"},"DELETE /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/requests/batch":{"$ref":"#/components/schemas/BatchDeleteResponse"},"DELETE /v2/actors/{actorId}":{"type":"object"},"DELETE /v2/actors/{actorId}/runs/last/request-queue/requests/batch":{"$ref":"#/components/schemas/BatchDeleteResponse"},"DELETE /v2/actors/{actorId}/versions/{versionNumber}":{"type":"object"},"DELETE /v2/actors/{actorId}/versions/{versionNumber}/env-vars/{envVarName}":{"type":"object"},"DELETE /v2/browser-info":{"$ref":"#/components/schemas/BrowserInfoResponse"},"DELETE /v2/request-queues/{queueId}/requests/batch":{"$ref":"#/components/schemas/BatchDeleteResponse"},"DELETE /v2/schedules/{scheduleId}":{"type":"object"},"DELETE /v2/webhooks/{webhookId}":{"type":"object"},"GET /v2/actor-builds":{"$ref":"#/components/schemas/ListOfBuildsResponse"},"GET /v2/actor-builds/{buildId}":{"allOf":[{"$ref":"#/components/schemas/BuildResponse"}]},"GET /v2/actor-builds/{buildId}/openapi.json":{"type":"object"},"GET /v2/actor-oauth-accounts/{accountId}":{"$ref":"#/components/schemas/ActorOAuthAccountResponse"},"GET /v2/actor-oauth-accounts/{connectionId}/accounts":{"$ref":"#/components/schemas/ListOfActorOAuthAccountsResponse"},"GET /v2/actor-runs":{"allOf":[{"$ref":"#/components/schemas/ListOfRunsResponse"}]},"GET /v2/actor-runs/{runId}":{"allOf":[{"$ref":"#/components/schemas/RunResponse"}]},"GET /v2/actor-runs/{runId}/dataset":{"$ref":"#/components/schemas/DatasetResponse"},"GET /v2/actor-runs/{runId}/dataset/items":{"items":{"type":"object"},"type":"array"},"GET /v2/actor-runs/{runId}/dataset/statistics":{"$ref":"#/components/schemas/DatasetStatisticsResponse"},"GET /v2/actor-runs/{runId}/key-value-store":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"GET /v2/actor-runs/{runId}/key-value-store/keys":{"$ref":"#/components/schemas/ListOfKeysResponse"},"GET /v2/actor-runs/{runId}/key-value-store/records/{recordKey}":{"$ref":"#/components/schemas/RecordResponse"},"GET /v2/actor-runs/{runId}/request-queue":{"$ref":"#/components/schemas/RequestQueueResponse"},"GET /v2/actor-runs/{runId}/request-queue/head":{"$ref":"#/components/schemas/RequestQueueHeadResponse"},"GET /v2/actor-runs/{runId}/request-queue/requests":{"$ref":"#/components/schemas/ListOfRequestsResponse"},"GET /v2/actor-runs/{runId}/request-queue/requests/{requestId}":{"$ref":"#/components/schemas/RequestResponse"},"GET /v2/actor-tasks":{"$ref":"#/components/schemas/ListOfTasksResponse"},"GET /v2/actor-tasks/{actorTaskId}":{"properties":{"data":{"$ref":"#/components/schemas/Task"}},"required":["data"],"type":"object"},"GET /v2/actor-tasks/{actorTaskId}/input":{"type":"object"},"GET /v2/actor-tasks/{actorTaskId}/run-sync":{"type":"object"},"GET /v2/actor-tasks/{actorTaskId}/run-sync-get-dataset-items":{"type":"object"},"GET /v2/actor-tasks/{actorTaskId}/runs":{"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/RunListItem"},"type":"array"}},"required":["items"],"type":"object"}]}},"required":["data"],"type":"object"},"GET /v2/actor-tasks/{actorTaskId}/runs/last":{"properties":{"data":{"$ref":"#/components/schemas/Run"}},"required":["data"],"type":"object"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/dataset":{"$ref":"#/components/schemas/DatasetResponse"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/dataset/items":{"items":{"type":"object"},"type":"array"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/dataset/statistics":{"$ref":"#/components/schemas/DatasetStatisticsResponse"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/key-value-store":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/key-value-store/keys":{"$ref":"#/components/schemas/ListOfKeysResponse"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/key-value-store/records/{recordKey}":{"$ref":"#/components/schemas/RecordResponse"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/request-queue":{"$ref":"#/components/schemas/RequestQueueResponse"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/head":{"$ref":"#/components/schemas/RequestQueueHeadResponse"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/requests":{"$ref":"#/components/schemas/ListOfRequestsResponse"},"GET /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/requests/{requestId}":{"$ref":"#/components/schemas/RequestResponse"},"GET /v2/actor-tasks/{actorTaskId}/webhooks":{"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/WebhookResource"},"type":"array"}},"required":["items"],"type":"object"}]}},"required":["data"],"type":"object"},"GET /v2/actors":{"$ref":"#/components/schemas/ListOfActorsResponse"},"GET /v2/actors/{actorId}":{"$ref":"#/components/schemas/ActorResponse"},"GET /v2/actors/{actorId}/builds":{"$ref":"#/components/schemas/ListOfBuildsResponse"},"GET /v2/actors/{actorId}/builds/default":{"$ref":"#/components/schemas/BuildResponse"},"GET /v2/actors/{actorId}/builds/{buildId}":{"$ref":"#/components/schemas/BuildResponse"},"GET /v2/actors/{actorId}/builds/{buildId}/openapi.json":{"type":"object"},"GET /v2/actors/{actorId}/oauth-connections":{"$ref":"#/components/schemas/ListOfActorOAuthConnectionsResponse"},"GET /v2/actors/{actorId}/run-sync":{"type":"object"},"GET /v2/actors/{actorId}/run-sync-get-dataset-items":{"items":{"type":"object"},"type":"array"},"GET /v2/actors/{actorId}/runs":{"$ref":"#/components/schemas/ListOfRunsResponse"},"GET /v2/actors/{actorId}/runs/last":{"$ref":"#/components/schemas/RunResponse"},"GET /v2/actors/{actorId}/runs/last/dataset":{"$ref":"#/components/schemas/DatasetResponse"},"GET /v2/actors/{actorId}/runs/last/dataset/items":{"items":{"type":"object"},"type":"array"},"GET /v2/actors/{actorId}/runs/last/dataset/statistics":{"$ref":"#/components/schemas/DatasetStatisticsResponse"},"GET /v2/actors/{actorId}/runs/last/key-value-store":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"GET /v2/actors/{actorId}/runs/last/key-value-store/keys":{"$ref":"#/components/schemas/ListOfKeysResponse"},"GET /v2/actors/{actorId}/runs/last/key-value-store/records/{recordKey}":{"$ref":"#/components/schemas/RecordResponse"},"GET /v2/actors/{actorId}/runs/last/request-queue":{"$ref":"#/components/schemas/RequestQueueResponse"},"GET /v2/actors/{actorId}/runs/last/request-queue/head":{"$ref":"#/components/schemas/RequestQueueHeadResponse"},"GET /v2/actors/{actorId}/runs/last/request-queue/requests":{"$ref":"#/components/schemas/ListOfRequestsResponse"},"GET /v2/actors/{actorId}/runs/last/request-queue/requests/{requestId}":{"$ref":"#/components/schemas/RequestResponse"},"GET /v2/actors/{actorId}/runs/{runId}":{"$ref":"#/components/schemas/RunResponse"},"GET /v2/actors/{actorId}/versions":{"$ref":"#/components/schemas/ListOfVersionsResponse"},"GET /v2/actors/{actorId}/versions/{versionNumber}":{"$ref":"#/components/schemas/VersionResponse"},"GET /v2/actors/{actorId}/versions/{versionNumber}/env-vars":{"$ref":"#/components/schemas/ListOfEnvVarsResponse"},"GET /v2/actors/{actorId}/versions/{versionNumber}/env-vars/{envVarName}":{"$ref":"#/components/schemas/EnvVarResponse"},"GET /v2/actors/{actorId}/webhooks":{"$ref":"#/components/schemas/ListOfWebhookResourcesResponse"},"GET /v2/browser-info":{"$ref":"#/components/schemas/BrowserInfoResponse"},"GET /v2/datasets":{"$ref":"#/components/schemas/ListOfDatasetsResponse"},"GET /v2/datasets/{datasetId}":{"$ref":"#/components/schemas/DatasetResponse"},"GET /v2/datasets/{datasetId}/items":{"items":{"type":"object"},"type":"array"},"GET /v2/datasets/{datasetId}/statistics":{"$ref":"#/components/schemas/DatasetStatisticsResponse"},"GET /v2/integrations/git":{"$ref":"#/components/schemas/ListOfGitProviderIntegrationsResponse"},"GET /v2/key-value-stores":{"$ref":"#/components/schemas/ListOfKeyValueStoresResponse"},"GET /v2/key-value-stores/{storeId}":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"GET /v2/key-value-stores/{storeId}/keys":{"$ref":"#/components/schemas/ListOfKeysResponse"},"GET /v2/key-value-stores/{storeId}/records/{recordKey}":{"$ref":"#/components/schemas/RecordResponse"},"GET /v2/request-queues":{"$ref":"#/components/schemas/ListOfRequestQueuesResponse"},"GET /v2/request-queues/{queueId}":{"$ref":"#/components/schemas/RequestQueueResponse"},"GET /v2/request-queues/{queueId}/head":{"$ref":"#/components/schemas/RequestQueueHeadResponse"},"GET /v2/request-queues/{queueId}/requests":{"$ref":"#/components/schemas/ListOfRequestsResponse"},"GET /v2/request-queues/{queueId}/requests/{requestId}":{"$ref":"#/components/schemas/RequestResponse"},"GET /v2/schedules":{"$ref":"#/components/schemas/ListOfSchedulesResponse"},"GET /v2/schedules/{scheduleId}":{"$ref":"#/components/schemas/ScheduleResponse"},"GET /v2/schedules/{scheduleId}/log":{"$ref":"#/components/schemas/ScheduleLogResponse"},"GET /v2/store":{"$ref":"#/components/schemas/ListOfStoreActorsResponse"},"GET /v2/users/me":{"$ref":"#/components/schemas/UserPrivateInfoResponse"},"GET /v2/users/me/limits":{"$ref":"#/components/schemas/AccountLimitsResponse"},"GET /v2/users/me/usage/monthly":{"$ref":"#/components/schemas/MonthlyUsageResponse"},"GET /v2/users/{userId}":{"anyOf":[{"$ref":"#/components/schemas/UserPublicInfoResponse"},{"$ref":"#/components/schemas/UserPrivateInfoResponse"}]},"GET /v2/users/{userId}/limits":{"$ref":"#/components/schemas/AccountLimitsResponse"},"GET /v2/users/{userId}/usage/monthly":{"$ref":"#/components/schemas/MonthlyUsageResponse"},"GET /v2/webhook-dispatches":{"$ref":"#/components/schemas/ListOfWebhookDispatchesResponse"},"GET /v2/webhook-dispatches/{dispatchId}":{"$ref":"#/components/schemas/WebhookDispatchResponse"},"GET /v2/webhooks":{"$ref":"#/components/schemas/ListOfWebhooksResponse"},"GET /v2/webhooks/{webhookId}":{"$ref":"#/components/schemas/WebhookResponse"},"GET /v2/webhooks/{webhookId}/dispatches":{"$ref":"#/components/schemas/ListOfWebhookDispatchesResponse"},"POST /v2/actor-builds/{buildId}/abort":{"allOf":[{"$ref":"#/components/schemas/BuildResponse"}]},"POST /v2/actor-runs/{runId}/abort":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actor-runs/{runId}/charge":{"type":"object"},"POST /v2/actor-runs/{runId}/dataset/items":{"type":"object"},"POST /v2/actor-runs/{runId}/key-value-store/records/{recordKey}":{"type":"object"},"POST /v2/actor-runs/{runId}/metamorph":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actor-runs/{runId}/reboot":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actor-runs/{runId}/request-queue/head/lock":{"$ref":"#/components/schemas/LockedRequestQueueHeadResponse"},"POST /v2/actor-runs/{runId}/request-queue/requests":{"$ref":"#/components/schemas/AddRequestResponse"},"POST /v2/actor-runs/{runId}/request-queue/requests/batch":{"$ref":"#/components/schemas/BatchAddResponse"},"POST /v2/actor-runs/{runId}/request-queue/requests/unlock":{"$ref":"#/components/schemas/UnlockRequestsResponse"},"POST /v2/actor-runs/{runId}/resurrect":{"allOf":[{"$ref":"#/components/schemas/RunResponse"}]},"POST /v2/actor-tasks":{"$ref":"#/components/schemas/TaskResponse"},"POST /v2/actor-tasks/{actorTaskId}/run-sync":{"type":"object"},"POST /v2/actor-tasks/{actorTaskId}/run-sync-get-dataset-items":{"type":"object"},"POST /v2/actor-tasks/{actorTaskId}/runs":{"properties":{"data":{"$ref":"#/components/schemas/Run"}},"required":["data"],"type":"object"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/abort":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/dataset/items":{"type":"object"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/key-value-store/records/{recordKey}":{"type":"object"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/metamorph":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/reboot":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/head/lock":{"$ref":"#/components/schemas/LockedRequestQueueHeadResponse"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/requests":{"$ref":"#/components/schemas/AddRequestResponse"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/requests/batch":{"$ref":"#/components/schemas/BatchAddResponse"},"POST /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/requests/unlock":{"$ref":"#/components/schemas/UnlockRequestsResponse"},"POST /v2/actors":{"$ref":"#/components/schemas/ActorResponse"},"POST /v2/actors/{actorId}/builds":{"$ref":"#/components/schemas/BuildResponse"},"POST /v2/actors/{actorId}/builds/{buildId}/abort":{"$ref":"#/components/schemas/BuildResponse"},"POST /v2/actors/{actorId}/run-sync":{"type":"object"},"POST /v2/actors/{actorId}/run-sync-get-dataset-items":{"items":{"type":"object"},"type":"array"},"POST /v2/actors/{actorId}/runs":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actors/{actorId}/runs/last/abort":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actors/{actorId}/runs/last/dataset/items":{"type":"object"},"POST /v2/actors/{actorId}/runs/last/key-value-store/records/{recordKey}":{"type":"object"},"POST /v2/actors/{actorId}/runs/last/metamorph":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actors/{actorId}/runs/last/reboot":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actors/{actorId}/runs/last/request-queue/head/lock":{"$ref":"#/components/schemas/LockedRequestQueueHeadResponse"},"POST /v2/actors/{actorId}/runs/last/request-queue/requests":{"$ref":"#/components/schemas/AddRequestResponse"},"POST /v2/actors/{actorId}/runs/last/request-queue/requests/batch":{"$ref":"#/components/schemas/BatchAddResponse"},"POST /v2/actors/{actorId}/runs/last/request-queue/requests/unlock":{"$ref":"#/components/schemas/UnlockRequestsResponse"},"POST /v2/actors/{actorId}/runs/{runId}/abort":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actors/{actorId}/runs/{runId}/metamorph":{"$ref":"#/components/schemas/RunResponse"},"POST /v2/actors/{actorId}/runs/{runId}/resurrect":{"allOf":[{"$ref":"#/components/schemas/RunResponse"}]},"POST /v2/actors/{actorId}/validate-input":{"properties":{"valid":{"type":"boolean"}},"required":["valid"],"type":"object"},"POST /v2/actors/{actorId}/versions":{"$ref":"#/components/schemas/VersionResponse"},"POST /v2/actors/{actorId}/versions/{versionNumber}":{"$ref":"#/components/schemas/VersionResponse"},"POST /v2/actors/{actorId}/versions/{versionNumber}/env-vars":{"$ref":"#/components/schemas/EnvVarResponse"},"POST /v2/actors/{actorId}/versions/{versionNumber}/env-vars/{envVarName}":{"$ref":"#/components/schemas/EnvVarResponse"},"POST /v2/browser-info":{"$ref":"#/components/schemas/BrowserInfoResponse"},"POST /v2/datasets":{"$ref":"#/components/schemas/DatasetResponse"},"POST /v2/datasets/{datasetId}/items":{"type":"object"},"POST /v2/integrations/git/{providerId}/create-repo":{"$ref":"#/components/schemas/GitRepoResponse"},"POST /v2/integrations/git/{providerId}/register-deployment-key":{"$ref":"#/components/schemas/RegisterDeploymentKeyResponse"},"POST /v2/key-value-stores":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"POST /v2/key-value-stores/{storeId}/records/{recordKey}":{"type":"object"},"POST /v2/request-queues":{"$ref":"#/components/schemas/RequestQueueResponse"},"POST /v2/request-queues/{queueId}/head/lock":{"$ref":"#/components/schemas/LockedRequestQueueHeadResponse"},"POST /v2/request-queues/{queueId}/requests":{"$ref":"#/components/schemas/AddRequestResponse"},"POST /v2/request-queues/{queueId}/requests/batch":{"$ref":"#/components/schemas/BatchAddResponse"},"POST /v2/request-queues/{queueId}/requests/unlock":{"$ref":"#/components/schemas/UnlockRequestsResponse"},"POST /v2/schedules":{"$ref":"#/components/schemas/ScheduleResponse"},"POST /v2/schedules/{scheduleId}/invoke":{"type":"object"},"POST /v2/tools/decode-and-verify":{"$ref":"#/components/schemas/DecodeAndVerifyResponse"},"POST /v2/tools/encode-and-sign":{"$ref":"#/components/schemas/EncodeAndSignResponse"},"POST /v2/webhooks":{"$ref":"#/components/schemas/WebhookResponse"},"POST /v2/webhooks/{webhookId}/test":{"$ref":"#/components/schemas/WebhookDispatchResponse"},"PUT /v2/actor-runs/{runId}":{"$ref":"#/components/schemas/RunResponse"},"PUT /v2/actor-runs/{runId}/dataset":{"$ref":"#/components/schemas/DatasetResponse"},"PUT /v2/actor-runs/{runId}/key-value-store":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"PUT /v2/actor-runs/{runId}/key-value-store/records/{recordKey}":{"type":"object"},"PUT /v2/actor-runs/{runId}/request-queue":{"$ref":"#/components/schemas/RequestQueueResponse"},"PUT /v2/actor-runs/{runId}/request-queue/requests/{requestId}":{"$ref":"#/components/schemas/UpdateRequestResponse"},"PUT /v2/actor-runs/{runId}/request-queue/requests/{requestId}/lock":{"$ref":"#/components/schemas/ProlongRequestLockResponse"},"PUT /v2/actor-tasks/{actorTaskId}":{"properties":{"data":{"$ref":"#/components/schemas/Task"}},"required":["data"],"type":"object"},"PUT /v2/actor-tasks/{actorTaskId}/input":{"type":"object"},"PUT /v2/actor-tasks/{actorTaskId}/runs/last/dataset":{"$ref":"#/components/schemas/DatasetResponse"},"PUT /v2/actor-tasks/{actorTaskId}/runs/last/key-value-store":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"PUT /v2/actor-tasks/{actorTaskId}/runs/last/key-value-store/records/{recordKey}":{"type":"object"},"PUT /v2/actor-tasks/{actorTaskId}/runs/last/request-queue":{"$ref":"#/components/schemas/RequestQueueResponse"},"PUT /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/requests/{requestId}":{"$ref":"#/components/schemas/UpdateRequestResponse"},"PUT /v2/actor-tasks/{actorTaskId}/runs/last/request-queue/requests/{requestId}/lock":{"$ref":"#/components/schemas/ProlongRequestLockResponse"},"PUT /v2/actors/{actorId}":{"$ref":"#/components/schemas/ActorResponse"},"PUT /v2/actors/{actorId}/runs/last/dataset":{"$ref":"#/components/schemas/DatasetResponse"},"PUT /v2/actors/{actorId}/runs/last/key-value-store":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"PUT /v2/actors/{actorId}/runs/last/key-value-store/records/{recordKey}":{"type":"object"},"PUT /v2/actors/{actorId}/runs/last/request-queue":{"$ref":"#/components/schemas/RequestQueueResponse"},"PUT /v2/actors/{actorId}/runs/last/request-queue/requests/{requestId}":{"$ref":"#/components/schemas/UpdateRequestResponse"},"PUT /v2/actors/{actorId}/runs/last/request-queue/requests/{requestId}/lock":{"$ref":"#/components/schemas/ProlongRequestLockResponse"},"PUT /v2/actors/{actorId}/versions/{versionNumber}":{"$ref":"#/components/schemas/VersionResponse"},"PUT /v2/actors/{actorId}/versions/{versionNumber}/env-vars/{envVarName}":{"$ref":"#/components/schemas/EnvVarResponse"},"PUT /v2/browser-info":{"$ref":"#/components/schemas/BrowserInfoResponse"},"PUT /v2/datasets/{datasetId}":{"$ref":"#/components/schemas/DatasetResponse"},"PUT /v2/key-value-stores/{storeId}":{"$ref":"#/components/schemas/KeyValueStoreResponse"},"PUT /v2/key-value-stores/{storeId}/records/{recordKey}":{"type":"object"},"PUT /v2/request-queues/{queueId}":{"$ref":"#/components/schemas/RequestQueueResponse"},"PUT /v2/request-queues/{queueId}/requests/{requestId}":{"$ref":"#/components/schemas/UpdateRequestResponse"},"PUT /v2/request-queues/{queueId}/requests/{requestId}/lock":{"$ref":"#/components/schemas/ProlongRequestLockResponse"},"PUT /v2/schedules/{scheduleId}":{"$ref":"#/components/schemas/ScheduleResponse"},"PUT /v2/users/me/limits":{"type":"object"},"PUT /v2/users/{userId}/limits":{"type":"object"},"PUT /v2/webhooks/{webhookId}":{"$ref":"#/components/schemas/WebhookResponse"}},"schemas":{"AccountLimits":{"properties":{"current":{"$ref":"#/components/schemas/CurrentUsage"},"limits":{"$ref":"#/components/schemas/Limits"},"monthlyUsageCycle":{"$ref":"#/components/schemas/UsageCycle"}},"required":["monthlyUsageCycle","limits","current"],"type":"object"},"AccountLimitsResponse":{"properties":{"data":{"$ref":"#/components/schemas/AccountLimits"}},"required":["data"],"type":"object"},"ActorDefinition":{"properties":{"actorSpecification":{"type":"integer"},"buildTag":{"type":"string"},"changelog":{"type":["string","null"]},"defaultMemoryMbytes":{"oneOf":[{"type":"string"},{"type":"integer"}]},"description":{"type":"string"},"dockerContextDir":{"type":"string"},"dockerfile":{"type":"string"},"environmentVariables":{"type":"object"},"input":{"type":["object","null"]},"maxMemoryMbytes":{"minimum":128,"type":"integer"},"meta":{"$ref":"#/components/schemas/ActorDefinitionMeta"},"minMemoryMbytes":{"minimum":128,"type":"integer"},"name":{"type":"string"},"output":{"type":["object","null"]},"readme":{"type":["string","null"]},"storages":{"properties":{"dataset":{"type":"object"},"datasets":{"type":"object"},"keyValueStore":{"type":"object"}},"type":"object"},"title":{"type":"string"},"usesStandbyMode":{"type":"boolean"},"version":{"pattern":"^[0-9]+(\\.[0-9]+)+$","type":"string"},"webServerMcpPath":{"type":"string"},"webServerSchema":{"type":"object"}},"type":"object"},"ActorDefinitionMeta":{"properties":{"generatedBy":{"type":"string"},"templateId":{"type":"string"}},"type":"object"},"ActorJobStatus":{"type":"string"},"ActorListItem":{"properties":{"createdAt":{"format":"date-time","type":"string"},"id":{"type":"string"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":"string"},"stats":{"anyOf":[{"$ref":"#/components/schemas/ActorStats"},{"type":"null"}]},"title":{"type":"string"},"username":{"type":"string"}},"required":["id","createdAt","modifiedAt","name","username"],"type":"object"},"ActorNotice":{"type":["string","null"]},"ActorOAuthAccount":{"properties":{"_id":{"type":"string"},"authorizedAt":{"format":"date-time","type":["string","null"]},"connectionId":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"data":{"type":"object"},"description":{"type":["string","null"]},"modifiedAt":{"format":"date-time","type":"string"},"userId":{"type":"string"}},"required":["_id","userId","connectionId","createdAt","modifiedAt","data"],"type":"object"},"ActorOAuthAccountResponse":{"properties":{"data":{"$ref":"#/components/schemas/ActorOAuthAccount"}},"required":["data"],"type":"object"},"ActorOAuthConnection":{"properties":{"_id":{"type":"string"},"actorId":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"credentials":{"properties":{"clientId":{"type":"string"},"imageUrl":{"type":"string"},"label":{"type":"string"},"scope":{"type":"string"}},"type":"object"},"modifiedAt":{"format":"date-time","type":"string"},"oAuthProviderType":{"type":"string"}},"required":["_id","actorId","oAuthProviderType","createdAt","modifiedAt"],"type":"object"},"ActorPermissionLevel":{"type":"string"},"ActorResource":{"properties":{"actorPermissionLevel":{"$ref":"#/components/schemas/ActorPermissionLevel"},"actorStandby":{"anyOf":[{"$ref":"#/components/schemas/ActorStandby"},{"type":"null"}]},"categories":{"items":{"type":"string"},"type":"array"},"createdAt":{"format":"date-time","type":"string"},"defaultRunOptions":{"$ref":"#/components/schemas/DefaultRunOptions"},"deploymentKey":{"type":"string"},"description":{"type":["string","null"]},"exampleRunInput":{"anyOf":[{"$ref":"#/components/schemas/ExampleRunInput"},{"type":"null"}]},"hasNoDataset":{"type":"boolean"},"id":{"type":"string"},"isCritical":{"type":"boolean"},"isDeprecated":{"type":["boolean","null"]},"isGeneric":{"type":"boolean"},"isPublic":{"type":"boolean"},"isSourceCodeHidden":{"type":"boolean"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":"string"},"notice":{"$ref":"#/components/schemas/ActorNotice"},"pictureUrl":{"type":["string","null"]},"pricingInfos":{"items":{"$ref":"#/components/schemas/ActorRunPricingInfo"},"type":"array"},"readmeSummary":{"type":"string"},"restartOnError":{"type":["boolean","null"]},"seoDescription":{"type":["string","null"]},"seoTitle":{"type":["string","null"]},"standbyUrl":{"type":["string","null"]},"stats":{"$ref":"#/components/schemas/ActorStats"},"taggedBuilds":{"anyOf":[{"$ref":"#/components/schemas/TaggedBuilds"},{"type":"null"}]},"title":{"type":["string","null"]},"userId":{"type":"string"},"username":{"type":"string"},"versions":{"items":{"$ref":"#/components/schemas/Version"},"type":"array"}},"required":["id","userId","name","username","isPublic","createdAt","modifiedAt","stats","versions","defaultRunOptions"],"type":"object"},"ActorResponse":{"properties":{"data":{"$ref":"#/components/schemas/ActorResource"}},"required":["data"],"type":"object"},"ActorRunPricingInfo":{"oneOf":[{"$ref":"#/components/schemas/PayPerEventActorPricingInfo"},{"$ref":"#/components/schemas/PricePerDatasetItemActorPricingInfo"},{"$ref":"#/components/schemas/FlatPricePerMonthActorPricingInfo"},{"$ref":"#/components/schemas/FreeActorPricingInfo"}]},"ActorStandby":{"properties":{"build":{"type":["string","null"]},"desiredRequestsPerActorRun":{"type":["integer","null"]},"disableStandbyFieldsOverride":{"type":["boolean","null"]},"idleTimeoutSecs":{"type":["integer","null"]},"isConsoleAuthEnabled":{"type":["boolean","null"]},"isEnabled":{"type":["boolean","null"]},"isTokenlessEnabled":{"type":["boolean","null"]},"maxRequestsPerActorRun":{"type":["integer","null"]},"memoryMbytes":{"type":["integer","null"]},"shouldPassActorInput":{"type":["boolean","null"]},"tenancy":{"type":["string","null"]}},"type":"object"},"ActorStats":{"properties":{"actorReviewCount":{"type":"integer"},"actorReviewRating":{"type":"number"},"bookmarkCount":{"type":"integer"},"lastRunStartedAt":{"format":"date-time","type":"string"},"publicActorRunStats30Days":{"properties":{"ABORTED":{"type":"integer"},"FAILED":{"type":"integer"},"SUCCEEDED":{"type":"integer"},"TIMED-OUT":{"type":"integer"},"TOTAL":{"type":"integer"}},"type":"object"},"totalBuilds":{"type":"integer"},"totalMetamorphs":{"type":"integer"},"totalRuns":{"type":"integer"},"totalUsers":{"type":"integer"},"totalUsers30Days":{"type":"integer"},"totalUsers7Days":{"type":"integer"},"totalUsers90Days":{"type":"integer"}},"type":"object"},"AddRequestResponse":{"properties":{"data":{"$ref":"#/components/schemas/RequestRegistration"}},"required":["data"],"type":"object"},"AddedRequest":{"properties":{"requestId":{"$ref":"#/components/schemas/RequestId"},"uniqueKey":{"$ref":"#/components/schemas/UniqueKey"},"wasAlreadyHandled":{"$ref":"#/components/schemas/WasAlreadyHandled"},"wasAlreadyPresent":{"$ref":"#/components/schemas/WasAlreadyPresent"}},"required":["requestId","uniqueKey","wasAlreadyHandled","wasAlreadyPresent"],"type":"object"},"AvailableProxyGroups":{"type":"object"},"BatchAddResponse":{"properties":{"data":{"$ref":"#/components/schemas/BatchAddResult"}},"required":["data"],"type":"object"},"BatchAddResult":{"properties":{"processedRequests":{"items":{"$ref":"#/components/schemas/AddedRequest"},"type":"array"},"unprocessedRequests":{"items":{"$ref":"#/components/schemas/UnprocessedRequest"},"type":"array"}},"required":["processedRequests","unprocessedRequests"],"type":"object"},"BatchDeleteResponse":{"properties":{"data":{"$ref":"#/components/schemas/BatchDeleteResult"}},"required":["data"],"type":"object"},"BatchDeleteResult":{"properties":{"processedRequests":{"items":{"$ref":"#/components/schemas/DeletedRequest"},"type":"array"},"unprocessedRequests":{"items":{"$ref":"#/components/schemas/UnprocessedRequest"},"type":"array"}},"required":["processedRequests","unprocessedRequests"],"type":"object"},"BrowserInfoResponse":{"properties":{"bodyLength":{"type":"integer"},"clientIp":{"type":["string","null"]},"countryCode":{"type":["string","null"]},"headers":{"type":"object"},"method":{"type":"string"},"rawHeaders":{"items":{"type":"string"},"type":"array"}},"required":["method","clientIp","countryCode","bodyLength"],"type":"object"},"Build":{"properties":{"actId":{"type":"string"},"actVersion":{"properties":{"actorPath":{"type":["string","null"]},"buildTag":{"type":["string","null"]},"gitHubGistUrl":{"type":"string"},"gitRepoUrl":{"type":"string"},"sourceFiles":{"items":{"$ref":"#/components/schemas/SourceCodeFile"},"type":"array"},"sourceType":{"$ref":"#/components/schemas/VersionSourceType"},"tarballUrl":{"type":"string"},"versionNumber":{"pattern":"^([0-9]|[1-9][0-9])\\.([0-9]|[1-9][0-9])$","type":"string"}},"type":"object"},"actorDefinition":{"anyOf":[{"$ref":"#/components/schemas/ActorDefinition"},{"type":"null"}]},"buildNumber":{"pattern":"^([0-9]|[1-9][0-9])\\.([0-9]|[1-9][0-9])(\\.[1-9][0-9]{0,4})$","type":"string"},"changelog":{"type":["string","null"]},"createdByOrganizationMemberUserId":{"type":"string"},"exitCode":{"type":["integer","null"]},"finishedAt":{"format":"date-time","type":["string","null"]},"gitBranchName":{"type":"string"},"gitCommitId":{"type":"string"},"id":{"type":"string"},"imageDigest":{"type":["string","null"]},"inputSchema":{"type":["string","null"]},"meta":{"$ref":"#/components/schemas/BuildMeta"},"options":{"anyOf":[{"$ref":"#/components/schemas/BuildOptions"},{"type":"null"}]},"readme":{"type":["string","null"]},"startedAt":{"format":"date-time","type":"string"},"stats":{"anyOf":[{"$ref":"#/components/schemas/BuildStats"},{"type":"null"}]},"status":{"$ref":"#/components/schemas/ActorJobStatus"},"usage":{"anyOf":[{"$ref":"#/components/schemas/BuildUsage"},{"type":"null"}]},"usageTotalUsd":{"type":["number","null"]},"usageUsd":{"anyOf":[{"$ref":"#/components/schemas/BuildUsage"},{"type":"null"}]},"userId":{"type":"string"}},"required":["id","actId","userId","startedAt","status","meta","buildNumber"],"type":"object"},"BuildListItem":{"properties":{"actId":{"type":"string"},"buildNumber":{"pattern":"^([0-9]|[1-9][0-9])\\.([0-9]|[1-9][0-9])(\\.[1-9][0-9]{0,4})$","type":"string"},"buildNumberInt":{"type":"integer"},"finishedAt":{"format":"date-time","type":["string","null"]},"id":{"type":"string"},"meta":{"$ref":"#/components/schemas/BuildMeta"},"startedAt":{"format":"date-time","type":"string"},"status":{"$ref":"#/components/schemas/ActorJobStatus"},"usageTotalUsd":{"type":"number"},"userId":{"type":"string"}},"required":["id","status","startedAt","buildNumber"],"type":"object"},"BuildMeta":{"properties":{"actorRunId":{"type":"string"},"clientIp":{"type":["string"]},"githubActionsRunUrl":{"type":"string"},"origin":{"$ref":"#/components/schemas/RunOrigin"},"userAgent":{"type":["string"]},"webhookDispatchId":{"type":"string"}},"required":["origin"],"type":"object"},"BuildOptions":{"properties":{"betaPackages":{"type":["boolean","null"]},"debug":{"type":["boolean","null"]},"diskMbytes":{"type":["integer","null"]},"memoryMbytes":{"type":["integer","null"]},"tag":{"type":["string","null"]},"useCache":{"type":["boolean","null"]}},"type":"object"},"BuildResponse":{"properties":{"data":{"$ref":"#/components/schemas/Build"}},"required":["data"],"type":"object"},"BuildStats":{"properties":{"computeUnits":{"type":"number"},"durationMillis":{"type":"integer"},"imageSizeBytes":{"type":["integer","null"]},"runTimeSecs":{"type":"number"}},"type":"object"},"BuildUsage":{"properties":{"ACTOR_COMPUTE_UNITS":{"type":["number","null"]}},"type":"object"},"CommonActorPricingInfo":{"properties":{"apifyMarginPercentage":{"type":"number"},"createdAt":{"format":"date-time","type":"string"},"forceContainsSignificantPriceChange":{"type":"boolean"},"isPriceChangeNotificationSuppressed":{"type":"boolean"},"notifiedAboutChangeAt":{"format":"date-time","type":["string","null"]},"notifiedAboutFutureChangeAt":{"format":"date-time","type":["string","null"]},"notifiedAboutRentalToPpePriceChangeAt":{"format":"date-time","type":["string","null"]},"reasonForChange":{"type":["string","null"]},"startedAt":{"format":"date-time","type":"string"}},"required":["apifyMarginPercentage","createdAt","startedAt"],"type":"object"},"CurrentPricingInfo":{"properties":{"apifyMarginPercentage":{"type":"number"},"createdAt":{"format":"date-time","type":"string"},"forceContainsSignificantPriceChange":{"type":"boolean"},"isPPEPlatformUsagePaidByUser":{"type":"boolean"},"isPriceChangeNotificationSuppressed":{"type":"boolean"},"minimalMaxTotalChargeUsd":{"type":["number","null"]},"notifiedAboutChangeAt":{"format":"date-time","type":["string","null"]},"notifiedAboutFutureChangeAt":{"format":"date-time","type":["string","null"]},"notifiedAboutRentalToPpePriceChangeAt":{"format":"date-time","type":["string","null"]},"pricePerUnitUsd":{"type":"number"},"pricingModel":{"type":"string"},"pricingPerEvent":{"type":["object","null"]},"reasonForChange":{"type":["string","null"]},"startedAt":{"format":"date-time","type":"string"},"tieredPricing":{"type":"object"},"trialMinutes":{"type":"integer"},"unitName":{"type":"string"}},"required":["pricingModel"],"type":"object"},"CurrentUsage":{"properties":{"activeActorJobCount":{"type":"integer"},"actorCount":{"type":"integer"},"actorMemoryGbytes":{"type":"number"},"actorTaskCount":{"type":"integer"},"monthlyActorComputeUnits":{"type":"number"},"monthlyExternalDataTransferGbytes":{"type":"number"},"monthlyProxySerps":{"type":"integer"},"monthlyResidentialProxyGbytes":{"type":"number"},"monthlyUnblockerUnits":{"type":"number"},"monthlyUsageUsd":{"type":"number"},"scheduleCount":{"type":"integer"},"teamAccountSeatCount":{"type":"integer"}},"required":["monthlyUsageUsd","monthlyActorComputeUnits","monthlyExternalDataTransferGbytes","monthlyProxySerps","monthlyResidentialProxyGbytes","actorMemoryGbytes","actorCount","actorTaskCount","activeActorJobCount","teamAccountSeatCount"],"type":"object"},"DailyServiceUsage":{"properties":{"date":{"format":"date-time","type":"string"},"serviceUsage":{"$ref":"#/components/schemas/ServiceUsage"},"totalUsageCreditsUsd":{"type":"number"}},"required":["date","serviceUsage","totalUsageCreditsUsd"],"type":"object"},"DatasetListItem":{"properties":{"accessedAt":{"format":"date-time","type":"string"},"actId":{"type":["string","null"]},"actRunId":{"type":["string","null"]},"cleanItemCount":{"type":"integer"},"createdAt":{"format":"date-time","type":"string"},"generalAccess":{"anyOf":[{"$ref":"#/components/schemas/GeneralAccess"},{"type":"null"}]},"id":{"type":"string"},"itemCount":{"type":"integer"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":["string","null"]},"stats":{"$ref":"#/components/schemas/DatasetStats"},"title":{"type":["string","null"]},"userId":{"type":"string"},"username":{"type":"string"}},"required":["id","userId","createdAt","modifiedAt","accessedAt","itemCount","cleanItemCount"],"type":"object"},"DatasetResource":{"properties":{"accessedAt":{"format":"date-time","type":"string"},"actId":{"type":["string","null"]},"actRunId":{"type":["string","null"]},"cleanItemCount":{"minimum":0,"type":"integer"},"consoleUrl":{"format":"uri","type":"string"},"createdAt":{"format":"date-time","type":"string"},"fields":{"items":{"type":"string"},"type":["array","null"]},"generalAccess":{"anyOf":[{"$ref":"#/components/schemas/GeneralAccess"},{"type":"null"}]},"id":{"type":"string"},"itemCount":{"minimum":0,"type":"integer"},"itemsPublicUrl":{"format":"uri","type":"string"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":["string","null"]},"schema":{"type":["object","null"]},"stats":{"$ref":"#/components/schemas/DatasetStats"},"title":{"type":["string","null"]},"urlSigningSecretKey":{"type":["string","null"]},"userId":{"type":"string"}},"required":["id","userId","createdAt","modifiedAt","accessedAt","itemCount","cleanItemCount","consoleUrl"],"type":"object"},"DatasetResponse":{"properties":{"data":{"$ref":"#/components/schemas/DatasetResource"}},"required":["data"],"type":"object"},"DatasetStatistics":{"properties":{"fieldStatistics":{"type":["object","null"]}},"type":"object"},"DatasetStatisticsResponse":{"properties":{"data":{"$ref":"#/components/schemas/DatasetStatistics"}},"required":["data"],"type":"object"},"DatasetStats":{"properties":{"inflatedBytes":{"type":"integer"},"readCount":{"type":"integer"},"storageBytes":{"type":"integer"},"writeCount":{"type":"integer"}},"type":"object"},"DecodeAndVerifyResponse":{"properties":{"data":{"$ref":"#/components/schemas/DecodeAndVerifyResult"}},"required":["data"],"type":"object"},"DecodeAndVerifyResult":{"properties":{"decoded":{},"encodedByUserId":{"type":["string","null"]},"isVerifiedUser":{"type":"boolean"}},"required":["decoded","encodedByUserId","isVerifiedUser"],"type":"object"},"DefaultRunOptions":{"properties":{"build":{"type":"string"},"forcePermissionLevel":{"anyOf":[{"$ref":"#/components/schemas/ActorPermissionLevel"},{"type":"null"}]},"isMaxTotalChargeUsdSetByUser":{"type":["boolean","null"]},"maxItems":{"type":["integer","null"]},"maxTotalChargeUsd":{"type":["number","null"]},"memoryMbytes":{"type":"integer"},"restartOnError":{"type":["boolean","null"]},"timeoutSecs":{"type":"integer"}},"type":"object"},"DeletedRequest":{"anyOf":[{"$ref":"#/components/schemas/DeletedRequestById"},{"$ref":"#/components/schemas/DeletedRequestByUniqueKey"}]},"DeletedRequestById":{"properties":{"id":{"$ref":"#/components/schemas/RequestId"},"uniqueKey":{"$ref":"#/components/schemas/UniqueKey"}},"required":["id"],"type":"object"},"DeletedRequestByUniqueKey":{"properties":{"id":{"$ref":"#/components/schemas/RequestId"},"uniqueKey":{"$ref":"#/components/schemas/UniqueKey"}},"required":["uniqueKey"],"type":"object"},"EffectivePlatformFeature":{"properties":{"disabledReason":{"type":["string","null"]},"disabledReasonType":{"type":["string","null"]},"isEnabled":{"type":"boolean"},"isTrial":{"type":"boolean"},"trialExpirationAt":{"format":"date-time","type":["string","null"]}},"required":["isEnabled","disabledReason","disabledReasonType","isTrial","trialExpirationAt"],"type":"object"},"EffectivePlatformFeatures":{"properties":{"ACTORS":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"ACTORS_PUBLIC_ALL":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"ACTORS_PUBLIC_DEVELOPER":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"PROXY":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"PROXY_EXTERNAL_ACCESS":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"PROXY_RESIDENTIAL":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"PROXY_SERPS":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"SCHEDULER":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"STORAGE":{"$ref":"#/components/schemas/EffectivePlatformFeature"},"WEBHOOKS":{"$ref":"#/components/schemas/EffectivePlatformFeature"}},"required":["ACTORS","STORAGE","SCHEDULER","PROXY","PROXY_EXTERNAL_ACCESS","PROXY_RESIDENTIAL","PROXY_SERPS","WEBHOOKS","ACTORS_PUBLIC_ALL","ACTORS_PUBLIC_DEVELOPER"],"type":"object"},"EncodeAndSignResponse":{"properties":{"data":{"$ref":"#/components/schemas/EncodeAndSignResult"}},"required":["data"],"type":"object"},"EncodeAndSignResult":{"properties":{"encoded":{"type":"string"}},"required":["encoded"],"type":"object"},"EnvVar":{"properties":{"isSecret":{"type":["boolean","null"]},"name":{"type":"string"},"value":{"type":"string"},"valueHash":{"type":"string"}},"required":["name"],"type":"object"},"EnvVarResponse":{"properties":{"data":{"$ref":"#/components/schemas/EnvVar"}},"required":["data"],"type":"object"},"ExampleRunInput":{"properties":{"body":{"type":"string"},"contentType":{"type":"string"}},"type":"object"},"FlatPricePerMonthActorPricingInfo":{"allOf":[{"$ref":"#/components/schemas/CommonActorPricingInfo"},{"properties":{"pricePerUnitUsd":{"type":"number"},"pricingModel":{"type":"string"},"trialMinutes":{"type":"integer"}},"required":["pricingModel","pricePerUnitUsd","trialMinutes"],"type":"object"}]},"FreeActorPricingInfo":{"allOf":[{"$ref":"#/components/schemas/CommonActorPricingInfo"},{"properties":{"pricingModel":{"type":"string"}},"required":["pricingModel"],"type":"object"}]},"GeneralAccess":{"type":"string"},"GitProviderIntegration":{"properties":{"addWorkspaceUrl":{"type":"string"},"id":{"type":"string"},"provider":{"type":"string"},"workspaces":{"items":{"$ref":"#/components/schemas/GitProviderWorkspace"},"type":"array"}},"required":["id","provider","workspaces"],"type":"object"},"GitProviderWorkspace":{"properties":{"id":{"type":"string"},"label":{"type":"string"}},"required":["id","label"],"type":"object"},"GitRepo":{"properties":{"htmlUrl":{"type":"string"},"httpsUrl":{"type":"string"},"sshUrl":{"type":"string"}},"required":["sshUrl","httpsUrl","htmlUrl"],"type":"object"},"GitRepoResponse":{"properties":{"data":{"$ref":"#/components/schemas/GitRepo"}},"required":["data"],"type":"object"},"HadMultipleClients":{"type":"boolean"},"HandledRequestCount":{"minimum":0,"type":"integer"},"HeadLimit":{"type":"integer"},"HttpMethod":{"type":"string"},"IntegrationTracking":{"properties":{"appId":{"type":"string"},"isAiTool":{"type":"boolean"},"platform":{"type":"string"}},"type":"object"},"KeyValueStoreKey":{"properties":{"key":{"type":"string"},"recordPublicUrl":{"format":"uri","type":"string"},"size":{"type":"integer"}},"required":["key","size","recordPublicUrl"],"type":"object"},"KeyValueStoreListItem":{"properties":{"accessedAt":{"format":"date-time","type":"string"},"actId":{"type":["string","null"]},"actRunId":{"type":["string","null"]},"createdAt":{"format":"date-time","type":"string"},"generalAccess":{"anyOf":[{"$ref":"#/components/schemas/GeneralAccess"},{"type":"null"}]},"id":{"type":"string"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":["string","null"]},"schema":{"type":["object","string","null"]},"stats":{"$ref":"#/components/schemas/KeyValueStoreListStats"},"title":{"type":["string","null"]},"userId":{"type":["string","null"]},"username":{"type":["string","null"]}},"required":["id","createdAt","modifiedAt","accessedAt"],"type":"object"},"KeyValueStoreListStats":{"properties":{"deleteCount":{"type":"integer"},"listCount":{"type":"integer"},"readCount":{"type":"integer"},"s3StorageBytes":{"type":"integer"},"writeCount":{"type":"integer"}},"type":"object"},"KeyValueStoreResource":{"properties":{"accessedAt":{"format":"date-time","type":"string"},"actId":{"type":["string","null"]},"actRunId":{"type":["string","null"]},"consoleUrl":{"format":"uri","type":"string"},"createdAt":{"format":"date-time","type":"string"},"generalAccess":{"anyOf":[{"$ref":"#/components/schemas/GeneralAccess"},{"type":"null"}]},"id":{"type":"string"},"keysPublicUrl":{"format":"uri","type":"string"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":["string","null"]},"recordsPublicUrl":{"format":"uri","type":"string"},"schema":{"type":["object","null"]},"stats":{"$ref":"#/components/schemas/KeyValueStoreStats"},"title":{"type":["string","null"]},"urlSigningSecretKey":{"type":["string","null"]},"userId":{"type":["string","null"]},"username":{"type":["string","null"]}},"required":["id","createdAt","modifiedAt","accessedAt"],"type":"object"},"KeyValueStoreResponse":{"properties":{"data":{"$ref":"#/components/schemas/KeyValueStoreResource"}},"required":["data"],"type":"object"},"KeyValueStoreStats":{"properties":{"deleteCount":{"type":"integer"},"listCount":{"type":"integer"},"readCount":{"type":"integer"},"s3StorageBytes":{"type":"integer"},"storageBytes":{"type":"integer"},"writeCount":{"type":"integer"}},"type":"object"},"Limits":{"properties":{"dataRetentionDays":{"type":"integer"},"maxActorCount":{"type":"integer"},"maxActorMemoryGbytes":{"type":"number"},"maxActorTaskCount":{"type":"integer"},"maxConcurrentActorJobs":{"type":"integer"},"maxMonthlyActorComputeUnits":{"type":"number"},"maxMonthlyExternalDataTransferGbytes":{"type":"number"},"maxMonthlyProxySerps":{"type":"integer"},"maxMonthlyResidentialProxyGbytes":{"type":"number"},"maxMonthlyUnblockerUnits":{"type":"number"},"maxMonthlyUsageUsd":{"type":"number"},"maxScheduleCount":{"type":"integer"},"maxTeamAccountSeatCount":{"type":"integer"}},"required":["maxMonthlyUsageUsd","maxMonthlyActorComputeUnits","maxMonthlyExternalDataTransferGbytes","maxMonthlyProxySerps","maxMonthlyResidentialProxyGbytes","maxActorMemoryGbytes","maxActorCount","maxActorTaskCount","maxConcurrentActorJobs","maxTeamAccountSeatCount","dataRetentionDays"],"type":"object"},"ListOfActorOAuthAccounts":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ActorOAuthAccount"},"type":"array"},"total":{"type":"integer"}},"required":["total","items"],"type":"object"},"ListOfActorOAuthAccountsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfActorOAuthAccounts"}},"required":["data"],"type":"object"},"ListOfActorOAuthConnections":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ActorOAuthConnection"},"type":"array"},"total":{"type":"integer"}},"required":["total","items"],"type":"object"},"ListOfActorOAuthConnectionsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfActorOAuthConnections"}},"required":["data"],"type":"object"},"ListOfActors":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/ActorListItem"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfActorsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfActors"}},"required":["data"],"type":"object"},"ListOfBuilds":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/BuildListItem"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfBuildsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfBuilds"}},"required":["data"],"type":"object"},"ListOfDatasets":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/DatasetListItem"},"type":"array"},"unnamed":{"type":"boolean"}},"required":["items"],"type":"object"}]},"ListOfDatasetsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfDatasets"}},"required":["data"],"type":"object"},"ListOfEnvVars":{"properties":{"items":{"items":{"$ref":"#/components/schemas/EnvVar"},"type":"array"},"total":{"type":"integer"}},"required":["total","items"],"type":"object"},"ListOfEnvVarsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfEnvVars"}},"required":["data"],"type":"object"},"ListOfGitProviderIntegrationsResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/GitProviderIntegration"},"type":"array"}},"required":["data"],"type":"object"},"ListOfKeyValueStores":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/KeyValueStoreListItem"},"type":"array"},"unnamed":{"type":"boolean"}},"required":["items"],"type":"object"}]},"ListOfKeyValueStoresResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfKeyValueStores"}},"required":["data"],"type":"object"},"ListOfKeys":{"properties":{"count":{"type":"integer"},"exclusiveStartKey":{"type":["string","null"]},"isTruncated":{"type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/KeyValueStoreKey"},"type":"array"},"limit":{"type":"integer"},"nextExclusiveStartKey":{"type":["string","null"]}},"required":["items","count","limit","isTruncated"],"type":"object"},"ListOfKeysResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfKeys"}},"required":["data"],"type":"object"},"ListOfRequestQueues":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/RequestQueueListItem"},"type":"array"},"unnamed":{"type":"boolean"}},"required":["items"],"type":"object"}]},"ListOfRequestQueuesResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfRequestQueues"}},"required":["data"],"type":"object"},"ListOfRequests":{"properties":{"cursor":{"type":"string"},"exclusiveStartId":{"type":"string"},"items":{"items":{"$ref":"#/components/schemas/RequestResource"},"type":"array"},"limit":{"type":"integer"},"nextCursor":{"type":"string"}},"required":["items","limit"],"type":"object"},"ListOfRequestsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfRequests"}},"required":["data"],"type":"object"},"ListOfRuns":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/RunListItem"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfRunsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfRuns"}},"required":["data"],"type":"object"},"ListOfSchedules":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/ScheduleListItem"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfSchedulesResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfSchedules"}},"required":["data"],"type":"object"},"ListOfStoreActors":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/StoreActor"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfStoreActorsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfStoreActors"}},"required":["data"],"type":"object"},"ListOfTasks":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/TaskListItem"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfTasksResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfTasks"}},"required":["data"],"type":"object"},"ListOfVersions":{"properties":{"items":{"items":{"$ref":"#/components/schemas/Version"},"type":"array"},"total":{"type":"integer"}},"required":["total","items"],"type":"object"},"ListOfVersionsResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfVersions"}},"required":["data"],"type":"object"},"ListOfWebhookDispatches":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/WebhookDispatch"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfWebhookDispatchesResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfWebhookDispatches"}},"required":["data"],"type":"object"},"ListOfWebhookResources":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/WebhookResource"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfWebhookResourcesResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfWebhookResources"}},"required":["data"],"type":"object"},"ListOfWebhooks":{"allOf":[{"$ref":"#/components/schemas/PaginationResponse"},{"properties":{"items":{"items":{"$ref":"#/components/schemas/WebhookListItem"},"type":"array"}},"required":["items"],"type":"object"}]},"ListOfWebhooksResponse":{"properties":{"data":{"$ref":"#/components/schemas/ListOfWebhooks"}},"required":["data"],"type":"object"},"LockExpiresAt":{"format":"date-time","type":"string"},"LockedRequestQueueHead":{"properties":{"clientKey":{"type":"string"},"hadMultipleClients":{"$ref":"#/components/schemas/HadMultipleClients"},"items":{"items":{"$ref":"#/components/schemas/LockedRequestQueueHeadItem"},"type":"array"},"limit":{"$ref":"#/components/schemas/HeadLimit"},"lockSecs":{"type":"integer"},"queueHasLockedRequests":{"type":"boolean"},"queueModifiedAt":{"$ref":"#/components/schemas/QueueModifiedAt"}},"required":["limit","queueModifiedAt","hadMultipleClients","lockSecs","items"],"type":"object"},"LockedRequestQueueHeadItem":{"properties":{"id":{"$ref":"#/components/schemas/RequestId"},"lockExpiresAt":{"$ref":"#/components/schemas/LockExpiresAt"},"method":{"$ref":"#/components/schemas/RequestMethod"},"retryCount":{"$ref":"#/components/schemas/RetryCount"},"uniqueKey":{"$ref":"#/components/schemas/UniqueKey"},"url":{"$ref":"#/components/schemas/RequestUrl"}},"required":["id","uniqueKey","url","lockExpiresAt"],"type":"object"},"LockedRequestQueueHeadResponse":{"properties":{"data":{"$ref":"#/components/schemas/LockedRequestQueueHead"}},"required":["data"],"type":"object"},"MonthlyServiceUsage":{"type":"object"},"MonthlyUsage":{"properties":{"dailyServiceUsages":{"items":{"$ref":"#/components/schemas/DailyServiceUsage"},"type":"array"},"monthlyServiceUsage":{"$ref":"#/components/schemas/MonthlyServiceUsage"},"totalUsageCreditsUsdAfterVolumeDiscount":{"type":"number"},"totalUsageCreditsUsdBeforeVolumeDiscount":{"type":"number"},"usageCycle":{"$ref":"#/components/schemas/UsageCycle"}},"required":["usageCycle","monthlyServiceUsage","dailyServiceUsages","totalUsageCreditsUsdBeforeVolumeDiscount","totalUsageCreditsUsdAfterVolumeDiscount"],"type":"object"},"MonthlyUsageResponse":{"properties":{"data":{"$ref":"#/components/schemas/MonthlyUsage"}},"required":["data"],"type":"object"},"PaginationResponse":{"properties":{"count":{"minimum":0,"type":"integer"},"desc":{"type":"boolean"},"limit":{"minimum":1,"type":"integer"},"offset":{"minimum":0,"type":"integer"},"total":{"minimum":0,"type":"integer"}},"required":["total","offset","limit","desc","count"],"type":"object"},"PayPerEventActorPricingInfo":{"allOf":[{"$ref":"#/components/schemas/CommonActorPricingInfo"},{"properties":{"isPPEPlatformUsagePaidByUser":{"type":["boolean","null"]},"minimalMaxTotalChargeUsd":{"type":["number","null"]},"pricingModel":{"type":"string"},"pricingPerEvent":{"properties":{"actorChargeEvents":{"type":"object"}},"type":"object"}},"required":["pricingModel","pricingPerEvent"],"type":"object"}]},"PendingRequestCount":{"type":"integer"},"PricePerDatasetItemActorPricingInfo":{"allOf":[{"$ref":"#/components/schemas/CommonActorPricingInfo"},{"properties":{"pricePerUnitUsd":{"type":"number"},"pricingModel":{"type":"string"},"tieredPricing":{"$ref":"#/components/schemas/TieredPricingPerDatasetItem"},"unitName":{"type":"string"}},"required":["pricingModel","unitName"],"type":"object"}]},"ProlongRequestLockResponse":{"properties":{"data":{"$ref":"#/components/schemas/RequestLockInfo"}},"required":["data"],"type":"object"},"ProxyGroup":{"properties":{"availableCount":{"type":"integer"},"description":{"type":["string","null"]},"id":{"type":"string"},"name":{"type":"string"}},"required":["name","description"],"type":"object"},"ProxyResource":{"properties":{"groups":{"items":{"$ref":"#/components/schemas/ProxyGroup"},"type":"array"},"password":{"type":["string","null"]}},"required":["groups"],"type":"object"},"QueueAccessedAt":{"format":"date-time","type":"string"},"QueueCreatedAt":{"format":"date-time","type":"string"},"QueueId":{"type":"string"},"QueueModifiedAt":{"format":"date-time","type":"string"},"QueueUserId":{"type":"string"},"RecordResponse":{"type":"object"},"RegisterDeploymentKeyResponse":{"properties":{"data":{"properties":{"repoName":{"type":"string"}},"required":["repoName"],"type":"object"}},"required":["data"],"type":"object"},"RequestBase":{"properties":{"errorMessages":{"items":{"type":"string"},"type":["array","null"]},"handledAt":{"format":"date-time","type":["string","null"]},"headers":{"type":["object","null"]},"loadedUrl":{"type":["string","null"]},"method":{"$ref":"#/components/schemas/RequestMethod"},"noRetry":{"type":["boolean","null"]},"payload":{"type":["string","object","null"]},"retryCount":{"$ref":"#/components/schemas/RetryCount"},"uniqueKey":{"$ref":"#/components/schemas/UniqueKey"},"url":{"$ref":"#/components/schemas/RequestUrl"},"userData":{"$ref":"#/components/schemas/RequestUserData"}},"required":["uniqueKey","url"],"type":"object"},"RequestId":{"type":"string"},"RequestLockInfo":{"properties":{"lockExpiresAt":{"$ref":"#/components/schemas/LockExpiresAt"}},"required":["lockExpiresAt"],"type":"object"},"RequestMethod":{"$ref":"#/components/schemas/HttpMethod"},"RequestQueueHead":{"properties":{"hadMultipleClients":{"$ref":"#/components/schemas/HadMultipleClients"},"items":{"items":{"$ref":"#/components/schemas/RequestQueueHeadItem"},"type":"array"},"limit":{"$ref":"#/components/schemas/HeadLimit"},"queueModifiedAt":{"$ref":"#/components/schemas/QueueModifiedAt"}},"required":["limit","queueModifiedAt","hadMultipleClients","items"],"type":"object"},"RequestQueueHeadItem":{"properties":{"id":{"$ref":"#/components/schemas/RequestId"},"method":{"$ref":"#/components/schemas/RequestMethod"},"retryCount":{"$ref":"#/components/schemas/RetryCount"},"uniqueKey":{"$ref":"#/components/schemas/UniqueKey"},"url":{"$ref":"#/components/schemas/RequestUrl"}},"required":["id","uniqueKey","url"],"type":"object"},"RequestQueueHeadResponse":{"properties":{"data":{"$ref":"#/components/schemas/RequestQueueHead"}},"required":["data"],"type":"object"},"RequestQueueListItem":{"properties":{"accessedAt":{"$ref":"#/components/schemas/QueueAccessedAt"},"actId":{"type":["string","null"]},"actRunId":{"type":["string","null"]},"createdAt":{"$ref":"#/components/schemas/QueueCreatedAt"},"expireAt":{"format":"date-time","type":"string"},"generalAccess":{"anyOf":[{"$ref":"#/components/schemas/GeneralAccess"},{"type":"null"}]},"hadMultipleClients":{"$ref":"#/components/schemas/HadMultipleClients"},"handledRequestCount":{"$ref":"#/components/schemas/HandledRequestCount"},"id":{"$ref":"#/components/schemas/QueueId"},"modifiedAt":{"$ref":"#/components/schemas/QueueModifiedAt"},"name":{"type":["string","null"]},"pendingRequestCount":{"$ref":"#/components/schemas/PendingRequestCount"},"stats":{"$ref":"#/components/schemas/RequestQueueListStats"},"title":{"type":["string","null"]},"totalRequestCount":{"$ref":"#/components/schemas/TotalRequestCount"},"userId":{"$ref":"#/components/schemas/QueueUserId"},"username":{"type":"string"}},"required":["id","userId","createdAt","modifiedAt","accessedAt","totalRequestCount","handledRequestCount","pendingRequestCount","hadMultipleClients"],"type":"object"},"RequestQueueListStats":{"properties":{"deleteCount":{"type":"integer"},"dynamodbStorageBytes":{"type":"integer"},"headItemReadCount":{"type":"integer"},"readCount":{"type":"integer"},"s3StorageBytes":{"type":"integer"},"writeCount":{"type":"integer"}},"type":"object"},"RequestQueueResource":{"properties":{"accessedAt":{"$ref":"#/components/schemas/QueueAccessedAt"},"actId":{"type":["string","null"]},"actRunId":{"type":["string","null"]},"consoleUrl":{"format":"uri","type":"string"},"createdAt":{"$ref":"#/components/schemas/QueueCreatedAt"},"generalAccess":{"anyOf":[{"$ref":"#/components/schemas/GeneralAccess"},{"type":"null"}]},"hadMultipleClients":{"$ref":"#/components/schemas/HadMultipleClients"},"handledRequestCount":{"$ref":"#/components/schemas/HandledRequestCount"},"id":{"$ref":"#/components/schemas/QueueId"},"modifiedAt":{"$ref":"#/components/schemas/QueueModifiedAt"},"name":{"type":["string","null"]},"pendingRequestCount":{"$ref":"#/components/schemas/PendingRequestCount"},"stats":{"$ref":"#/components/schemas/RequestQueueStats"},"title":{"type":["string","null"]},"totalRequestCount":{"$ref":"#/components/schemas/TotalRequestCount"},"userId":{"$ref":"#/components/schemas/QueueUserId"}},"required":["id","userId","createdAt","modifiedAt","accessedAt","totalRequestCount","handledRequestCount","pendingRequestCount","hadMultipleClients","consoleUrl"],"type":"object"},"RequestQueueResponse":{"properties":{"data":{"$ref":"#/components/schemas/RequestQueueResource"}},"required":["data"],"type":"object"},"RequestQueueStats":{"properties":{"deleteCount":{"type":"integer"},"headItemReadCount":{"type":"integer"},"readCount":{"type":"integer"},"storageBytes":{"type":"integer"},"writeCount":{"type":"integer"}},"type":"object"},"RequestRegistration":{"properties":{"requestId":{"$ref":"#/components/schemas/RequestId"},"wasAlreadyHandled":{"$ref":"#/components/schemas/WasAlreadyHandled"},"wasAlreadyPresent":{"$ref":"#/components/schemas/WasAlreadyPresent"}},"required":["requestId","wasAlreadyPresent","wasAlreadyHandled"],"type":"object"},"RequestResource":{"allOf":[{"$ref":"#/components/schemas/RequestBase","required":["id"]},{"properties":{"id":{"$ref":"#/components/schemas/RequestId"},"lockByClient":{"type":"string"},"lockExpiresAt":{"$ref":"#/components/schemas/LockExpiresAt"}}}]},"RequestResponse":{"properties":{"data":{"$ref":"#/components/schemas/RequestResource"}},"required":["data"],"type":"object"},"RequestUrl":{"type":"string"},"RequestUserData":{"type":"object"},"RetryCount":{"type":"integer"},"Run":{"properties":{"accountedChargedEventCounts":{"type":["object","null"]},"actId":{"type":"string"},"actorTaskId":{"type":["string","null"]},"buildId":{"type":"string"},"buildNumber":{"pattern":"^([0-9]|[1-9][0-9])\\.([0-9]|[1-9][0-9])(\\.[1-9][0-9]{0,4})$","type":["string","null"]},"chargedEventCounts":{"type":"object"},"consoleUrl":{"format":"uri","type":"string"},"containerUrl":{"format":"uri","type":"string"},"createdByOrganizationMemberUserId":{"type":"string"},"defaultDatasetId":{"type":"string"},"defaultKeyValueStoreId":{"type":"string"},"defaultRequestQueueId":{"type":"string"},"eventUsage":{"type":"object"},"exitCode":{"type":["integer","null"]},"externalChargedTotalUsd":{"type":"number"},"finishedAt":{"format":"date-time","type":["string","null"]},"generalAccess":{"anyOf":[{"$ref":"#/components/schemas/GeneralAccess"},{"type":"null"}]},"gitBranchName":{"type":["string","null"]},"id":{"type":"string"},"integrationTracking":{"$ref":"#/components/schemas/IntegrationTracking"},"isContainerServerReady":{"type":["boolean","null"]},"isStatusMessageTerminal":{"type":["boolean","null"]},"meta":{"$ref":"#/components/schemas/RunMeta"},"metamorphs":{"anyOf":[{"items":{"$ref":"#/components/schemas/RunMetamorphEvent"},"type":"array"},{"type":"null"}]},"options":{"$ref":"#/components/schemas/RunOptions"},"output":{"type":"object"},"platformUsageBillingModel":{"type":"string"},"pricingInfo":{"$ref":"#/components/schemas/ActorRunPricingInfo"},"standby":{"anyOf":[{"$ref":"#/components/schemas/RunStandby"},{"type":"null"}]},"startedAt":{"format":"date-time","type":"string"},"stats":{"$ref":"#/components/schemas/RunStats"},"status":{"$ref":"#/components/schemas/ActorJobStatus"},"statusMessage":{"type":["string","null"]},"storageIds":{"properties":{"datasets":{"properties":{"default":{"type":"string"}},"type":"object"},"keyValueStores":{"properties":{"default":{"type":"string"}},"type":"object"},"requestQueues":{"properties":{"default":{"type":"string"}},"type":"object"}},"type":"object"},"usage":{"anyOf":[{"$ref":"#/components/schemas/RunUsage"},{"type":"null"}]},"usageTotalUsd":{"type":["number","null"]},"usageUsd":{"anyOf":[{"$ref":"#/components/schemas/RunUsageUsd"},{"type":"null"}]},"userId":{"type":"string"}},"required":["id","actId","userId","startedAt","status","meta","stats","options","buildId","defaultKeyValueStoreId","defaultDatasetId","defaultRequestQueueId"],"type":"object"},"RunListItem":{"properties":{"actId":{"type":"string"},"actorTaskId":{"type":["string","null"]},"buildId":{"type":"string"},"buildNumber":{"pattern":"^([0-9]|[1-9][0-9])\\.([0-9]|[1-9][0-9])(\\.[1-9][0-9]{0,4})$","type":"string"},"buildNumberInt":{"type":"integer"},"defaultDatasetId":{"type":"string"},"defaultKeyValueStoreId":{"type":"string"},"defaultRequestQueueId":{"type":"string"},"finishedAt":{"format":"date-time","type":["string","null"]},"id":{"type":"string"},"meta":{"$ref":"#/components/schemas/RunMeta"},"standby":{"anyOf":[{"$ref":"#/components/schemas/RunStandby"},{"type":"null"}]},"startedAt":{"format":"date-time","type":"string"},"status":{"$ref":"#/components/schemas/ActorJobStatus"},"usageTotalUsd":{"type":"number"},"userId":{"type":"string"}},"required":["id","actId","status","startedAt","buildId","meta","usageTotalUsd","defaultKeyValueStoreId","defaultDatasetId","defaultRequestQueueId"],"type":"object"},"RunMeta":{"properties":{"actorRunId":{"type":"string"},"clientIp":{"type":["string","null"]},"origin":{"$ref":"#/components/schemas/RunOrigin"},"scheduleId":{"type":["string","null"]},"scheduledAct2Id":{"type":"string"},"scheduledActorTaskId":{"type":"string"},"scheduledAt":{"format":"date-time","type":["string","null"]},"userAgent":{"type":["string","null"]},"webhookDispatchId":{"type":"string"}},"required":["origin"],"type":"object"},"RunMetamorphEvent":{"properties":{"actorId":{"type":"string"},"buildId":{"type":"string"},"buildTags":{"items":{"type":"string"},"type":"array"},"createdAt":{"format":"date-time","type":"string"},"inputKey":{"type":["string","null"]}},"required":["createdAt","actorId","buildId"],"type":"object"},"RunOptions":{"properties":{"build":{"type":"string"},"diskMbytes":{"minimum":0,"type":"integer"},"isMaxTotalChargeUsdSetByUser":{"type":"boolean"},"maxItems":{"minimum":0,"type":["integer","null"]},"maxTotalChargeUsd":{"minimum":0,"type":["number","null"]},"memoryMbytes":{"maximum":32768,"minimum":128,"type":"integer"},"restartOnError":{"type":["boolean","null"]},"timeoutSecs":{"minimum":0,"type":"integer"}},"required":["build","timeoutSecs","memoryMbytes","diskMbytes"],"type":"object"},"RunOrigin":{"type":"string"},"RunResponse":{"properties":{"data":{"$ref":"#/components/schemas/Run"}},"required":["data"],"type":"object"},"RunStandby":{"properties":{"deployment":{"type":"string"}},"required":["deployment"],"type":"object"},"RunStats":{"properties":{"computeUnits":{"minimum":0,"type":"number"},"cpuAvgUsage":{"type":"number"},"cpuCurrentUsage":{"type":"number"},"cpuMaxUsage":{"type":"number"},"durationMillis":{"minimum":0,"type":"integer"},"inputBodyLen":{"minimum":0,"type":["integer","null"]},"memAvgBytes":{"type":"number"},"memCurrentBytes":{"minimum":0,"type":"integer"},"memMaxBytes":{"minimum":0,"type":"integer"},"metamorph":{"minimum":0,"type":"integer"},"migrationCount":{"minimum":0,"type":"integer"},"netRxBytes":{"minimum":0,"type":"integer"},"netTxBytes":{"minimum":0,"type":"integer"},"rebootCount":{"minimum":0,"type":"integer"},"restartCount":{"minimum":0,"type":"integer"},"resurrectCount":{"minimum":0,"type":"integer"},"runTimeSecs":{"minimum":0,"type":"number"}},"type":"object"},"RunUsage":{"properties":{"ACTOR_COMPUTE_UNITS":{"type":["number","null"]},"DATASET_READS":{"type":["integer","null"]},"DATASET_WRITES":{"type":["integer","null"]},"DATA_TRANSFER_EXTERNAL_GBYTES":{"type":["number","null"]},"DATA_TRANSFER_INTERNAL_GBYTES":{"type":["number","null"]},"KEY_VALUE_STORE_LISTS":{"type":["integer","null"]},"KEY_VALUE_STORE_READS":{"type":["integer","null"]},"KEY_VALUE_STORE_WRITES":{"type":["integer","null"]},"PROXY_RESIDENTIAL_TRANSFER_GBYTES":{"type":["number","null"]},"PROXY_SERPS":{"type":["integer","null"]},"PROXY_UNBLOCKER_UNITS":{"type":["number","null"]},"REQUEST_QUEUE_READS":{"type":["integer","null"]},"REQUEST_QUEUE_WRITES":{"type":["integer","null"]}},"type":"object"},"RunUsageUsd":{"properties":{"ACTOR_COMPUTE_UNITS":{"type":["number","null"]},"DATASET_READS":{"type":["number","null"]},"DATASET_WRITES":{"type":["number","null"]},"DATA_TRANSFER_EXTERNAL_GBYTES":{"type":["number","null"]},"DATA_TRANSFER_INTERNAL_GBYTES":{"type":["number","null"]},"KEY_VALUE_STORE_LISTS":{"type":["number","null"]},"KEY_VALUE_STORE_READS":{"type":["number","null"]},"KEY_VALUE_STORE_WRITES":{"type":["number","null"]},"PROXY_RESIDENTIAL_TRANSFER_GBYTES":{"type":["number","null"]},"PROXY_SERPS":{"type":["number","null"]},"PROXY_UNBLOCKER_UNITS":{"type":["number","null"]},"REQUEST_QUEUE_READS":{"type":["number","null"]},"REQUEST_QUEUE_WRITES":{"type":["number","null"]}},"type":"object"},"Schedule":{"allOf":[{"$ref":"#/components/schemas/ScheduleBase"},{"properties":{"actions":{"items":{"$ref":"#/components/schemas/ScheduleAction"},"type":"array"},"description":{"type":["string","null"]},"integrationTracking":{"$ref":"#/components/schemas/IntegrationTracking"},"notifications":{"$ref":"#/components/schemas/ScheduleNotifications"},"title":{"type":["string","null"]},"uiState":{"anyOf":[{"$ref":"#/components/schemas/ScheduleUiState"},{"type":"null"}]}},"required":["actions"],"type":"object"}]},"ScheduleAction":{"oneOf":[{"$ref":"#/components/schemas/ScheduleActionRunActor"},{"$ref":"#/components/schemas/ScheduleActionRunActorTask"}]},"ScheduleActionRunActor":{"properties":{"actorId":{"type":"string"},"id":{"type":"string"},"runInput":{"anyOf":[{"$ref":"#/components/schemas/ScheduleActionRunInput"},{"type":"null"}]},"runOptions":{"anyOf":[{"$ref":"#/components/schemas/TaskOptions"},{"type":"null"}]},"type":{}},"required":["id","type","actorId"],"type":"object"},"ScheduleActionRunActorTask":{"properties":{"actorTaskId":{"type":"string"},"id":{"type":"string"},"input":{"anyOf":[{"type":"object"},{"type":"null"}]},"type":{}},"required":["id","type","actorTaskId"],"type":"object"},"ScheduleActionRunInput":{"properties":{"body":{"type":["string","null"]},"contentType":{"type":["string","null"]}},"type":"object"},"ScheduleBase":{"properties":{"createdAt":{"format":"date-time","type":"string"},"cronExpression":{"type":"string"},"id":{"type":"string"},"isEnabled":{"type":"boolean"},"isExclusive":{"type":"boolean"},"lastRunAt":{"format":"date-time","type":["string","null"]},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":"string"},"nextRunAt":{"format":"date-time","type":["string","null"]},"timezone":{"type":"string"},"userId":{"type":"string"}},"required":["id","userId","name","cronExpression","timezone","isEnabled","isExclusive","createdAt","modifiedAt"],"type":"object"},"ScheduleListItem":{"allOf":[{"$ref":"#/components/schemas/ScheduleBase"},{"properties":{"actions":{"items":{"$ref":"#/components/schemas/ScheduleListItemAction"},"type":"array"},"integrationTracking":{"$ref":"#/components/schemas/IntegrationTracking"},"notifications":{"$ref":"#/components/schemas/ScheduleNotifications"},"title":{"type":["string","null"]}},"required":["actions"],"type":"object"}]},"ScheduleListItemAction":{"oneOf":[{"$ref":"#/components/schemas/ScheduleListItemActionRunActor"},{"$ref":"#/components/schemas/ScheduleListItemActionRunActorTask"}]},"ScheduleListItemActionRunActor":{"properties":{"actorId":{"type":"string"},"id":{"type":"string"},"type":{}},"required":["id","type","actorId"],"type":"object"},"ScheduleListItemActionRunActorTask":{"properties":{"actorTaskId":{"type":"string"},"id":{"type":"string"},"type":{}},"required":["id","type","actorTaskId"],"type":"object"},"ScheduleLogEntry":{"properties":{"createdAt":{"format":"date-time","type":"string"},"level":{"type":"string"},"message":{"type":"string"}},"required":["message","level","createdAt"],"type":"object"},"ScheduleLogResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ScheduleLogEntry"},"type":"array"}},"required":["data"],"type":"object"},"ScheduleNotifications":{"properties":{"email":{"type":"boolean"}},"type":"object"},"ScheduleResponse":{"properties":{"data":{"$ref":"#/components/schemas/Schedule"}},"required":["data"],"type":"object"},"ScheduleUiState":{"properties":{"data":{"properties":{"cronExpression":{"type":"string"},"timezone":{"type":"string"}},"type":"object"},"version":{"type":"integer"}},"type":"object"},"ServiceUsage":{"type":"object"},"SourceCodeFile":{"properties":{"content":{"type":"string"},"format":{"$ref":"#/components/schemas/SourceCodeFileFormat"},"name":{"type":"string"}},"required":["name"],"type":"object"},"SourceCodeFileFormat":{"type":"string"},"SourceCodeFolder":{"properties":{"folder":{"type":"boolean"},"name":{"type":"string"}},"required":["name","folder"],"type":"object"},"StoreActor":{"properties":{"actorReviewCount":{"type":"integer"},"actorReviewRating":{"type":"number"},"badge":{"type":["string","null"]},"bookmarkCount":{"type":"integer"},"categories":{"items":{"type":"string"},"type":"array"},"currentPricingInfo":{"$ref":"#/components/schemas/CurrentPricingInfo"},"description":{"type":["string","null"]},"id":{"type":"string"},"inputSchema":{"properties":{"properties":{"type":"object"},"required":{"items":{"type":"string"},"type":"array"},"type":{"type":"string"}},"required":["type","properties"],"type":"object"},"isWhiteListedForAgenticPayments":{"type":["boolean","null"]},"name":{"type":"string"},"notice":{"$ref":"#/components/schemas/ActorNotice"},"pictureUrl":{"type":["string","null"]},"readmeSummary":{"type":"string"},"stats":{"$ref":"#/components/schemas/ActorStats"},"title":{"type":"string"},"url":{"format":"uri","type":["string","null"]},"userFullName":{"type":["string","null"]},"userPictureUrl":{"format":"uri","type":["string","null"]},"username":{"type":"string"}},"required":["id","title","name","username","stats"],"type":"object"},"TaggedBuilds":{"type":"object"},"Task":{"properties":{"actId":{"type":"string"},"actorStandby":{"anyOf":[{"$ref":"#/components/schemas/ActorStandby"},{"type":"null"}]},"createdAt":{"format":"date-time","type":"string"},"description":{"type":["string","null"]},"id":{"type":"string"},"input":{"anyOf":[{"$ref":"#/components/schemas/TaskInput"},{"items":{"$ref":"#/components/schemas/TaskInput"},"type":"array"},{"type":"null"}]},"isPublic":{"type":"boolean"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":"string"},"nameLowerCase":{"type":"string"},"options":{"anyOf":[{"$ref":"#/components/schemas/TaskOptions"},{"type":"null"}]},"publicConfig":{"anyOf":[{"$ref":"#/components/schemas/TaskPublicConfig"},{"type":"null"}]},"removedAt":{"format":"date-time","type":["string","null"]},"standbyUrl":{"format":"uri","type":["string","null"]},"stats":{"anyOf":[{"$ref":"#/components/schemas/TaskStats"},{"type":"null"}]},"title":{"type":["string","null"]},"userId":{"type":"string"},"username":{"type":["string","null"]}},"required":["id","userId","actId","name","createdAt","modifiedAt"],"type":"object"},"TaskInput":{"type":"object"},"TaskListItem":{"properties":{"actId":{"type":"string"},"actName":{"type":["string","null"]},"actUsername":{"type":["string","null"]},"createdAt":{"format":"date-time","type":"string"},"id":{"type":"string"},"isPublic":{"type":"boolean"},"modifiedAt":{"format":"date-time","type":"string"},"name":{"type":"string"},"stats":{"anyOf":[{"$ref":"#/components/schemas/TaskStats"},{"type":"null"}]},"title":{"type":["string","null"]},"userId":{"type":"string"},"username":{"type":["string","null"]}},"required":["id","userId","actId","name","createdAt","modifiedAt"],"type":"object"},"TaskOptions":{"properties":{"build":{"type":["string","null"]},"forcePermissionLevel":{"anyOf":[{"$ref":"#/components/schemas/ActorPermissionLevel"},{"type":"null"}]},"isMaxTotalChargeUsdSetByUser":{"type":["boolean","null"]},"maxItems":{"type":["integer","null"]},"maxTotalChargeUsd":{"type":["number","null"]},"memoryMbytes":{"type":["integer","null"]},"restartOnError":{"type":["boolean","null"]},"timeoutSecs":{"type":["integer","null"]}},"type":"object"},"TaskPublicConfig":{"properties":{"categorization":{"type":["string","null"]},"datasetName":{"type":["string","null"]},"datasetView":{"type":["string","null"]},"faq":{"items":{"properties":{"answer":{"type":"string"},"question":{"type":"string"}},"required":["question","answer"],"type":"object"},"type":["array","null"]},"highlightedFields":{"items":{"type":"string"},"type":["array","null"]},"inputSchemaFields":{"items":{"type":"string"},"type":["array","null"]},"publishedAt":{"format":"date-time","type":["string","null"]},"seoDescription":{"maxLength":160,"type":["string","null"]},"seoTitle":{"maxLength":60,"type":["string","null"]}},"type":"object"},"TaskResponse":{"properties":{"data":{"$ref":"#/components/schemas/Task"}},"required":["data"],"type":"object"},"TaskStats":{"properties":{"lastRunStartedAt":{"format":"date-time","type":["string","null"]},"totalRuns":{"type":"integer"}},"type":"object"},"TieredPricingPerDatasetItem":{"type":"object"},"TotalRequestCount":{"minimum":0,"type":"integer"},"UniqueKey":{"type":"string"},"UnlockRequestsResponse":{"properties":{"data":{"$ref":"#/components/schemas/UnlockRequestsResult"}},"required":["data"],"type":"object"},"UnlockRequestsResult":{"properties":{"unlockedCount":{"type":"integer"}},"required":["unlockedCount"],"type":"object"},"UnprocessedRequest":{"properties":{"id":{"$ref":"#/components/schemas/RequestId"},"method":{"$ref":"#/components/schemas/RequestMethod"},"uniqueKey":{"$ref":"#/components/schemas/UniqueKey"},"url":{"$ref":"#/components/schemas/RequestUrl"}},"required":["uniqueKey","url"],"type":"object"},"UpdateRequestResponse":{"properties":{"data":{"$ref":"#/components/schemas/RequestRegistration"}},"required":["data"],"type":"object"},"UsageCycle":{"properties":{"endAt":{"format":"date-time","type":"string"},"startAt":{"format":"date-time","type":"string"}},"required":["startAt","endAt"],"type":"object"},"UserPlan":{"properties":{"apiRateLimitBoosts":{"type":"integer"},"availableAddOns":{"items":{"type":"string"},"type":"array"},"availableProxyGroups":{"$ref":"#/components/schemas/AvailableProxyGroups"},"dataRetentionDays":{"type":"integer"},"description":{"type":"string"},"enabledPlatformFeatures":{"items":{"type":"string"},"type":"array"},"forceBillingInterval":{"type":"string"},"id":{"type":"string"},"isEnabled":{"type":"boolean"},"isPayingCustomer":{"type":"boolean"},"legacyPlanId":{"type":"string"},"maxActorCount":{"type":"integer"},"maxActorMemoryGbytes":{"type":"number"},"maxActorTaskCount":{"type":"integer"},"maxConcurrentActorRuns":{"type":"integer"},"maxMonthlyActorComputeUnits":{"type":"number"},"maxMonthlyExternalDataTransferGbytes":{"type":"number"},"maxMonthlyProxySerps":{"type":"integer"},"maxMonthlyResidentialProxyGbytes":{"type":"number"},"maxMonthlyUnblockerUnits":{"type":"integer"},"maxMonthlyUsageUsd":{"type":"number"},"maxPublicActorTaskCount":{"type":"integer"},"maxPublicActorTaskCountPerActor":{"type":"integer"},"maxScheduleCount":{"type":"integer"},"monitoringLevel":{"type":["string","null"]},"monthlyBasePriceUsd":{"type":"number"},"monthlyUsageCreditsUsd":{"type":"number"},"overageThresholdUsd":{"type":"number"},"personalizedSupport":{"type":["string","null"]},"planPricing":{"type":"object"},"planTags":{"items":{"type":"string"},"type":"array"},"planVersion":{"type":"number"},"supportLevel":{"type":"string"},"teamAccountSeatCount":{"type":"integer"},"tier":{"type":"string"},"usageDiscountPercent":{"type":"number"}},"required":["availableProxyGroups"],"type":"object"},"UserPrivateInfo":{"properties":{"createdAt":{"format":"date-time","type":"string"},"effectivePlatformFeatures":{"$ref":"#/components/schemas/EffectivePlatformFeatures"},"email":{"format":"email","type":["string","null"]},"id":{"type":"string"},"isPaying":{"type":"boolean"},"organizationOwnerUserId":{"type":["string","null"]},"plan":{"$ref":"#/components/schemas/UserPlan"},"profile":{"$ref":"#/components/schemas/UserProfile"},"proxy":{"$ref":"#/components/schemas/ProxyResource"},"username":{"type":"string"}},"required":["username"],"type":"object"},"UserPrivateInfoResponse":{"properties":{"data":{"$ref":"#/components/schemas/UserPrivateInfo"}},"required":["data"],"type":"object"},"UserProfile":{"properties":{"bio":{"type":["string","null"]},"githubUsername":{"type":["string","null"]},"name":{"type":"string"},"pictureUrl":{"format":"uri","type":["string","null"]},"readme":{"type":["string","null"]},"twitterUsername":{"type":["string","null"]},"websiteUrl":{"format":"uri","type":["string","null"]}},"type":"object"},"UserPublicInfo":{"properties":{"profile":{"$ref":"#/components/schemas/UserProfile"},"username":{"type":"string"}},"required":["username"],"type":"object"},"UserPublicInfoResponse":{"properties":{"data":{"$ref":"#/components/schemas/UserPublicInfo"}},"required":["data"],"type":"object"},"Version":{"properties":{"actorPath":{"maxLength":255,"minLength":1,"type":["string","null"]},"applyEnvVarsToBuild":{"type":["boolean","null"]},"bitbucketWebhookId":{"type":["string","null"]},"buildTag":{"type":["string","null"]},"envVars":{"anyOf":[{"items":{"$ref":"#/components/schemas/EnvVar"},"type":"array"},{"type":"null"}]},"gitHubGistUrl":{"type":["string","null"]},"gitRepoUrl":{"type":["string","null"]},"githubWebhookId":{"type":["integer","null"]},"gitlabWebhookId":{"type":["integer","null"]},"sourceFiles":{"$ref":"#/components/schemas/VersionSourceFiles"},"sourceType":{"anyOf":[{"$ref":"#/components/schemas/VersionSourceType"},{"type":"null"}]},"tarballUrl":{"type":["string","null"]},"versionNumber":{"pattern":"^([0-9]|[1-9][0-9])\\.([0-9]|[1-9][0-9])$","type":"string"}},"required":["versionNumber","sourceType"],"type":"object"},"VersionResponse":{"properties":{"data":{"$ref":"#/components/schemas/Version"}},"required":["data"],"type":"object"},"VersionSourceFiles":{"items":{"anyOf":[{"$ref":"#/components/schemas/SourceCodeFile"},{"$ref":"#/components/schemas/SourceCodeFolder"}]},"type":"array"},"VersionSourceType":{"type":"string"},"WasAlreadyHandled":{"type":"boolean"},"WasAlreadyPresent":{"type":"boolean"},"WebhookCondition":{"properties":{"actorId":{"type":["string","null"]},"actorRunId":{"type":["string","null"]},"actorTaskId":{"type":["string","null"]}},"type":"object"},"WebhookDatasetTransformation":{"properties":{"fields":{"items":{"type":"string"},"type":"array"},"omit":{"items":{"type":"string"},"type":"array"}},"type":"object"},"WebhookDispatch":{"properties":{"calls":{"items":{"properties":{"debugPostMessage":{"type":"object"},"errorMessage":{"type":["string","null"]},"finishedAt":{"format":"date-time","type":["string","null"]},"responseBody":{"type":["string","null"]},"responseStatus":{"type":["integer","null"]},"startedAt":{"format":"date-time","type":["string","null"]}},"type":"object"},"type":"array"},"createdAt":{"format":"date-time","type":"string"},"eventData":{"properties":{"actorBuildId":{"type":"string"},"actorId":{"type":"string"},"actorRunId":{"type":"string"},"actorTaskId":{"type":["string","null"]}},"required":["actorId"],"type":["object","null"]},"eventType":{"$ref":"#/components/schemas/WebhookEventType"},"id":{"type":"string"},"status":{"$ref":"#/components/schemas/WebhookDispatchStatus"},"userId":{"type":"string"},"webhook":{"anyOf":[{"$ref":"#/components/schemas/WebhookDispatchWebhookSummary"},{"type":"null"}]},"webhookId":{"type":"string"}},"required":["id","userId","webhookId","createdAt","status","eventType"],"type":"object"},"WebhookDispatchResponse":{"properties":{"data":{"$ref":"#/components/schemas/WebhookDispatch"}},"required":["data"],"type":"object"},"WebhookDispatchStatus":{"type":"string"},"WebhookDispatchWebhookSummary":{"properties":{"_id":{"type":"string"},"actionType":{"type":"string"},"condition":{"$ref":"#/components/schemas/WebhookCondition"},"githubIssue":{"$ref":"#/components/schemas/WebhookGithubIssue"},"googleDrive":{"$ref":"#/components/schemas/WebhookGoogleDrive"},"googleMail":{"$ref":"#/components/schemas/WebhookGoogleMail"},"isAdHoc":{"type":"boolean"},"isApifyIntegration":{"type":["boolean","null"]},"requestUrl":{"format":"uri","type":["string","null"]},"slackMessage":{"$ref":"#/components/schemas/WebhookSlackMessage"}},"type":"object"},"WebhookEventType":{"type":"string"},"WebhookGithubIssue":{"properties":{"body":{"type":"string"},"repository":{"type":"string"},"title":{"type":"string"},"userIntegrationId":{"type":"string"}},"type":"object"},"WebhookGoogleDrive":{"properties":{"filename":{"type":"string"},"format":{"type":["string","null"]},"transformation":{"$ref":"#/components/schemas/WebhookDatasetTransformation"},"userIntegrationId":{"type":"string"}},"type":"object"},"WebhookGoogleMail":{"properties":{"attachmentFormat":{"type":"string"},"attachmentTransformation":{"$ref":"#/components/schemas/WebhookDatasetTransformation"},"body":{"type":"string"},"emailTo":{"items":{"type":"string"},"type":"array"},"subject":{"type":"string"},"userIntegrationId":{"type":"string"}},"type":"object"},"WebhookLastDispatch":{"properties":{"finishedAt":{"format":"date-time","type":["string","null"]},"removedAt":{"format":"date-time","type":["string","null"]},"status":{"$ref":"#/components/schemas/WebhookDispatchStatus"}},"required":["status"],"type":"object"},"WebhookListItem":{"properties":{"actionType":{"type":"string"},"condition":{"$ref":"#/components/schemas/WebhookCondition"},"createdAt":{"format":"date-time","type":"string"},"description":{"type":["string","null"]},"doNotRetry":{"type":"boolean"},"eventTypes":{"items":{"$ref":"#/components/schemas/WebhookEventType"},"type":"array"},"githubIssue":{"$ref":"#/components/schemas/WebhookGithubIssue"},"googleDrive":{"$ref":"#/components/schemas/WebhookGoogleDrive"},"googleMail":{"$ref":"#/components/schemas/WebhookGoogleMail"},"headersTemplate":{"type":["string","null"]},"id":{"type":"string"},"ignoreSslErrors":{"type":"boolean"},"isAdHoc":{"type":["boolean","null"]},"isApifyIntegration":{"type":"boolean"},"isEnabled":{"type":"boolean"},"lastDispatch":{"anyOf":[{"$ref":"#/components/schemas/WebhookLastDispatch"},{"type":"null"}]},"modifiedAt":{"format":"date-time","type":"string"},"requestUrl":{"format":"uri","type":["string","null"]},"shouldInterpolateStrings":{"type":["boolean","null"]},"slackMessage":{"$ref":"#/components/schemas/WebhookSlackMessage"},"stats":{"anyOf":[{"$ref":"#/components/schemas/WebhookStats"},{"type":"null"}]},"title":{"type":["string","null"]},"userId":{"type":"string"}},"required":["id","createdAt","modifiedAt","userId","eventTypes","condition","ignoreSslErrors","doNotRetry"],"type":"object"},"WebhookResource":{"properties":{"actionType":{"type":"string"},"condition":{"$ref":"#/components/schemas/WebhookCondition"},"createdAt":{"format":"date-time","type":"string"},"description":{"type":["string","null"]},"doNotRetry":{"type":["boolean","null"]},"eventTypes":{"items":{"$ref":"#/components/schemas/WebhookEventType"},"type":"array"},"githubIssue":{"$ref":"#/components/schemas/WebhookGithubIssue"},"googleDrive":{"$ref":"#/components/schemas/WebhookGoogleDrive"},"googleMail":{"$ref":"#/components/schemas/WebhookGoogleMail"},"headersTemplate":{"type":["string","null"]},"id":{"type":"string"},"ignoreSslErrors":{"type":"boolean"},"isAdHoc":{"type":["boolean","null"]},"isApifyIntegration":{"type":["boolean","null"]},"isEnabled":{"type":"boolean"},"lastDispatch":{"anyOf":[{"$ref":"#/components/schemas/WebhookLastDispatch"},{"type":"null"}]},"modifiedAt":{"format":"date-time","type":"string"},"payloadTemplate":{"type":["string","null"]},"requestUrl":{"format":"uri","type":["string","null"]},"shouldInterpolateStrings":{"type":["boolean","null"]},"slackMessage":{"$ref":"#/components/schemas/WebhookSlackMessage"},"stats":{"anyOf":[{"$ref":"#/components/schemas/WebhookStats"},{"type":"null"}]},"title":{"type":["string","null"]},"userId":{"type":"string"}},"required":["id","createdAt","modifiedAt","userId","eventTypes","condition","ignoreSslErrors"],"type":"object"},"WebhookResponse":{"properties":{"data":{"$ref":"#/components/schemas/WebhookResource"}},"required":["data"],"type":"object"},"WebhookSlackMessage":{"properties":{"channelId":{"type":"string"},"messageTemplate":{"type":"string"},"userIntegrationId":{"type":"string"}},"type":"object"},"WebhookStats":{"properties":{"totalDispatches":{"type":"integer"}},"type":"object"}},"specVersion":"v2-2026-11-09T143926Z"} diff --git a/src/Apify.Client/Internal/RetryConfig.cs b/src/Apify.Client/Internal/RetryConfig.cs index 0a78b1e..baa4d53 100644 --- a/src/Apify.Client/Internal/RetryConfig.cs +++ b/src/Apify.Client/Internal/RetryConfig.cs @@ -1,18 +1,13 @@ namespace Apify.Client.Internal; -/// -/// Retry/timeout policy for the orchestrating HTTP client. -/// +/// Retry and timeout policy of the HTTP client. internal sealed class RetryConfig { /// Creates a retry policy. - /// Maximum retries (the request is attempted up to maxRetries + 1 times). - /// Minimum delay between retries, in ms; doubled on each retry. - /// Upper bound on the (exponentially growing) inter-retry delay, in ms. - /// - /// Overall per-request timeout budget, in seconds: the cap every attempt's growing timeout is clamped - /// to (the reference client's timeoutMaxSecs). - /// + /// Maximum retries; a request is attempted up to maxRetries + 1 times. + /// Minimum delay between retries in ms, doubled on each retry. + /// Upper bound of the growing delay in ms. + /// Overall per-request timeout in seconds: the cap of every attempt's timeout (the reference client's timeoutMaxSecs). /// Base duration of the tier, in seconds. /// Base duration of the tier, in seconds. /// Base duration of the tier, in seconds. @@ -27,19 +22,16 @@ public RetryConfig(int maxRetries, double minDelayMillis, double maxDelayMillis, MediumTimeoutSecs = mediumTimeoutSecs; } - /// Maximum number of retries (the request is attempted up to MaxRetries + 1 times). + /// Maximum number of retries. public int MaxRetries { get; } - /// Minimum delay between retries, in milliseconds; doubled on each retry (exponential backoff). + /// Minimum delay between retries in ms, doubled on each retry. public double MinDelayMillis { get; } - /// Upper bound on the (exponentially growing) inter-retry delay, in milliseconds. + /// Upper bound of the growing delay between retries in ms. public double MaxDelayMillis { get; } - /// - /// Overall per-request timeout budget, in seconds (every attempt's growing timeout is capped here, - /// regardless of tier). - /// + /// Overall per-request timeout in seconds: the cap of every attempt's timeout, whatever the tier. public double TimeoutSecs { get; } /// Base duration of the tier, in seconds. diff --git a/src/Apify.Client/Internal/Signatures.cs b/src/Apify.Client/Internal/Signatures.cs index 0dc99f4..67d2bb5 100644 --- a/src/Apify.Client/Internal/Signatures.cs +++ b/src/Apify.Client/Internal/Signatures.cs @@ -6,28 +6,24 @@ namespace Apify.Client.Internal; -/// -/// Apify storage-content URL signing, byte-for-byte compatible with the platform's -/// @apify/utilities implementation that the reference clients rely on. -/// +/// Signing of storage-content URLs, byte-for-byte compatible with @apify/utilities. internal static class Signatures { - /// Version tag embedded in storage-content signatures (upstream default). + /// Version tag in the signatures. private const string StorageContentSignatureVersion = "0"; - /// Number of leading hex characters of the HMAC digest used. + /// Number of leading hex characters of the HMAC digest that are used. private const int HmacSignatureHexLen = 30; - /// Base62 alphabet (digits, then lowercase, then uppercase), matching upstream. + /// Base62 alphabet: digits, lowercase, uppercase. private const string Base62Alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; private const int Base = 62; private const int ByteBase = 256; /// - /// Computes an Apify URL-signing signature, byte-for-byte compatible with upstream - /// createHmacSignature: HMAC-SHA256(secret, message) as lowercase hex, take the first 30 hex - /// characters, interpret them as a big integer, then base62-encode (alphabet 0-9a-zA-Z). + /// Computes the signature as createHmacSignature upstream does: HMAC-SHA256 as lowercase hex, the first 30 hex + /// characters read as a big integer and base62-encoded. /// public static string CreateHmacSignature(string secretKey, string message) { @@ -38,9 +34,8 @@ public static string CreateHmacSignature(string secretKey, string message) } /// - /// Rejects a zero or negative expiry, which would yield a URL that has already expired (the expiry is added to - /// the current time); a caller that wants a permanent URL leaves the expiry out. Matches the reference - /// client, which requires a positive expiresInSecs. + /// Rejects a zero or negative expiry, which would give a URL that has already expired. Omit the expiry for a permanent + /// URL. The reference client also requires a positive expiresInSecs. /// /// The expiry from the caller's arguments, or null when unset. /// The caller's parameter name, used in the error message. @@ -52,18 +47,15 @@ public static void ValidateExpiry(int? expiresInSecs, string paramName) } } - /// - /// Builds a storage-content signature for a resource's public URL, byte-for-byte compatible with - /// upstream createStorageContentSignature. - /// + /// Builds the signature of a resource's public URL, as upstream createStorageContentSignature. /// - /// It signs the message "{version}.{expiresAtMillis}.{resourceId}" (expiresAtMillis is - /// the absolute expiry in ms, or 0 for a non-expiring URL) with , - /// then returns the base64url (no padding) encoding of "{version}.{expiresAtMillis}.{hmac}". + /// It signs "{version}.{expiresAtMillis}.{resourceId}" (expiresAtMillis is the absolute expiry, or + /// 0 for none) with and returns the base64url encoding, without padding, of + /// "{version}.{expiresAtMillis}.{hmac}". /// - /// The store/dataset URL-signing secret key. + /// The URL-signing secret key of the store or dataset. /// The resource id being signed. - /// Optional expiry in seconds (null for a non-expiring URL). + /// Optional expiry in seconds (null for none). public static string SignStorageContent(string secretKey, string resourceId, int? expiresInSecs) { ValidateExpiry(expiresInSecs, nameof(expiresInSecs)); @@ -78,10 +70,7 @@ public static string SignStorageContent(string secretKey, string resourceId, int return Base64UrlNoPadding(Encoding.UTF8.GetBytes(envelope)); } - /// - /// Interprets a hex string as a big-endian non-negative integer and encodes it in base62. - /// Implemented with byte-wise long division (base 256 → base 62) so it needs no bignum dependency. - /// + /// Encodes a hex string, read as a big-endian integer, in base62 by long division, without a bignum dependency. private static string HexToBase62(string hex) { var digits = new List(Convert.FromHexString(hex).Length); @@ -118,7 +107,7 @@ private static string HexToBase62(string hex) return result.Length == 0 ? "0" : result.ToString(); } - /// Encodes bytes as base64url without padding (+/ → -_, trailing = removed). + /// Encodes bytes as base64url without padding. private static string Base64UrlNoPadding(byte[] bytes) { return Convert.ToBase64String(bytes) diff --git a/src/Apify.Client/Models/Actor.cs b/src/Apify.Client/Models/Actor.cs index d8c02b3..09ad350 100644 --- a/src/Apify.Client/Models/Actor.cs +++ b/src/Apify.Client/Models/Actor.cs @@ -33,28 +33,21 @@ public Actor(JsonObject data) /// Whether the Actor is publicly available in Apify Store. public bool? IsPublic => GetBool("isPublic"); - /// When the Actor was created (ISO-8601 string). + /// When the Actor was created (ISO 8601). public string? CreatedAt => GetString("createdAt"); - /// When the Actor was last modified (ISO-8601 string). + /// When the Actor was last modified (ISO 8601). public string? ModifiedAt => GetString("modifiedAt"); - /// - /// The URL of the Actor's server when it is a Server Actor (formerly Actor Standby), otherwise null - /// or absent. - /// + /// The URL of the Actor's server if it is a Server Actor (formerly Actor Standby), otherwise null. public string? StandbyUrl => GetString("standbyUrl"); /// /// The Server Actor settings (actorStandby): isEnabled, build, memoryMbytes, - /// idleTimeoutSecs, tenancy (SINGLE_TENANT or MULTI_TENANT) and others, or - /// null when absent. + /// idleTimeoutSecs, tenancy (SINGLE_TENANT or MULTI_TENANT) and others; null when absent. /// public JsonObject? ActorStandby => Get("actorStandby") as JsonObject; - /// - /// The builds the Actor's tags point to, keyed by tag (e.g. latest); each value holds the - /// buildId and buildNumber, or null when absent. - /// + /// The builds the Actor's tags point to, keyed by tag (latest), each with a buildId and buildNumber; null when absent. public JsonObject? TaggedBuilds => Get("taggedBuilds") as JsonObject; } diff --git a/src/Apify.Client/Models/ActorEnvVar.cs b/src/Apify.Client/Models/ActorEnvVar.cs index ae22924..a74d383 100644 --- a/src/Apify.Client/Models/ActorEnvVar.cs +++ b/src/Apify.Client/Models/ActorEnvVar.cs @@ -58,8 +58,7 @@ public bool? IsSecret set => SetOrRemove("isSecret", value); } - // Honor the documented "null fields are omitted" contract: a null assignment removes the key rather - // than writing a JSON null node. + // A null assignment removes the key instead of writing a JSON null, so null fields stay omitted. private void SetOrRemove(string key, string? value) { if (value is null) diff --git a/src/Apify.Client/Models/ActorRun.cs b/src/Apify.Client/Models/ActorRun.cs index 1487efd..8a8abb6 100644 --- a/src/Apify.Client/Models/ActorRun.cs +++ b/src/Apify.Client/Models/ActorRun.cs @@ -26,18 +26,18 @@ public ActorRun(JsonObject data) public string? UserId => GetString("userId"); /// - /// The current run status. One of the eight ActorJobStatus values: READY, RUNNING, - /// SUCCEEDED, FAILED, TIMING-OUT, TIMED-OUT, ABORTING, ABORTED. + /// The run status: READY, RUNNING, SUCCEEDED, FAILED, TIMING-OUT, TIMED-OUT, + /// ABORTING or ABORTED. /// public string? Status => GetString("status"); /// An optional human-readable status message. public string? StatusMessage => GetString("statusMessage"); - /// When the run started (ISO-8601 string). + /// When the run started (ISO 8601). public string? StartedAt => GetString("startedAt"); - /// When the run finished (absent while still running). + /// When the run finished; absent while it is running. public string? FinishedAt => GetString("finishedAt"); /// The ID of the build used for the run. @@ -52,25 +52,19 @@ public ActorRun(JsonObject data) /// The ID of the run's default request queue. public string? DefaultRequestQueueId => GetString("defaultRequestQueueId"); - /// - /// The URL of the run's container (for live access). Normalized to its RFC 3986 absolute-URI form - /// (see ), matching the reference client. - /// + /// The URL of the run's container, as an RFC 3986 absolute URI (see ). public string? ContainerUrl => GetUrlString("containerUrl"); - /// Whether the run has reached a terminal (finished) status. + /// Whether the run has reached a terminal status. public bool IsTerminal => Statuses.IsTerminal(Status); - /// - /// The URL of the run's page in Apify Console. Normalized to its RFC 3986 absolute-URI form (see - /// ), matching the reference client. - /// + /// The URL of the run in Apify Console, as an RFC 3986 absolute URI (see ). public string? ConsoleUrl => GetUrlString("consoleUrl"); /// - /// The Server Actor deployment of the run: an object whose deployment names the pool serving the run + /// The Server Actor deployment of the run: an object whose deployment names the pool that serves it /// (SINGLE_TENANT, MULTI_TENANT_OWNER_PAID, MULTI_TENANT_FREE_USERS or - /// MULTI_TENANT_PAYING_USERS), or null for a run not started by a Server Actor request. + /// MULTI_TENANT_PAYING_USERS). null for a run not started by a Server Actor request. /// public JsonObject? Standby => Get("standby") as JsonObject; } diff --git a/src/Apify.Client/Models/ActorTask.cs b/src/Apify.Client/Models/ActorTask.cs index 8d4da9b..d4b72cd 100644 --- a/src/Apify.Client/Models/ActorTask.cs +++ b/src/Apify.Client/Models/ActorTask.cs @@ -2,13 +2,8 @@ namespace Apify.Client.Models; -/// -/// A pre-configured Actor run (an Actor task). -/// -/// -/// Named ActorTask rather than Task to avoid colliding with -/// ; it corresponds to the reference client's task resource. -/// +/// An Actor task, a pre-configured Actor run. +/// Named ActorTask to avoid colliding with . public sealed class ActorTask : ApifyResource { /// Wraps a raw task object. @@ -36,16 +31,16 @@ public ActorTask(JsonObject data) /// The human-readable description shown in the UI and on the task's public landing page. public string? Description => GetString("description"); - /// When the task was created (ISO-8601 string). + /// When the task was created (ISO 8601). public string? CreatedAt => GetString("createdAt"); - /// When the task was last modified (ISO-8601 string). + /// When the task was last modified (ISO 8601). public string? ModifiedAt => GetString("modifiedAt"); /// - /// Whether the task is published on its public landing page in Apify Store. Derived from - /// 's publishedAt; set it via - /// or , not by writing this field directly. + /// Whether the task is published on its public landing page in Apify Store, derived from 's + /// publishedAt. Change it with or + /// . /// public bool? IsPublic => GetBool("isPublic"); diff --git a/src/Apify.Client/Models/ActorVersion.cs b/src/Apify.Client/Models/ActorVersion.cs index a268067..630148e 100644 --- a/src/Apify.Client/Models/ActorVersion.cs +++ b/src/Apify.Client/Models/ActorVersion.cs @@ -19,10 +19,9 @@ public ActorVersion(JsonObject data) public string? SourceType => GetString("sourceType"); /// - /// The Actor's directory inside the uploaded source of a SOURCE_FILES or TARBALL version, as a - /// path relative to the upload root with / separators (for example actors/typescript-actor). - /// null when the upload root is the Actor's directory, and absent for a TARBALL version when the - /// caller has no write access to the Actor. + /// The Actor's directory inside the uploaded source of a SOURCE_FILES or TARBALL version, a path relative + /// to the upload root with / separators (for example actors/typescript-actor). null when the upload + /// root is the Actor's directory; absent on a TARBALL version for a caller without write access to the Actor. /// public string? ActorPath => GetString("actorPath"); } diff --git a/src/Apify.Client/Models/ApifyResource.cs b/src/Apify.Client/Models/ApifyResource.cs index ae51f45..0316a42 100644 --- a/src/Apify.Client/Models/ApifyResource.cs +++ b/src/Apify.Client/Models/ApifyResource.cs @@ -6,13 +6,10 @@ namespace Apify.Client.Models; -/// -/// Base class for API resource models. -/// +/// Base class of the API resource models. /// -/// Each model wraps the raw decoded JSON object and exposes commonly-used fields as typed properties. -/// The full payload — including any field the API adds that is not modelled here — is always available -/// via and , so additive API changes never lose data. +/// A model wraps the raw JSON object and exposes common fields as typed properties. Every field, including ones the API +/// adds later, stays available through and . /// public abstract class ApifyResource { @@ -45,12 +42,10 @@ protected ApifyResource(JsonObject data) } /// - /// Reads a field the OpenAPI specification marks format: uri and normalizes it to its RFC 3986 - /// absolute-URI form, matching the reference client's z.url({ normalize: true }): the host is - /// lowercased and punycoded, a default port is dropped, an empty path becomes /, and unsafe - /// characters are percent-encoded. null if the field is absent. + /// Reads a format: uri field and normalizes it to an RFC 3986 absolute URI, as the reference client's + /// z.url({ normalize: true }) does: lowercase punycoded host, no default port, / for an empty path and + /// percent-encoded unsafe characters. null if the field is absent. /// - /// The field is present but not a valid absolute URL. protected string? GetUrlString(string key) { var raw = GetString(key); diff --git a/src/Apify.Client/Models/BatchDeleteResult.cs b/src/Apify.Client/Models/BatchDeleteResult.cs index 104774e..39811a2 100644 --- a/src/Apify.Client/Models/BatchDeleteResult.cs +++ b/src/Apify.Client/Models/BatchDeleteResult.cs @@ -4,10 +4,7 @@ namespace Apify.Client.Models; -/// -/// The result of a batch request-delete: the requests that were successfully removed and the ones that -/// could not be (and can be retried). -/// +/// The result of a batch request delete: the requests removed and the ones that failed and can be retried. public sealed class BatchDeleteResult { private BatchDeleteResult(IReadOnlyList processed, IReadOnlyList unprocessed) diff --git a/src/Apify.Client/Models/Build.cs b/src/Apify.Client/Models/Build.cs index 8db96da..fd7a337 100644 --- a/src/Apify.Client/Models/Build.cs +++ b/src/Apify.Client/Models/Build.cs @@ -20,28 +20,27 @@ public Build(JsonObject data) public string? ActId => GetString("actId"); /// - /// The current build status. One of the eight ActorJobStatus values: READY, RUNNING, - /// SUCCEEDED, FAILED, TIMING-OUT, TIMED-OUT, ABORTING, ABORTED. + /// The build status: READY, RUNNING, SUCCEEDED, FAILED, TIMING-OUT, TIMED-OUT, + /// ABORTING or ABORTED. /// public string? Status => GetString("status"); - /// When the build started (ISO-8601 string). + /// When the build started (ISO 8601). public string? StartedAt => GetString("startedAt"); - /// When the build finished (absent while still building). + /// When the build finished; absent while it is running. public string? FinishedAt => GetString("finishedAt"); /// The human-readable build number (e.g. 0.1.2). public string? BuildNumber => GetString("buildNumber"); /// - /// Digest of the built Docker image manifest, without the sha256: prefix. Compare the digests of - /// two builds to find out whether their image contents differ. null if the digest is not - /// available. + /// Digest of the built Docker image manifest, without the sha256: prefix; builds with different digests have + /// different image contents. null if unavailable. /// public string? ImageDigest => GetString("imageDigest"); - /// Whether the build has reached a terminal (finished) status. + /// Whether the build has reached a terminal status. public bool IsTerminal => Statuses.IsTerminal(Status); /// The changelog the Actor's source provided for this build, if any. @@ -56,9 +55,6 @@ public Build(JsonObject data) /// The ID of the Git commit the build's source was taken from, if it came from a Git repository. public string? GitCommitId => GetString("gitCommitId"); - /// - /// The Actor definition the build was made from (the actorDefinition object, including its - /// webServerSchema), or null when absent. - /// + /// The Actor definition of the build (actorDefinition, with its webServerSchema), or null when absent. public JsonObject? ActorDefinition => Get("actorDefinition") as JsonObject; } diff --git a/src/Apify.Client/Models/KeyValueStoreRecord.cs b/src/Apify.Client/Models/KeyValueStoreRecord.cs index 8738880..87ac4c8 100644 --- a/src/Apify.Client/Models/KeyValueStoreRecord.cs +++ b/src/Apify.Client/Models/KeyValueStoreRecord.cs @@ -1,14 +1,10 @@ namespace Apify.Client.Models; -/// -/// A single record retrieved from a key-value store. -/// +/// A record from a key-value store. /// -/// holds the record's raw bytes exactly as stored, so binary records (images, -/// gzip, protobuf, XLSX, …) survive a round-trip intact. Decode it according to : -/// for text use System.Text.Encoding.UTF8.GetString(record.Value), and for JSON -/// (e.g. records written with ) -/// deserialize the bytes with System.Text.Json.JsonSerializer.Deserialize<T>(record.Value). +/// holds the raw bytes exactly as stored, so binary records survive intact. Decode it by +/// : text with System.Text.Encoding.UTF8.GetString(record.Value), JSON with +/// System.Text.Json.JsonSerializer.Deserialize<T>(record.Value). /// public sealed class KeyValueStoreRecord { @@ -26,10 +22,7 @@ public KeyValueStoreRecord(string key, byte[] value, string? contentType) /// The record key. public string Key { get; } - /// - /// The raw record bytes, exactly as stored. Decode according to (see the - /// class remarks for text/JSON decoding). - /// + /// The raw record bytes, exactly as stored. public byte[] Value { get; } /// The record's MIME type, as reported by the API. diff --git a/src/Apify.Client/Models/PaginationList.cs b/src/Apify.Client/Models/PaginationList.cs index 2e31d97..e0afeec 100644 --- a/src/Apify.Client/Models/PaginationList.cs +++ b/src/Apify.Client/Models/PaginationList.cs @@ -6,18 +6,13 @@ namespace Apify.Client.Models; -/// -/// A single page of an offset/limit-paginated list. -/// +/// A page of an offset/limit-paginated list. /// -/// The pagination metadata (, , , -/// , ) accompanies the . is the -/// number of entries the API went through for this page, which can differ from the number of items -/// returned: use Items.Count for the items. The list's own Count (through -/// , so also LINQ's Count()), enumeration and indexer cover the -/// items only. The API's total across all pages is exposed separately as . Note: -/// reflects the API's reported total, which can briefly lag immediately after a write (the -/// count is computed asynchronously) — re-read after a short delay if you need an exact post-write total. +/// The page metadata (, , , , +/// ) accompanies the . is the number of entries the API went +/// through for this page, which can differ from the number of items, so use Items.Count for the items. The +/// list's own Count (, LINQ's Count()), enumeration and indexer +/// cover the items only. can lag briefly after a write, since the API computes it asynchronously. /// /// The hydrated item type. public sealed class PaginationList : IReadOnlyList @@ -56,10 +51,7 @@ internal static PaginationList FromData(JsonNode? data, Func h JsonValues.BoolOr(obj, "desc", false)); } - /// - /// Builds a page directly from items and metadata (used by the dataset-items endpoint, which returns - /// a bare array with pagination in response headers). - /// + /// Builds a page from items and metadata, for the dataset-items endpoint, which returns a bare array. /// The page items. /// The rows the API scanned for the page, or the item count when that is larger. /// Total number of items available across all pages. @@ -69,7 +61,7 @@ internal static PaginationList FromData(JsonNode? data, Func h internal static PaginationList FromItems(IReadOnlyList items, long count, long total, long offset, long limit, bool desc) => new(items, count, total, offset, limit, desc); - /// The items of this page (never null). + /// The items of this page. public IReadOnlyList Items => _items; /// Total number of items available across all pages. @@ -82,15 +74,13 @@ internal static PaginationList FromItems(IReadOnlyList items, long count, public long Limit { get; } /// - /// Number of entries on this page. On a page from , the - /// number of dataset rows the API scanned for it, or the number of items returned when that is larger, so - /// filters such as or - /// can leave it above Items.Count. The next page - /// starts at Offset + Math.Min(Count, Limit); the cap matters only with - /// , which can return more items than the rows scanned. - /// On any other page it is the count the API reports, which is the number of items returned except - /// where the API drops entries after counting them, as the Store listing does. Use - /// for the count across all pages. + /// The number of entries on this page. For it is the number of + /// dataset rows the API scanned, or the number of items if that is larger, so filters such as + /// or can + /// leave it above Items.Count. The next page starts at Offset + Math.Min(Count, Limit); the cap matters + /// with , which can return more items than rows. For other pages it + /// is the count the API reports, which equals the items returned unless the API drops entries after counting + /// them, as the Store listing does. is the count across all pages. /// public long Count { get; } diff --git a/src/Apify.Client/Models/RequestQueueOperationInfo.cs b/src/Apify.Client/Models/RequestQueueOperationInfo.cs index 9f2270a..8f5df8c 100644 --- a/src/Apify.Client/Models/RequestQueueOperationInfo.cs +++ b/src/Apify.Client/Models/RequestQueueOperationInfo.cs @@ -15,10 +15,7 @@ public RequestQueueOperationInfo(JsonObject data) /// The ID of the affected request. public string? RequestId => GetString("requestId"); - /// - /// The unique key of the affected request. Populated for batch-add results; may be null for - /// single add/update operations. - /// + /// The unique key of the affected request. Set for batch-add results, may be null otherwise. public string? UniqueKey => GetString("uniqueKey"); /// Whether the request was already in the queue. diff --git a/src/Apify.Client/Models/RequestQueueRequest.cs b/src/Apify.Client/Models/RequestQueueRequest.cs index 9f4628f..016d932 100644 --- a/src/Apify.Client/Models/RequestQueueRequest.cs +++ b/src/Apify.Client/Models/RequestQueueRequest.cs @@ -2,10 +2,7 @@ namespace Apify.Client.Models; -/// -/// A single request stored in a request queue. Fields left null are omitted when the request is -/// sent to the API. Construct one for adding to a queue, or receive one when reading a queue. -/// +/// A request in a request queue. null fields are omitted when the request is sent to the API. public sealed class RequestQueueRequest : ApifyResource { private RequestQueueRequest(JsonObject data) @@ -62,10 +59,7 @@ public string? Method set => SetString("method", value); } - /// - /// The number of times this request has been retried after a failed processing attempt (assigned by - /// the API; absent on create). - /// + /// How many times the request was retried after a failed processing attempt (assigned by the API). public long? RetryCount { get => GetInt("retryCount"); @@ -83,8 +77,8 @@ public long? RetryCount } /// - /// When this request's processing lock expires (ISO-8601 string). Only present on requests returned - /// by . + /// When the request's lock expires (ISO 8601). Only on requests from + /// . /// public string? LockExpiresAt { @@ -93,8 +87,8 @@ public string? LockExpiresAt } /// - /// The client key of the client that currently holds this request's processing lock. Only present on - /// locked requests returned by . + /// The client key of the client that holds the request's lock. Only on locked requests from + /// . /// public string? LockByClient { @@ -102,9 +96,7 @@ public string? LockByClient set => SetString("lockByClient", value); } - /// - /// When this request was handled (ISO-8601 string); null while the request is still pending. - /// + /// When the request was handled (ISO 8601); null while it is pending. public string? HandledAt { get => GetString("handledAt"); diff --git a/src/Apify.Client/Models/RequestQueueRequestsPage.cs b/src/Apify.Client/Models/RequestQueueRequestsPage.cs index b2f46f0..8aaabbb 100644 --- a/src/Apify.Client/Models/RequestQueueRequestsPage.cs +++ b/src/Apify.Client/Models/RequestQueueRequestsPage.cs @@ -4,10 +4,7 @@ namespace Apify.Client.Models; -/// -/// A single, cursor-paginated page of a request queue's requests (as returned by -/// ). -/// +/// A cursor-paginated page of requests, from . public sealed class RequestQueueRequestsPage { private RequestQueueRequestsPage( diff --git a/src/Apify.Client/Models/TaskPublicConfig.cs b/src/Apify.Client/Models/TaskPublicConfig.cs index 4776e7e..02ebc16 100644 --- a/src/Apify.Client/Models/TaskPublicConfig.cs +++ b/src/Apify.Client/Models/TaskPublicConfig.cs @@ -3,13 +3,10 @@ namespace Apify.Client.Models; -/// -/// Public-facing display configuration for a task's public landing page in Apify Store. -/// +/// Display configuration of a task's public landing page in Apify Store. /// -/// The task is published when is set and unpublished when it is -/// null. is read-only; use -/// and to change the publication state. +/// The task is published when is set. It is read-only; use +/// and . /// public sealed class TaskPublicConfig : ApifyResource { @@ -20,7 +17,7 @@ public TaskPublicConfig(JsonObject data) { } - /// When the task was published (ISO-8601 string), or null if it is not published. + /// When the task was published (ISO 8601), or null if it is not. public string? PublishedAt => GetString("publishedAt"); /// Name to display for search engines such as Google. @@ -29,11 +26,7 @@ public TaskPublicConfig(JsonObject data) /// Description to display for search engines such as Google. public string? SeoDescription => GetString("seoDescription"); - /// - /// The task's category on its public landing page. Not part of the documented OpenAPI schema for - /// this resource; kept for parity with the reference JS client, which exposes the same - /// undocumented field. - /// + /// The category of the task on its public landing page. Not in the OpenAPI schema; kept because the reference client exposes it. public string? Categorization => GetString("categorization"); /// The input schema fields shown on the public landing page. diff --git a/src/Apify.Client/Models/Webhook.cs b/src/Apify.Client/Models/Webhook.cs index 0627c65..aab04fd 100644 --- a/src/Apify.Client/Models/Webhook.cs +++ b/src/Apify.Client/Models/Webhook.cs @@ -19,10 +19,7 @@ public Webhook(JsonObject data) /// The ID of the user who owns the webhook. public string? UserId => GetString("userId"); - /// - /// The URL the webhook posts to. Normalized to its RFC 3986 absolute-URI form (see - /// ), matching the reference client. - /// + /// The URL the webhook posts to, as an RFC 3986 absolute URI (see ). public string? RequestUrl => GetUrlString("requestUrl"); /// The events that trigger the webhook. diff --git a/src/Apify.Client/Options/ActorListOptions.cs b/src/Apify.Client/Options/ActorListOptions.cs index 442b30c..f0702a4 100644 --- a/src/Apify.Client/Options/ActorListOptions.cs +++ b/src/Apify.Client/Options/ActorListOptions.cs @@ -12,9 +12,8 @@ public sealed class ActorListOptions public int? Limit { get; init; } /// - /// Maximum number of items requested per API call when iterating across pages (must be positive). Only - /// iteration uses it, as the page size; it is never sent to the API. The effective page size is the - /// smaller of this and the remaining Limit. Defaults to the API's own page size. + /// Maximum items requested per API call when iterating, which must be positive. It sizes the pages and is never sent to + /// the API; the page size is the smaller of this and the remaining Limit. Defaults to the API's page size. /// public int? ChunkSize { get; init; } diff --git a/src/Apify.Client/Options/ActorServerRequestOptions.cs b/src/Apify.Client/Options/ActorServerRequestOptions.cs index e7b4302..860ec1b 100644 --- a/src/Apify.Client/Options/ActorServerRequestOptions.cs +++ b/src/Apify.Client/Options/ActorServerRequestOptions.cs @@ -12,26 +12,24 @@ public sealed class ActorServerRequestOptions public HttpMethod? Method { get; init; } /// - /// The query parameters of the request, converted as for API calls: null values are left out, booleans - /// become 1/0, dates become ISO 8601 in UTC (a of unspecified kind is - /// taken as UTC), lists become their comma-joined values and other values use their invariant text form. A - /// query in the path is kept and these are appended to it. + /// The query parameters, converted as for API calls: null values are left out, booleans become 1/0, + /// dates ISO 8601 in UTC (a of unspecified kind is taken as UTC), lists their comma-joined values + /// and other values their invariant text. A query in the path is kept and these are appended. /// public IReadOnlyDictionary? Query { get; init; } /// /// Additional request headers. They reach the Actor's server as they are, Authorization included, and - /// a header of the token's name (X-Apify-Authorization) replaces the token. A Content-Type header - /// is the same as . A Content-Encoding header marks a body that is already - /// encoded, which is then sent without compressing it (identity opts out of compression). Other - /// Content-* headers, such as Content-Disposition, describe the body and are sent only with a - /// ; a Content-Length header is ignored, since the client sets the length itself. + /// X-Apify-Authorization replaces the token. Content-Type equals . A + /// Content-Encoding marks a body that is already encoded and is sent without compressing it (identity opts + /// out of compression). Other Content-* headers, such as Content-Disposition, are sent only with a + /// . Content-Length is ignored, since the client sets it. /// public IReadOnlyDictionary? Headers { get; init; } /// - /// The request body, serialized as for API calls: raw bytes (byte[]) and a string are sent as - /// they are, and any other value is serialized to JSON. null sends no body. + /// The request body: byte[] and string are sent as they are, any other value is serialized to JSON, and + /// null sends no body. /// public object? Body { get; init; } @@ -42,24 +40,29 @@ public sealed class ActorServerRequestOptions public string? ContentType { get; init; } /// - /// The compressor for the request body, for example . The body is sent - /// uncompressed when it is not set, since the Actor's server may not decode a compressed body. The body is - /// compressed under the same conditions as for API calls. Set it only if the Actor's server decodes the + /// The X-Actor-Request-ID header of the request a multi-tenant Server Actor is handling, sent on as the + /// same header so the called Actor charges at that request's pricing tier. It replaces an + /// X-Actor-Request-ID in . Use a client created with the run's APIFY_TOKEN. + /// + public string? ParentRequestId { get; init; } + + /// + /// The compressor of the request body, for example . Without it the body is sent + /// uncompressed, since the Actor's server may not decode a compressed body. Set it only if the server decodes the /// resulting Content-Encoding. Matches the reference client's compression option. /// public HttpCompressor? Compressor { get; init; } /// - /// Whether to return the response body unread, to read it as a stream with - /// . The response then holds its connection open - /// until it is disposed. Defaults to false, which reads the whole body before returning. + /// Whether to return the response body unread, to read it with . + /// The response holds its connection until disposed. Defaults to false, which reads the whole body first. /// public bool StreamResponse { get; init; } /// - /// The timeout of the HTTP request. Defaults to the client's Long timeout tier, which covers the cold - /// start of an idle Actor. With , it bounds the wait for the server to start - /// responding. A value above is capped at it. + /// The timeout of the HTTP request. Defaults to the Long tier, which covers the cold start of an idle Actor. With + /// it bounds the wait for the server to start responding. A value above + /// is capped at it. /// public TimeSpan? Timeout { get; init; } } diff --git a/src/Apify.Client/Options/ActorStartOptions.cs b/src/Apify.Client/Options/ActorStartOptions.cs index badc4d7..e2effef 100644 --- a/src/Apify.Client/Options/ActorStartOptions.cs +++ b/src/Apify.Client/Options/ActorStartOptions.cs @@ -32,21 +32,16 @@ public sealed class ActorStartOptions public bool? RestartOnError { get; init; } /// - /// Retries the start while the account lacks the memory or a concurrent-run slot for the run (an - /// actor-memory-limit-exceeded/concurrent-runs-limit-exceeded error), instead of throwing - /// immediately. null (the default) disables retrying. + /// Retries the start while the account lacks the memory or a concurrent-run slot + /// (actor-memory-limit-exceeded, concurrent-runs-limit-exceeded) instead of throwing. null (the + /// default) does not retry. /// public WaitForResources? WaitForResources { get; init; } - /// - /// Override the Actor's permission level for this run (LIMITED_PERMISSIONS/FULL_PERMISSIONS). - /// + /// Overrides the Actor's permission level for this run (LIMITED_PERMISSIONS or FULL_PERMISSIONS). public string? ForcePermissionLevel { get; init; } - /// - /// Ad-hoc webhooks to attach to this run; a JSON-serializable list serialized to base64-encoded JSON - /// as the webhooks query parameter. - /// + /// Ad-hoc webhooks for this run: a JSON-serializable list, sent as base64-encoded JSON in the webhooks query parameter. public object? Webhooks { get; init; } /// The configured content type, or the JSON default when unset. @@ -67,9 +62,8 @@ internal void AppendTo(QueryParams q) } /// - /// Encodes an ad-hoc webhooks list as base64-encoded JSON, as the API's webhooks query - /// parameter requires. Returns null for a null list. Shared by Actor and task start - /// options. + /// Encodes ad-hoc webhooks as base64-encoded JSON for the webhooks query parameter; null for null. + /// Shared by the Actor and task start options. /// internal static string? EncodeWebhooks(object? webhooks) { diff --git a/src/Apify.Client/Options/BatchAddRequestsOptions.cs b/src/Apify.Client/Options/BatchAddRequestsOptions.cs index 3c67ec0..cb59b95 100644 --- a/src/Apify.Client/Options/BatchAddRequestsOptions.cs +++ b/src/Apify.Client/Options/BatchAddRequestsOptions.cs @@ -2,19 +2,16 @@ namespace Apify.Client.Options; -/// -/// Tuning options for batch request adding, mirroring the reference client. Requests the API reports as -/// unprocessed (typically due to rate limiting) are automatically retried with exponential backoff. -/// +/// Tuning options of batch request adding. Requests the API reports as unprocessed are retried with exponential backoff. public sealed class BatchAddRequestsOptions { - /// Default number of retry rounds for unprocessed requests (matches the reference client). + /// Default number of retry rounds for unprocessed requests, as in the reference client. public const int DefaultMaxUnprocessedRetries = 3; - /// Default maximum number of batch API calls made in parallel (matches the reference client). + /// Default maximum number of parallel batch API calls, as in the reference client. public const int DefaultMaxParallel = 5; - /// Default minimum delay before retrying unprocessed requests (matches the reference client). + /// Default minimum delay before retrying unprocessed requests, as in the reference client. public const int DefaultMinDelayMillis = 500; /// Creates batch-add options, clamping values to their valid ranges. diff --git a/src/Apify.Client/Options/DatasetDownloadOptions.cs b/src/Apify.Client/Options/DatasetDownloadOptions.cs index eab665f..0349404 100644 --- a/src/Apify.Client/Options/DatasetDownloadOptions.cs +++ b/src/Apify.Client/Options/DatasetDownloadOptions.cs @@ -2,10 +2,7 @@ namespace Apify.Client.Options; -/// -/// Adds format-specific options for downloading dataset items on top of the shared item -/// filtering/projection options (). -/// +/// Format-specific options for downloading dataset items, on top of the filtering and projection options of . public sealed class DatasetDownloadOptions { /// The shared filtering/projection options. diff --git a/src/Apify.Client/Options/DatasetListItemsOptions.cs b/src/Apify.Client/Options/DatasetListItemsOptions.cs index 77903eb..826c88a 100644 --- a/src/Apify.Client/Options/DatasetListItemsOptions.cs +++ b/src/Apify.Client/Options/DatasetListItemsOptions.cs @@ -3,28 +3,22 @@ namespace Apify.Client.Options; -/// -/// Configures listing or downloading dataset items (GET /v2/datasets/{datasetId}/items). All -/// fields are optional. -/// +/// Configures listing or downloading dataset items. All fields are optional. public sealed class DatasetListItemsOptions { /// Number of items to skip. public int? Offset { get; init; } /// - /// Maximum number of dataset rows to read. Filters can return fewer items than rows read and - /// more. When iterating, it caps the rows read in total across all pages; 0 - /// or unset means no cap. + /// Maximum number of dataset rows to read. Filters can return fewer items than rows and more. When + /// iterating, it caps the rows read across all pages; 0 or unset means no cap. /// public int? Limit { get; init; } /// - /// Maximum number of items requested per API call when iterating across pages (must be positive). Only - /// iteration uses it, as the page size; it is never sent to the API. The effective page size is the - /// smaller of this and the remaining Limit. Dataset items have no default limit, so without - /// ChunkSize and Limit iteration loads the whole dataset in one response; set it to iterate a - /// large dataset. + /// Maximum rows requested per API call when iterating (must be positive); never sent to the API. The page size is the + /// smaller of this and the remaining Limit. Without ChunkSize and Limit, iteration loads the whole + /// dataset in one response, so set it for a large dataset. /// public int? ChunkSize { get; init; } diff --git a/src/Apify.Client/Options/IterateKeysOptions.cs b/src/Apify.Client/Options/IterateKeysOptions.cs index 8385dc6..6899126 100644 --- a/src/Apify.Client/Options/IterateKeysOptions.cs +++ b/src/Apify.Client/Options/IterateKeysOptions.cs @@ -1,10 +1,6 @@ namespace Apify.Client.Options; -/// -/// Configures lazy iteration over a key-value store's keys -/// (), mirroring the reference -/// client's chunked listKeys() iteration (limit, chunkSize, ...). -/// +/// Configures , as the reference client's chunked listKeys(). public sealed class IterateKeysOptions { /// Default maximum number of keys fetched per page (matches the API's own default/maximum). @@ -14,16 +10,12 @@ public sealed class IterateKeysOptions public int? Limit { get; init; } /// - /// Maximum number of keys requested per API call (must be positive; defaults to - /// ). Never sent to the API as-is; it only sizes each page request. - /// Matches the reference client's chunkSize. + /// Maximum keys requested per API call, which must be positive (default ). It only + /// sizes the pages and is never sent to the API. As the reference client's chunkSize. /// public int? ChunkSize { get; init; } - /// - /// The earlier name of , kept so existing callers keep working. - /// takes precedence when both are set. - /// + /// The earlier name of ; wins when both are set. public int? MaxPageLimit { get; init; } /// Start iterating after this key (first page only). diff --git a/src/Apify.Client/Options/LastRunOptions.cs b/src/Apify.Client/Options/LastRunOptions.cs index fd32f95..c5842c2 100644 --- a/src/Apify.Client/Options/LastRunOptions.cs +++ b/src/Apify.Client/Options/LastRunOptions.cs @@ -1,15 +1,10 @@ namespace Apify.Client.Options; -/// -/// Filters which "last" run the last-run accessors resolve to. Leave a field null to leave that -/// filter unset. -/// +/// Filters which last run the last-run accessors resolve to. A null field leaves that filter unset. /// -/// Status and Origin are both spec-declared query parameters on the last-run endpoints -/// (GET /v2/actors/{actorId}/runs/last and GET /v2/actor-tasks/{actorTaskId}/runs/last), -/// matching the reference client's lastRun({ status, origin }). The spec also declares -/// waitForFinish on those endpoints, but the reference client does not expose it on lastRun, -/// so it is intentionally omitted here for parity. +/// Status and Origin are the query parameters of the last-run endpoints, as in the reference client's +/// lastRun({ status, origin }). waitForFinish is left out because the reference client does not expose it +/// there. /// public sealed class LastRunOptions { diff --git a/src/Apify.Client/Options/ListOptions.cs b/src/Apify.Client/Options/ListOptions.cs index 521899c..0eb57cd 100644 --- a/src/Apify.Client/Options/ListOptions.cs +++ b/src/Apify.Client/Options/ListOptions.cs @@ -3,9 +3,8 @@ namespace Apify.Client.Options; /// -/// The standard offset/limit pagination shared by most list endpoints (builds, runs, tasks, -/// schedules, webhooks, Actor versions). All fields are optional; leave one null to use the API -/// default. +/// The offset/limit pagination shared by the list endpoints (builds, runs, tasks, schedules, webhooks, Actor versions). +/// All fields are optional; null uses the API default. /// public sealed class ListOptions { @@ -16,9 +15,8 @@ public sealed class ListOptions public int? Limit { get; init; } /// - /// Maximum number of items requested per API call when iterating across pages (must be positive). Only - /// iteration uses it, as the page size; it is never sent to the API. The effective page size is the - /// smaller of this and the remaining Limit. Defaults to the API's own page size. + /// Maximum items requested per API call when iterating, which must be positive. It sizes the pages and is never sent to + /// the API; the page size is the smaller of this and the remaining Limit. Defaults to the API's page size. /// public int? ChunkSize { get; init; } diff --git a/src/Apify.Client/Options/PaginateRequestsOptions.cs b/src/Apify.Client/Options/PaginateRequestsOptions.cs index 3a3f008..39c993b 100644 --- a/src/Apify.Client/Options/PaginateRequestsOptions.cs +++ b/src/Apify.Client/Options/PaginateRequestsOptions.cs @@ -6,9 +6,8 @@ namespace Apify.Client.Options; /// -/// Configures lazy iteration over a request queue's requests -/// (), mirroring the -/// reference client's paginateRequests({ limit, maxPageLimit, exclusiveStartId, cursor, filter }). +/// Configures , as the reference client's +/// paginateRequests. /// public sealed class PaginateRequestsOptions { @@ -25,15 +24,12 @@ public sealed class PaginateRequestsOptions public int? Limit { get; init; } /// - /// Maximum number of requests fetched per API call (must be positive; defaults to - /// ). Matches the reference client's chunkSize. + /// Maximum requests fetched per API call, which must be positive (default ). As the + /// reference client's chunkSize. /// public int? ChunkSize { get; init; } - /// - /// The earlier name of , kept so existing callers keep working. - /// takes precedence when both are set. - /// + /// The earlier name of ; wins when both are set. public int? MaxPageLimit { get; init; } /// Start iterating after this request ID (first page only; mutually exclusive with cursor). @@ -42,10 +38,7 @@ public sealed class PaginateRequestsOptions /// An opaque pagination cursor to start from (mutually exclusive with ). public string? Cursor { get; init; } - /// - /// Restrict the iteration to requests in the given states; each value must be - /// or . - /// + /// Restricts the iteration to requests in the given states, each or . public IReadOnlyList? Filter { get; init; } /// Validates the options for API-level constraints. diff --git a/src/Apify.Client/Options/RequestQueueClientOptions.cs b/src/Apify.Client/Options/RequestQueueClientOptions.cs index 16f1a51..1bb1cef 100644 --- a/src/Apify.Client/Options/RequestQueueClientOptions.cs +++ b/src/Apify.Client/Options/RequestQueueClientOptions.cs @@ -1,21 +1,17 @@ namespace Apify.Client.Options; -/// -/// Per-client options for a , mirroring the -/// reference client's requestQueue(id, { clientKey, timeoutSecs }). -/// +/// Options of a , as the reference client's requestQueue(id, { clientKey, timeoutSecs }). public sealed class RequestQueueClientOptions { /// - /// A stable client key identifying this client to the queue. Required to operate on locks the client - /// itself created, and lets the API detect whether multiple clients access the queue. + /// A stable key that identifies this client to the queue. Needed to operate on locks the client created, and lets the API + /// detect several clients on one queue. /// public string? ClientKey { get; init; } /// - /// Per-request timeout (seconds) for this queue client's calls. It shortens the wait for each call and - /// is capped at the client-wide overall timeout, so a value larger than that timeout has no effect. When - /// null the shared client-wide timeout is used. + /// Per-request timeout in seconds for this queue client's calls, capped at the client-wide overall timeout. null + /// uses the client-wide timeout. /// public double? TimeoutSecs { get; init; } } diff --git a/src/Apify.Client/Options/RunChargeOptions.cs b/src/Apify.Client/Options/RunChargeOptions.cs index 3e82fc0..a224226 100644 --- a/src/Apify.Client/Options/RunChargeOptions.cs +++ b/src/Apify.Client/Options/RunChargeOptions.cs @@ -7,8 +7,8 @@ public sealed class RunChargeOptions /// The name of the event to charge for. Required. /// The number of times to charge the event (defaults to 1). /// - /// A key that deduplicates the charge across retries. If unset, one is auto-generated as - /// "{runId}-{eventName}-{timestampMillis}-{random}", matching the reference client. + /// A key that deduplicates the charge across retries. If unset, {runId}-{eventName}-{timestampMillis}-{random} is + /// generated, as in the reference client. /// public RunChargeOptions(string eventName, int? count = null, string? idempotencyKey = null) { @@ -27,9 +27,8 @@ public RunChargeOptions(string eventName, int? count = null, string? idempotency public string? IdempotencyKey { get; } /// - /// The ID of the Server Actor request being charged, taken from the X-Actor-Request-ID header of - /// the incoming request. Required for multi-tenant Server Actor runs, where it identifies the request - /// the charge belongs to. + /// The ID of the Server Actor request being charged, from the X-Actor-Request-ID header of the incoming request. + /// Required for multi-tenant Server Actor runs. Runs of single-tenant Server Actors ignore it. /// public string? RequestId { get; init; } diff --git a/src/Apify.Client/Options/RunIterateDatasetItemsOptions.cs b/src/Apify.Client/Options/RunIterateDatasetItemsOptions.cs index abd5bd7..fc4e6a1 100644 --- a/src/Apify.Client/Options/RunIterateDatasetItemsOptions.cs +++ b/src/Apify.Client/Options/RunIterateDatasetItemsOptions.cs @@ -3,9 +3,9 @@ namespace Apify.Client.Options; /// -/// Configures , which iterates over -/// the items of a run's default dataset while the run is still producing them. Mirrors the reference -/// client's RunIterateDatasetItemsOptions. All fields are optional. +/// Configures , which iterates the items of a +/// run's default dataset while the run is still producing them. As the reference client's +/// RunIterateDatasetItemsOptions. All fields are optional. /// public sealed class RunIterateDatasetItemsOptions { @@ -19,8 +19,8 @@ public sealed class RunIterateDatasetItemsOptions public int? Offset { get; init; } /// - /// Maximum number of dataset rows to scan (null or 0 for no limit). Fewer items are - /// yielded when filters drop some, more when splits a row into several. + /// Maximum dataset rows to scan (null or 0 for no limit). Filters can drop items and + /// can split a row into several. /// public int? Limit { get; init; } diff --git a/src/Apify.Client/Options/RunListOptions.cs b/src/Apify.Client/Options/RunListOptions.cs index 2243e20..748a06c 100644 --- a/src/Apify.Client/Options/RunListOptions.cs +++ b/src/Apify.Client/Options/RunListOptions.cs @@ -4,23 +4,19 @@ namespace Apify.Client.Options; /// -/// Run-specific filters for listing runs. The StartedAfter/StartedBefore filters are only -/// honoured by the account-wide and Actor-scoped run collections, not by task runs. +/// Run-specific filters for listing runs. StartedAfter and StartedBefore apply only to the account-wide and +/// Actor-scoped run collections, not to task runs. /// public sealed class RunListOptions { - /// - /// Filter by one or more run statuses (e.g. SUCCEEDED, RUNNING); sent as a - /// comma-separated list. - /// + /// Filters by one or more run statuses (SUCCEEDED, RUNNING), sent as a comma-separated list. public IReadOnlyList? Status { get; init; } /// - /// Filter Actor Standby runs by the pool that serves them: one or more of SINGLE_TENANT, - /// MULTI_TENANT_FREE_USERS, MULTI_TENANT_PAYING_USERS or MULTI_TENANT_OWNER_PAID; sent - /// as a comma-separated list. Requires with only non-terminal statuses (READY, - /// RUNNING, TIMING-OUT or ABORTING). Only the account-wide and Actor-scoped run - /// collections honour it. + /// Filters Server Actor runs by the pool that serves them: SINGLE_TENANT, MULTI_TENANT_FREE_USERS, + /// MULTI_TENANT_PAYING_USERS or MULTI_TENANT_OWNER_PAID, sent as a comma-separated list. Needs + /// with only non-terminal statuses (READY, RUNNING, TIMING-OUT or + /// ABORTING). Only the account-wide and Actor-scoped run collections honor it. /// public IReadOnlyList? Deployment { get; init; } diff --git a/src/Apify.Client/Options/SetRecordOptions.cs b/src/Apify.Client/Options/SetRecordOptions.cs index 41acf18..15bbcf2 100644 --- a/src/Apify.Client/Options/SetRecordOptions.cs +++ b/src/Apify.Client/Options/SetRecordOptions.cs @@ -1,26 +1,18 @@ namespace Apify.Client.Options; -/// -/// Write options for storing a key-value-store record, mirroring the reference client's -/// timeoutSecs/doNotRetryTimeouts. -/// +/// Write options of a key-value store record, as the reference client's timeoutSecs and doNotRetryTimeouts. public sealed class SetRecordOptions { - /// - /// Per-request timeout for the upload, in seconds. Use it to shorten the wait for this upload; defaults - /// to (and is capped at) the client's configured overall request timeout, so a value larger than that - /// timeout has no effect. - /// + /// Per-request timeout of the upload in seconds, capped at the client's overall request timeout. public int? TimeoutSecs { get; init; } /// If true, do not retry the upload when it fails with a request timeout. public bool DoNotRetryTimeouts { get; init; } /// - /// Encoding already applied to the record value, sent verbatim as the Content-Encoding header. - /// Set it to upload a pre-compressed value (gzip, br, deflate, or identity): - /// the client then sends the bytes as they are and skips its own compression, so they are never - /// compressed twice. Must not be empty or whitespace-only. + /// The encoding already applied to the value, sent as the Content-Encoding header, to upload a pre-compressed value + /// (gzip, br, zstd, deflate or identity). The client sends the bytes as they are and + /// skips its own compression. Must not be empty or whitespace-only. /// public string? ContentEncoding { get; init; } } diff --git a/src/Apify.Client/Options/StorageListOptions.cs b/src/Apify.Client/Options/StorageListOptions.cs index 259162e..efbef26 100644 --- a/src/Apify.Client/Options/StorageListOptions.cs +++ b/src/Apify.Client/Options/StorageListOptions.cs @@ -2,11 +2,7 @@ namespace Apify.Client.Options; -/// -/// Options for the storage collection list endpoints (GET /v2/datasets, -/// /v2/key-value-stores, /v2/request-queues), which add unnamed and ownership -/// filters on top of the standard pagination. -/// +/// Options for listing datasets, key-value stores and request queues: pagination plus unnamed and ownership filters. public sealed class StorageListOptions { /// Number of items to skip from the beginning of the list. @@ -16,9 +12,8 @@ public sealed class StorageListOptions public int? Limit { get; init; } /// - /// Maximum number of items requested per API call when iterating across pages (must be positive). Only - /// iteration uses it, as the page size; it is never sent to the API. The effective page size is the - /// smaller of this and the remaining Limit. Defaults to the API's own page size. + /// Maximum items requested per API call when iterating, which must be positive. It sizes the pages and is never sent to + /// the API; the page size is the smaller of this and the remaining Limit. Defaults to the API's page size. /// public int? ChunkSize { get; init; } diff --git a/src/Apify.Client/Options/StoreListOptions.cs b/src/Apify.Client/Options/StoreListOptions.cs index f957b1b..1dca04f 100644 --- a/src/Apify.Client/Options/StoreListOptions.cs +++ b/src/Apify.Client/Options/StoreListOptions.cs @@ -13,9 +13,8 @@ public sealed class StoreListOptions public int? Limit { get; init; } /// - /// Maximum number of items requested per API call when iterating across pages (must be positive). Only - /// iteration uses it, as the page size; it is never sent to the API. The effective page size is the - /// smaller of this and the remaining Limit. Defaults to the API's own page size. + /// Maximum items requested per API call when iterating, which must be positive. It sizes the pages and is never sent to + /// the API; the page size is the smaller of this and the remaining Limit. Defaults to the API's page size. /// public int? ChunkSize { get; init; } @@ -44,9 +43,9 @@ public sealed class StoreListOptions public bool? AllowsAgenticUsers { get; init; } /// - /// Include each Actor's inputSchema: a compact version of the input schema of its default build - /// (property types and required properties only). With it, the API's limit defaults to and cannot - /// exceed , so iteration requests pages of at most that size. + /// Include each Actor's inputSchema, a compact form of the input schema of its default build (property types and + /// required properties). The API then caps limit at , and iteration + /// requests pages of at most that size. /// public bool? IncludeInputSchema { get; init; } diff --git a/src/Apify.Client/Options/TaskStartOptions.cs b/src/Apify.Client/Options/TaskStartOptions.cs index f11ee5b..a78426c 100644 --- a/src/Apify.Client/Options/TaskStartOptions.cs +++ b/src/Apify.Client/Options/TaskStartOptions.cs @@ -2,12 +2,10 @@ namespace Apify.Client.Options; -/// -/// Configures starting a task run. -/// +/// Configures starting a task run. /// -/// It mirrors but omits the fields the task run endpoint does not accept -/// (the Actor-only ContentType and ForcePermissionLevel), matching the reference client. +/// Like without the fields the task run endpoint does not accept (ContentType and +/// ForcePermissionLevel), as in the reference client. /// public sealed class TaskStartOptions { @@ -33,9 +31,9 @@ public sealed class TaskStartOptions public bool? RestartOnError { get; init; } /// - /// Retries the start while the account lacks the memory or a concurrent-run slot for the run (an - /// actor-memory-limit-exceeded/concurrent-runs-limit-exceeded error), instead of throwing - /// immediately. null (the default) disables retrying. + /// Retries the start while the account lacks the memory or a concurrent-run slot + /// (actor-memory-limit-exceeded, concurrent-runs-limit-exceeded) instead of throwing. null (the + /// default) does not retry. /// public WaitForResources? WaitForResources { get; init; } diff --git a/src/Apify.Client/Options/WaitForResources.cs b/src/Apify.Client/Options/WaitForResources.cs index be3b33a..175a9b4 100644 --- a/src/Apify.Client/Options/WaitForResources.cs +++ b/src/Apify.Client/Options/WaitForResources.cs @@ -3,12 +3,11 @@ namespace Apify.Client.Options; /// -/// Configures retrying a run start while the account has no free memory or concurrent-run slot for it (the -/// API's actor-memory-limit-exceeded/concurrent-runs-limit-exceeded errors, which clear as -/// other runs or builds finish). Passed to and -/// ; matches the reference client's waitForResources -/// start option (there a boolean | number union, modelled here as this small value type so the -/// "retry forever" and "retry for N seconds" cases stay distinct and self-documenting). +/// Configures retrying a run start while the account has no free memory or concurrent-run slot +/// (actor-memory-limit-exceeded, concurrent-runs-limit-exceeded), which clears as other runs finish. Passed +/// to and . The reference +/// client's waitForResources is a boolean | number; this value type keeps "retry forever" and "retry for N +/// seconds" distinct. /// public readonly struct WaitForResources : IEquatable { @@ -17,22 +16,16 @@ private WaitForResources(double? maxWaitSecs) MaxWaitSecs = maxWaitSecs; } - /// - /// The maximum time to keep retrying, in seconds, or null when retrying until the run starts - /// (see ). - /// + /// The longest time to keep retrying in seconds, or null to retry until the run starts (see ). public double? MaxWaitSecs { get; } /// Retries indefinitely until the run starts. public static WaitForResources UntilAvailable { get; } = new(null); - /// - /// Retries for up to seconds, after which the last - /// actor-memory-limit-exceeded/concurrent-runs-limit-exceeded error is thrown. - /// + /// Retries for up to seconds, then throws the last resource-limit error. /// - /// The retry budget in seconds; must be finite and not negative (use , - /// not , to retry forever). + /// The retry budget in seconds; finite and not negative. Use , not + /// , to retry forever. /// public static WaitForResources For(double maxWaitSecs) { diff --git a/src/Apify.Client/RequestCompression.cs b/src/Apify.Client/RequestCompression.cs index 880626c..90ec6f8 100644 --- a/src/Apify.Client/RequestCompression.cs +++ b/src/Apify.Client/RequestCompression.cs @@ -1,18 +1,18 @@ namespace Apify.Client; -/// -/// Algorithm used to compress large request bodies before sending them. -/// +/// Algorithm used to compress large request bodies. /// -/// Mirrors the reference client's explicit compression: 'brotli' | 'gzip' option, which defaults to -/// brotli. Regardless of the choice, only string/byte payloads at or above the compression threshold are -/// compressed. +/// Mirrors the reference client's compression option, which defaults to brotli. Only string or byte payloads of +/// at least 1024 bytes are compressed. /// public enum RequestCompression { - /// Compress with brotli (Content-Encoding: br). This is the default and matches the reference client's preference. + /// Compress with brotli (Content-Encoding: br). This is the default. Brotli, /// Compress with gzip (Content-Encoding: gzip). Gzip, + + /// Compress with zstd (Content-Encoding: zstd). + Zstd, } diff --git a/src/Apify.Client/Resources/AbstractWebhookCollectionClient.cs b/src/Apify.Client/Resources/AbstractWebhookCollectionClient.cs index 91470ca..199f87e 100644 --- a/src/Apify.Client/Resources/AbstractWebhookCollectionClient.cs +++ b/src/Apify.Client/Resources/AbstractWebhookCollectionClient.cs @@ -8,10 +8,9 @@ namespace Apify.Client.Resources; /// -/// Shared read-only behavior for webhook collections. Both the account-wide collection -/// () and the read-only collections nested under an Actor or task -/// () can list webhooks; only the account-wide collection can -/// create them. +/// Read-only behavior shared by the webhook collections. The account-wide and the +/// collections nested under an Actor or task () can list webhooks; only the +/// account-wide one can create them. /// public abstract class AbstractWebhookCollectionClient { diff --git a/src/Apify.Client/Resources/ActorClient.cs b/src/Apify.Client/Resources/ActorClient.cs index 90023b9..0d1fa8f 100644 --- a/src/Apify.Client/Resources/ActorClient.cs +++ b/src/Apify.Client/Resources/ActorClient.cs @@ -9,12 +9,9 @@ namespace Apify.Client.Resources; /// -/// A client for a specific Actor. +/// A client for a specific Actor: get, update, delete, start, call, build, and access to its runs, builds, versions, +/// webhooks and server interface. /// -/// -/// It provides CRUD methods plus convenience helpers to start/call the Actor, build it, and access its -/// runs, builds, versions and webhooks. -/// public sealed class ActorClient { private readonly ApifyClient _root; @@ -43,7 +40,7 @@ internal ActorClient(ApifyClient root, HttpClientCore http, string baseUrl, stri return data is JsonObject obj ? new Actor(obj) : null; } - /// Updates the Actor with the given fields and returns the updated object. + /// Updates the Actor and returns the updated Actor. /// Any JSON-serializable set of fields to update. /// A token to cancel the request. public async Task UpdateAsync(object newFields, CancellationToken cancellationToken = default) @@ -55,10 +52,10 @@ public async Task UpdateAsync(object newFields, CancellationToken cancell /// A token to cancel the request. public Task DeleteAsync(CancellationToken cancellationToken = default) => _ctx.DeleteResourceAsync("", RequestTier.Short, cancellationToken); - /// Starts the Actor and returns immediately with the created run. + /// Starts the Actor and returns the created run without waiting. /// - /// A plain object or array serialized to JSON, raw bytes (byte[]) sent as-is (typically paired - /// with ), or null for no input. + /// byte[] is sent as it is (typically with ), anything else as JSON, + /// and null sends no input. /// /// Optional run-start options. /// A token to cancel the request. @@ -76,25 +73,21 @@ private async Task StartOnceAsync(object? input, ActorStartOptions opt var q = new QueryParams(); options.AppendTo(q); ResourceContext.EncodeInputBody(input, out var body, out var bodyBytes); - // Long, not the reference's base "medium": options.WaitForFinish can ask the server to hold the - // connection open for up to 60s, and this call does not clamp it to a shorter budget the way - // RunClient.GetAsync/BuildClient.GetAsync do, so the HTTP timeout must cover the full wait. + // Long: options.WaitForFinish can make the server hold the connection for up to 60s, and it is not clamped here. return new ActorRun(await _ctx.PostWithBodyAsync("runs", q, body, options.ContentTypeOrDefault(), RequestTier.Long, cancellationToken, bodyBytes).ConfigureAwait(false)); } - /// Starts the Actor and waits (client-side polling) for it to finish. - /// Any JSON-serializable value (or null for no input). + /// Starts the Actor and waits for it to finish by polling. + /// Any JSON-serializable value, or null for no input. /// - /// Optional run-start options. Time spent retrying - /// does not count toward , which only bounds the wait once the run starts. + /// Optional run-start options. Time spent retrying does not count + /// toward . /// /// Bounds the wait; null waits indefinitely. /// - /// If provided, the run's live log is redirected to this sink (one complete message per call) for the - /// duration of the wait, matching the reference client's log call option. Unlike the reference - /// client, which redirects the log by default when the option is omitted, null (the default) does - /// not redirect anything; pass to get the reference's default - /// redirection, with its <Actor name> runId:<id> -> prefix. + /// Redirects the run's live log to this sink, one complete message per call, while waiting. Unlike the reference + /// client, null redirects nothing; pass for the reference's default + /// redirection with the <Actor name> runId:<id> -> prefix. /// /// A token to cancel the request. public async Task CallAsync( @@ -108,10 +101,8 @@ public async Task CallAsync( return await _root.Run(run.Id ?? string.Empty).WaitForFinishWithLogAsync(waitSecs, log, cancellationToken).ConfigureAwait(false); } - /// Validates against the Actor's input schema and returns whether it is valid. - /// - /// A plain object or array serialized to JSON, raw bytes (byte[]) sent as-is, or null. - /// + /// Checks against the Actor's input schema and returns whether it is valid. + /// byte[] is sent as it is, anything else as JSON, null sends no input. /// Optional validation options. /// A token to cancel the request. public async Task ValidateInputAsync(object? input = null, ValidateInputOptions? options = null, CancellationToken cancellationToken = default) @@ -120,8 +111,7 @@ public async Task ValidateInputAsync(object? input = null, ValidateInputOp var q = new QueryParams(); options.AppendTo(q); ResourceContext.EncodeInputBody(input, out var body, out var bodyBytes); - // The validate-input endpoint returns a bare {"valid": } object, not the standard - // {"data": ...} envelope, so parse it without unwrapping. + // The endpoint returns a bare {"valid": bool} without the usual "data" envelope. var result = await _ctx.PostWithBodyNoEnvelopeAsync("validate-input", q, body, options.ContentTypeOrDefault(), RequestTier.Short, cancellationToken, bodyBytes).ConfigureAwait(false); return result is JsonObject obj && obj.TryGetPropertyValue("valid", out var valid) && valid?.GetValueKind() == System.Text.Json.JsonValueKind.True; @@ -140,8 +130,8 @@ public async Task BuildAsync(string versionNumber, ActorBuildOptions? opt } /// - /// Resolves the Actor's default build and returns a client for it. - /// optionally bounds how long (seconds) the API waits for the build to finish before responding. + /// Returns a client for the Actor's default build. bounds how long (seconds) the API + /// waits for the build to finish. /// /// Optional server-side wait in seconds. /// A token to cancel the request. @@ -149,13 +139,13 @@ public async Task DefaultBuildAsync(int? waitForFinish = null, Canc { var q = new QueryParams(); q.AddInt("waitForFinish", waitForFinish); - // Long: waitForFinish is sent unclamped, so the HTTP timeout must cover whatever the caller asks for. + // Long: waitForFinish is not clamped. var data = await _ctx.GetResourceRequiredAsync("builds/default", q, RequestTier.Long, cancellationToken).ConfigureAwait(false); var build = new Build(data); return new BuildClient(_http, _baseUrl, build.Id ?? string.Empty); } - /// Returns a client for the last run of this Actor, optionally filtered by status and/or origin. + /// Returns a client for the last run of this Actor, optionally filtered by status and origin. /// Optional last-run filters. public RunClient LastRun(LastRunOptions? options = null) { @@ -171,7 +161,7 @@ public RunClient LastRun(LastRunOptions? options = null) public RunCollectionClient Runs() => new(_http, _ctx.SubUrl(""), "runs"); /// A client for a specific version of this Actor. - /// The version identifier (e.g. 0.1). Must not be empty. + /// The version, such as 0.1. Must not be empty. public ActorVersionClient Version(string versionNumber) { if (versionNumber.Length == 0) @@ -186,22 +176,19 @@ public ActorVersionClient Version(string versionNumber) public ActorVersionCollectionClient Versions() => new(_http, _ctx.SubUrl("")); /// - /// A client for the server interface of this Actor. A Server Actor (formerly Actor Standby) runs a web server - /// that is called over HTTP without starting a run for each call. Every call returns the same client, so the - /// server URL it fetches is reused. + /// A client for the server interface of this Actor. A Server Actor (formerly Actor Standby) runs a web server that is + /// called over HTTP without starting a run per call. Every call returns the same client, which reuses the server URL. /// public ActorServerClient Server() => LazyInitializer.EnsureInitialized(ref _serverClient, () => new ActorServerClient(_http, _baseUrl, Id)); - /// A read-only client for this Actor's webhook collection (GET /v2/actors/{id}/webhooks). + /// A read-only client for this Actor's webhooks. public NestedWebhookCollectionClient Webhooks() => new(_http, _ctx.SubUrl("")); /// - /// Returns this client with every subsequent call's timeout set to , - /// overriding the tier default (see the "Timeout tiers" section of docs/README.md). Pass - /// for no timeout, matching the reference client's 'noTimeout'. A - /// value above (the overall budget) is capped at it — - /// raise itself to allow a longer per-call timeout. + /// Returns this client with the timeout of every subsequent call set to , overriding the + /// tier default (see "Timeout tiers" in docs/README.md). means no timeout. A value above + /// is capped at it. /// /// The timeout to use for every call made through this client. public ActorClient WithTimeout(TimeSpan timeout) diff --git a/src/Apify.Client/Resources/ActorCollectionClient.cs b/src/Apify.Client/Resources/ActorCollectionClient.cs index 1663c7a..0228e46 100644 --- a/src/Apify.Client/Resources/ActorCollectionClient.cs +++ b/src/Apify.Client/Resources/ActorCollectionClient.cs @@ -51,11 +51,9 @@ public async Task CreateAsync(object actor, CancellationToken cancellatio } /// - /// Returns this client with every subsequent call's timeout set to , - /// overriding the tier default (see the "Timeout tiers" section of docs/README.md). Pass - /// for no timeout, matching the reference client's 'noTimeout'. A - /// value above (the overall budget) is capped at it — - /// raise itself to allow a longer per-call timeout. + /// Returns this client with the timeout of every subsequent call set to , overriding the + /// tier default (see "Timeout tiers" in docs/README.md). means no timeout. A value above + /// is capped at it. /// /// The timeout to use for every call made through this client. public ActorCollectionClient WithTimeout(TimeSpan timeout) diff --git a/src/Apify.Client/Resources/ActorEnvVarClient.cs b/src/Apify.Client/Resources/ActorEnvVarClient.cs index b18e4a6..c6b783d 100644 --- a/src/Apify.Client/Resources/ActorEnvVarClient.cs +++ b/src/Apify.Client/Resources/ActorEnvVarClient.cs @@ -42,11 +42,9 @@ public async Task UpdateAsync(ActorEnvVar envVar, CancellationToken public Task DeleteAsync(CancellationToken cancellationToken = default) => _ctx.DeleteResourceAsync("", RequestTier.Short, cancellationToken); /// - /// Returns this client with every subsequent call's timeout set to , - /// overriding the tier default (see the "Timeout tiers" section of docs/README.md). Pass - /// for no timeout, matching the reference client's 'noTimeout'. A - /// value above (the overall budget) is capped at it — - /// raise itself to allow a longer per-call timeout. + /// Returns this client with the timeout of every subsequent call set to , overriding the + /// tier default (see "Timeout tiers" in docs/README.md). means no timeout. A value above + /// is capped at it. /// /// The timeout to use for every call made through this client. public ActorEnvVarClient WithTimeout(TimeSpan timeout) diff --git a/src/Apify.Client/Resources/ActorEnvVarCollectionClient.cs b/src/Apify.Client/Resources/ActorEnvVarCollectionClient.cs index 53378ca..fdf0167 100644 --- a/src/Apify.Client/Resources/ActorEnvVarCollectionClient.cs +++ b/src/Apify.Client/Resources/ActorEnvVarCollectionClient.cs @@ -38,11 +38,9 @@ public async Task CreateAsync(ActorEnvVar envVar, CancellationToken } /// - /// Lazily iterates over the version's environment variables. The env-var endpoint returns the whole - /// list in a single page, so this yields that page's items; it exists for API parity with the other - /// collection iterators and the reference client. + /// Iterates the version's environment variables. The endpoint returns the whole list in one page, so this yields that + /// page's items; it exists for parity with the other collection iterators and the reference client. /// - /// A token to cancel the iteration. public async IAsyncEnumerable IterateAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { var page = await ListAsync(cancellationToken).ConfigureAwait(false); @@ -54,11 +52,9 @@ public async IAsyncEnumerable IterateAsync([EnumeratorCancellation] /// - /// Returns this client with every subsequent call's timeout set to , - /// overriding the tier default (see the "Timeout tiers" section of docs/README.md). Pass - /// for no timeout, matching the reference client's 'noTimeout'. A - /// value above (the overall budget) is capped at it — - /// raise itself to allow a longer per-call timeout. + /// Returns this client with the timeout of every subsequent call set to , overriding the + /// tier default (see "Timeout tiers" in docs/README.md). means no timeout. A value above + /// is capped at it. /// /// The timeout to use for every call made through this client. public ActorEnvVarCollectionClient WithTimeout(TimeSpan timeout) diff --git a/src/Apify.Client/Resources/ActorServerClient.cs b/src/Apify.Client/Resources/ActorServerClient.cs index 2c0dd1c..6b21627 100644 --- a/src/Apify.Client/Resources/ActorServerClient.cs +++ b/src/Apify.Client/Resources/ActorServerClient.cs @@ -19,16 +19,19 @@ namespace Apify.Client.Resources; /// A client for the server interface of a specific Actor, obtained with . /// /// -/// A Server Actor (formerly Actor Standby) runs a web server that is called over HTTP without starting a run for -/// each call. This client sends requests to that server, and returns its URL and its OpenAPI specification. See -/// . +/// A Server Actor (formerly Actor Standby) runs a web server that is called over HTTP without starting a run for each +/// call. This client sends requests to that server and returns its URL and OpenAPI specification. See +/// . /// public sealed class ActorServerClient { - /// The header that carries the API token to an Actor's server. The platform removes it before the request reaches the Actor. + /// The header that carries the API token to an Actor's server; the platform removes it before the request reaches the Actor. private const string ServerAuthorizationHeader = "X-Apify-Authorization"; - /// The build tag whose build serves the server interface when actorStandby.build is not set. + /// The header that identifies a request to a multi-tenant Server Actor. + private const string ParentRequestIdHeader = "X-Actor-Request-ID"; + + /// The build tag that serves the server interface when actorStandby.build is not set. private const string DefaultBuildTag = "latest"; private const string DefaultRequestPath = "/"; @@ -36,25 +39,21 @@ public sealed class ActorServerClient private const string TextContentType = "text/plain; charset=utf-8"; private const string BinaryContentType = "application/octet-stream"; - /// The format of a date in a query parameter: ISO 8601 in UTC with milliseconds, as the reference client sends it. + /// The format of a date in a query parameter: ISO 8601 in UTC with milliseconds, as in the reference client. private const string QueryTimestampFormat = "yyyy-MM-dd'T'HH:mm:ss.fff'Z'"; /// The separator of the values of a list in a query parameter. private const string QueryListSeparator = ","; - /// The Service Unavailable status the platform answers with when it cannot pass a request to a server run. + /// The status the platform answers with when it cannot pass a request to a server run. private const int ServiceUnavailableStatus = 503; - /// - /// Bounds the build number matching so that a pathological input cannot stall the call; the pattern is simple, - /// so a second is generous. - /// + /// Bounds the build number matching so that a pathological input cannot stall the call. private static readonly TimeSpan BuildNumberMatchTimeout = TimeSpan.FromSeconds(1); /// - /// Statuses a server request is retried on. The platform answers with them when it cannot pass a request to a - /// server run, so a retry usually does not run the Actor's code again. An Actor that returns one itself gets - /// the request again. + /// Statuses a server request is retried on. The platform answers with them when it cannot pass a request to a server + /// run, so a retry usually does not run the Actor's code again. An Actor that returns one itself gets the request again. /// private static readonly int[] ServerRetryStatusCodes = { HttpClientCore.RateLimitExceeded, ServiceUnavailableStatus }; @@ -78,13 +77,12 @@ internal ActorServerClient(HttpClientCore http, string baseUrl, string id) public string Id { get; } /// - /// Returns the URL of the Actor's server, to call it with another HTTP client, for example one generated from - /// the server's OpenAPI specification (see ). + /// Returns the URL of the Actor's server, to call it with another HTTP client, for example one generated from the + /// server's OpenAPI specification (see ). /// /// /// The URL is the Actor's standbyUrl. The first call of this method, or - /// fetches it from the API and this client reuses it afterwards; concurrent first - /// calls may each fetch it. + /// fetches it and the client reuses it afterwards. /// /// A token to cancel the request. /// The Actor is not a Server Actor. @@ -93,14 +91,13 @@ public async Task GetUrlAsync(CancellationToken cancellationToken = defa => _serverUrl ?? (await GetServerActorAsync(cancellationToken).ConfigureAwait(false)).StandbyUrl!; /// - /// Returns the OpenAPI specification of the Actor's server, to generate an HTTP client for it and point that - /// client at the URL returned by ; null if the Actor's definition does not - /// include one. + /// Returns the OpenAPI specification of the Actor's server, to generate an HTTP client for it; null if the + /// Actor's definition has none. /// /// - /// The specification is the web server schema of the Actor's definition, taken from the build that serves the - /// server interface: the build set in actorStandby.build (a tag or a build number), or latest if - /// none is set. See . + /// The specification is the web server schema of the build that serves the server interface: actorStandby.build + /// (a tag or a build number), or latest. See + /// . /// /// A token to cancel the requests. /// The Actor is not a Server Actor, or the build that serves its server interface does not exist. @@ -114,7 +111,7 @@ public async Task GetUrlAsync(CancellationToken cancellationToken = defa var buildId = TaggedBuildId(actor, buildRef); if (buildId is null && ParseBuildNumber(buildRef) is { } target) { - // Not a tag, so look `buildRef` up as a build number, which the API cannot resolve directly. + // Not a tag, so look it up as a build number, which the API cannot resolve directly. buildId = await FindBuildIdByNumberAsync(buildRef, target, cancellationToken).ConfigureAwait(false); } @@ -127,31 +124,26 @@ public async Task GetUrlAsync(CancellationToken cancellationToken = defa return build.ActorDefinition?["webServerSchema"] as JsonObject; } - /// - /// Sends an HTTP request to the Actor's server and returns its response, whatever its status. - /// + /// Sends an HTTP request to the Actor's server and returns its response, whatever its status. /// /// - /// The request goes to the URL returned by and carries the client's token in the - /// X-Apify-Authorization header, which the platform consumes, so it never reaches the Actor's code. - /// Headers in , Authorization included, reach the Actor's - /// server as they are. The first request to an idle Actor waits for the platform to start a server run, which - /// is why the timeout defaults to the Long tier. + /// The request goes to the URL from with the client's token in the + /// X-Apify-Authorization header, which the platform consumes, so the Actor's code never sees it. Headers in + /// , Authorization included, reach the Actor as they are. The + /// first request to an idle Actor waits for a server run to start, so the timeout defaults to the Long tier. /// /// - /// A rate limit (429) or Service Unavailable (503) response is retried with the client's retry settings, and - /// the last one is returned once the retries run out. The Actor's code can return these statuses too, and - /// then it gets the request again. Other statuses are not retried, since they may come from the Actor's code, - /// and neither are network errors and timeouts, since the Actor may have already handled the request. A - /// redirect is returned as it is, since following it would send the token to a host the Actor's code picked. - /// Request bodies are sent uncompressed unless is set. + /// A 429 or 503 response is retried with the client's retry settings, and the last one is returned when the retries + /// run out. Other statuses are not retried, since they may come from the Actor's code, and neither are network errors + /// and timeouts, since the Actor may have handled the request. A redirect is returned as it is, since following it + /// would send the token to a host the Actor's code picked. Bodies are sent uncompressed unless + /// is set. /// /// /// - /// The path on the Actor's server, for example /search. It is always resolved against the server URL, - /// so the request, and the token with it, cannot be sent to another host. Pass the query in - /// . A fragment (#...) is dropped, since it is never sent - /// to the server. + /// The path on the Actor's server, for example /search. It is resolved against the server URL, so the request + /// and the token cannot go to another host. Pass the query in . A fragment + /// (#...) is dropped. /// /// Optional request settings. /// A token to cancel the request. @@ -202,7 +194,7 @@ public async Task RequestAsync( options.ContentType ?? FindHeader(options.Headers, HttpClientCore.ContentTypeHeader) ?? DefaultContentType(options.Body), options.Timeout, bodyBytes: bodyBytes, - extraHeaders: options.Headers, + extraHeaders: WithParentRequestId(options.Headers, options.ParentRequestId), tier: RequestTier.Long, explicitContentEncoding: FindHeader(options.Headers, HttpClientCore.ContentEncodingHeader), policy: policy, @@ -211,12 +203,10 @@ public async Task RequestAsync( } /// - /// Returns this client with the timeout of the API calls it makes (the Actor and build lookups of - /// and ) set to , - /// overriding the tier default (see the "Timeout tiers" section of docs/README.md). Pass - /// for no timeout. A value above - /// is capped at it. It does not affect , which takes its timeout from - /// . + /// Returns this client with the timeout of its API calls (the Actor and build lookups) set to + /// , overriding the tier default (see "Timeout tiers" in docs/README.md). + /// means no timeout. A value above is capped at + /// it. uses instead. /// /// The timeout to use for the API calls made through this client. public ActorServerClient WithTimeout(TimeSpan timeout) @@ -250,8 +240,8 @@ private static void AddQueryValue(QueryParams query, string key, object? value) } /// - /// Converts a query value to text: dates to ISO 8601 in UTC, booleans to 1/0, lists to their - /// comma-joined values and other values to their invariant text; null stays null and is left out. + /// Converts a query value to text: dates to ISO 8601 in UTC, booleans to 1/0, lists to comma-joined + /// values and others to their invariant text. null is left out. /// private static string? FormatQueryValue(object? value) => value switch { @@ -265,7 +255,7 @@ private static void AddQueryValue(QueryParams query, string key, object? value) _ => value.ToString(), }; - /// A date as UTC; a date of unspecified kind is taken as UTC too, so it is sent the same on every machine. + /// A date as UTC; a date of unspecified kind is taken as UTC, so it is sent the same on every machine. private static DateTime ToUtc(DateTime time) => time.Kind == DateTimeKind.Unspecified ? DateTime.SpecifyKind(time, DateTimeKind.Utc) : time.ToUniversalTime(); @@ -276,6 +266,26 @@ private static DateTime ToUtc(DateTime time) _ => ResourceContext.ContentTypeJson, }; + private static IReadOnlyDictionary? WithParentRequestId(IReadOnlyDictionary? headers, string? parentRequestId) + { + if (parentRequestId is null) + { + return headers; + } + + var merged = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (headers is not null) + { + foreach (var header in headers) + { + merged[header.Key] = header.Value; + } + } + + merged[ParentRequestIdHeader] = parentRequestId; + return merged; + } + private static string? FindHeader(IReadOnlyDictionary? headers, string name) { if (headers is null) @@ -294,7 +304,7 @@ private static DateTime ToUtc(DateTime time) return null; } - /// Fetches the Actor, checks that it is a Server Actor, and remembers its server URL. + /// Fetches the Actor, checks that it is a Server Actor and remembers its server URL. private async Task GetServerActorAsync(CancellationToken cancellationToken) { var actor = new Actor(await _ctx.GetResourceRequiredAsync("", new QueryParams(), RequestTier.Short, cancellationToken).ConfigureAwait(false)); @@ -320,8 +330,7 @@ private async Task GetServerActorAsync(CancellationToken cancellationToke builds.WithTimeout(timeout); } - // Builds are listed newest first and the sequence numbers of a version grow over time, so meeting an older - // build of the same version means the target does not exist. + // Builds are listed newest first and sequence numbers grow, so an older build of the same version means the target does not exist. await foreach (var build in builds.IterateAsync(new ListOptions { Desc = true }, cancellationToken).ConfigureAwait(false)) { if (build.BuildNumber == buildNumber) @@ -350,10 +359,7 @@ private BuildClient BuildClientFor(string buildId) return client; } - /// - /// Splits a build number like 0.1.23 into its parts, or returns null for anything else, including - /// numbers too large to be a real build number. - /// + /// Splits a build number such as 0.1.23 into its parts; null for anything else. private static (long Major, long Minor, long Sequence)? ParseBuildNumber(string? buildNumber) { var match = BuildNumberPattern.Match(buildNumber ?? string.Empty); diff --git a/src/Apify.Client/Resources/ActorServerResponse.cs b/src/Apify.Client/Resources/ActorServerResponse.cs index 2db329c..64883c0 100644 --- a/src/Apify.Client/Resources/ActorServerResponse.cs +++ b/src/Apify.Client/Resources/ActorServerResponse.cs @@ -49,11 +49,9 @@ public Task ReadAsStringAsync(CancellationToken cancellationToken = defa => _response.Content.ReadAsStringAsync(cancellationToken); /// - /// Reads the response body as a stream. With - /// the body is not read yet and arrives as it is received; the stream is valid until this response is - /// disposed. + /// Reads the response body as a stream. With the body + /// arrives as it is received. The stream is valid until this response is disposed. /// - /// A token to cancel the read. public Task ReadAsStreamAsync(CancellationToken cancellationToken = default) => _response.Content.ReadAsStreamAsync(cancellationToken); diff --git a/src/Apify.Client/Resources/ActorVersionClient.cs b/src/Apify.Client/Resources/ActorVersionClient.cs index 2d7f8f0..e74ff1c 100644 --- a/src/Apify.Client/Resources/ActorVersionClient.cs +++ b/src/Apify.Client/Resources/ActorVersionClient.cs @@ -60,11 +60,9 @@ public ActorEnvVarClient EnvVar(string name) public ActorEnvVarCollectionClient EnvVars() => new(_http, _versionUrl); /// - /// Returns this client with every subsequent call's timeout set to , - /// overriding the tier default (see the "Timeout tiers" section of docs/README.md). Pass - /// for no timeout, matching the reference client's 'noTimeout'. A - /// value above (the overall budget) is capped at it — - /// raise itself to allow a longer per-call timeout. + /// Returns this client with the timeout of every subsequent call set to , overriding the + /// tier default (see "Timeout tiers" in docs/README.md). means no timeout. A value above + /// is capped at it. /// /// The timeout to use for every call made through this client. public ActorVersionClient WithTimeout(TimeSpan timeout) diff --git a/src/Apify.Client/Resources/ActorVersionCollectionClient.cs b/src/Apify.Client/Resources/ActorVersionCollectionClient.cs index 6f13162..38be500 100644 --- a/src/Apify.Client/Resources/ActorVersionCollectionClient.cs +++ b/src/Apify.Client/Resources/ActorVersionCollectionClient.cs @@ -18,11 +18,7 @@ internal ActorVersionCollectionClient(HttpClientCore http, string actorUrl) _ctx = ResourceContext.Collection(http, actorUrl, "versions"); } - /// - /// Lists the Actor's versions. The endpoint returns every version in one response; it does not support - /// pagination, so unlike most collection clients this takes no filtering/paging options. - /// - /// A token to cancel the request. + /// Lists the Actor's versions. The endpoint returns all of them in one response, so this takes no paging options. public Task> ListAsync(CancellationToken cancellationToken = default) { return _ctx.ListResourceAsync("", new QueryParams(), static d => new ActorVersion(d), RequestTier.Short, cancellationToken); @@ -37,11 +33,9 @@ public async Task CreateAsync(object version, CancellationToken ca } /// - /// Iterates over the Actor's versions. The endpoint returns the whole list in a single page, so this - /// yields that page's items; it exists for API parity with the other collection iterators and the - /// reference client. + /// Iterates the Actor's versions. The endpoint returns the whole list in one page, so this yields that page's items; it + /// exists for parity with the other collection iterators and the reference client. /// - /// A token to cancel the iteration. public async IAsyncEnumerable IterateAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { var page = await ListAsync(cancellationToken).ConfigureAwait(false); @@ -52,11 +46,9 @@ public async IAsyncEnumerable IterateAsync([EnumeratorCancellation } /// - /// Returns this client with every subsequent call's timeout set to , - /// overriding the tier default (see the "Timeout tiers" section of docs/README.md). Pass - /// for no timeout, matching the reference client's 'noTimeout'. A - /// value above (the overall budget) is capped at it — - /// raise itself to allow a longer per-call timeout. + /// Returns this client with the timeout of every subsequent call set to , overriding the + /// tier default (see "Timeout tiers" in docs/README.md). means no timeout. A value above + /// is capped at it. /// /// The timeout to use for every call made through this client. public ActorVersionCollectionClient WithTimeout(TimeSpan timeout) diff --git a/src/Apify.Client/Resources/BuildClient.cs b/src/Apify.Client/Resources/BuildClient.cs index 33eacf7..d67b29e 100644 --- a/src/Apify.Client/Resources/BuildClient.cs +++ b/src/Apify.Client/Resources/BuildClient.cs @@ -20,18 +20,17 @@ internal BuildClient(HttpClientCore http, string baseUrl, string id) } /// - /// Fetches the build, optionally asking the API to wait up to - /// seconds (max 60) for the build to finish before responding. Returns null if it does not exist. + /// Fetches the build, optionally asking the API to wait up to seconds (at most + /// 60) for it to finish. Returns null if it does not exist. /// /// Optional server-side wait in seconds. /// A token to cancel the request. public async Task GetAsync(int? waitForFinishSecs = null, CancellationToken cancellationToken = default) { var q = new QueryParams(); - // Clamp to the client's per-request timeout so a short custom timeout doesn't abort the call. + // Clamped to the request timeout, so a short custom timeout does not abort the call. q.AddInt("waitForFinish", _ctx.ClampServerWait(waitForFinishSecs)); - // Long: the clamped waitForFinish can hold the connection open close to the overall budget, so the - // HTTP timeout for this call must cover it (matches the reference's short-base-extended-for-wait). + // Long tier: the server may hold the connection for most of the wait. var data = await _ctx.GetResourceAsync("", q, RequestTier.Long, cancellationToken).ConfigureAwait(false); return data is JsonObject obj ? new Build(obj) : null; } @@ -48,8 +47,8 @@ public async Task AbortAsync(CancellationToken cancellationToken = defaul public Task DeleteAsync(CancellationToken cancellationToken = default) => _ctx.DeleteResourceAsync("", RequestTier.Short, cancellationToken); /// - /// Polls until the build reaches a terminal state or elapses (null - /// waits indefinitely). Returns the latest build. + /// Polls until the build reaches a terminal state or elapses (null waits + /// indefinitely) and returns the latest build. /// /// The wait budget in seconds, or null to wait indefinitely. /// A token to cancel the wait. @@ -70,11 +69,9 @@ public async Task WaitForFinishAsync(int? waitSecs = null, CancellationTo public LogClient Log() => LogClient.Nested(_http, _ctx.SubUrl(""), timeout: _ctx.RequestTimeout); /// - /// Returns this client with every subsequent call's timeout set to , - /// overriding the tier default (see the "Timeout tiers" section of docs/README.md). Pass - /// for no timeout, matching the reference client's 'noTimeout'. A - /// value above (the overall budget) is capped at it — - /// raise itself to allow a longer per-call timeout. + /// Returns this client with the timeout of every subsequent call set to , overriding the + /// tier default (see "Timeout tiers" in docs/README.md). means no timeout. A value above + /// is capped at it. /// /// The timeout to use for every call made through this client. public BuildClient WithTimeout(TimeSpan timeout) diff --git a/src/Apify.Client/Resources/BuildCollectionClient.cs b/src/Apify.Client/Resources/BuildCollectionClient.cs index 14a9074..b4592c3 100644 --- a/src/Apify.Client/Resources/BuildCollectionClient.cs +++ b/src/Apify.Client/Resources/BuildCollectionClient.cs @@ -45,11 +45,9 @@ public IAsyncEnumerable IterateAsync(ListOptions? options = null, Cancell /// - /// Returns this client with every subsequent call's timeout set to , - /// overriding the tier default (see the "Timeout tiers" section of docs/README.md). Pass - /// for no timeout, matching the reference client's 'noTimeout'. A - /// value above (the overall budget) is capped at it — - /// raise itself to allow a longer per-call timeout. + /// Returns this client with the timeout of every subsequent call set to , overriding the + /// tier default (see "Timeout tiers" in docs/README.md). means no timeout. A value above + /// is capped at it. /// /// The timeout to use for every call made through this client. public BuildCollectionClient WithTimeout(TimeSpan timeout) diff --git a/src/Apify.Client/Resources/DatasetClient.cs b/src/Apify.Client/Resources/DatasetClient.cs index 57eab05..103f263 100644 --- a/src/Apify.Client/Resources/DatasetClient.cs +++ b/src/Apify.Client/Resources/DatasetClient.cs @@ -15,7 +15,7 @@ namespace Apify.Client.Resources; -/// A client for a specific dataset (and run-nested variants). +/// A client for a specific dataset. public sealed class DatasetClient { private readonly HttpClientCore _http; @@ -47,7 +47,7 @@ internal DatasetClient WithPublicBase(string publicBaseUrl) return data is JsonObject obj ? new Dataset(obj) : null; } - /// Updates the dataset metadata (e.g. name, title) and returns the updated object. + /// Updates the dataset metadata, such as its name or title, and returns the updated dataset. /// Any JSON-serializable set of fields to update. /// A token to cancel the request. public async Task UpdateAsync(object newFields, CancellationToken cancellationToken = default) @@ -59,15 +59,12 @@ public async Task UpdateAsync(object newFields, CancellationToken cance /// A token to cancel the request. public Task DeleteAsync(CancellationToken cancellationToken = default) => _ctx.DeleteResourceAsync("", RequestTier.Short, cancellationToken); - /// - /// Lists items from the dataset, each decoded to a (objects become - /// ). - /// + /// Lists items from the dataset, each decoded to a . /// - /// The dataset items endpoint returns a bare JSON array (not a data envelope) and reports pagination via - /// X-Apify-Pagination-* headers, surfaced in the returned page. + /// The endpoint returns a bare JSON array and reports pagination in X-Apify-Pagination-* headers, which end up + /// in the returned page. /// - /// Optional item filtering/projection and pagination. + /// Optional item filtering, projection and pagination. /// A token to cancel the request. public async Task> ListItemsAsync(DatasetListItemsOptions? options = null, CancellationToken cancellationToken = default) { @@ -77,23 +74,19 @@ public async Task UpdateAsync(object newFields, CancellationToken cance return await FetchItemsPageAsync(q, options.Desc ?? false, cancellationToken).ConfigureAwait(false); } - /// - /// Lazily iterates over all items of the dataset across pages, fetching each page on demand. Mirrors - /// the reference client's auto-paging listItems iterator. - /// + /// Lazily iterates all items of the dataset across pages, fetching each page on demand. /// - /// The offset for the next page, and whether iteration continues, follow the number of rows the API - /// scanned (X-Apify-Pagination-Count) rather than the number of items a page returned: the - /// // - /// filters apply after offset/limit, so a - /// page can scan up to the requested limit of rows while returning fewer of them, or none at all, and - /// can return more items than rows scanned. - /// therefore caps the rows scanned (0 or null - /// for no cap). The header can lag behind items that were just pushed, so the larger of it and the - /// returned item count is used, capped at the page's requested limit; see . + /// Pagination follows the rows the API scanned (X-Apify-Pagination-Count), not the items a page returned. The + /// , and + /// filters apply after offset and limit, so a page can + /// scan its full limit and return fewer items, and can return more items + /// than rows. therefore caps the rows scanned (0 or null for no + /// cap). The header lags behind fresh pushes, so the larger of it and the item count is used, capped at the page limit. /// - /// Optional item filtering/projection; Offset/Limit bound where - /// iteration starts and the rows scanned, and ChunkSize sets the page size. + /// + /// Optional filtering and projection. Offset and Limit bound where iteration starts and the rows scanned, + /// and ChunkSize sets the page size. + /// /// A token to cancel the iteration. public IAsyncEnumerable IterateItemsAsync( DatasetListItemsOptions? options = null, @@ -116,7 +109,7 @@ public async Task UpdateAsync(object newFields, CancellationToken cance cancellationToken); } - /// Fetches a single page of items for the given options (see the query overload). + /// Fetches one page of items. internal Task> FetchItemsPageAsync(DatasetListItemsOptions options, CancellationToken cancellationToken) { var q = new QueryParams(); @@ -125,11 +118,9 @@ public async Task UpdateAsync(object newFields, CancellationToken cance } /// - /// Fetches a single page of dataset items. The endpoint returns a bare JSON array with pagination in - /// X-Apify-Pagination-* response headers. The page's is the - /// number of rows the API scanned to produce it (X-Apify-Pagination-Count), which the offset - /// iterator paginates by (see ). The header is derived from the dataset's - /// item count, which lags a fresh push, so the item count is used when it is larger or the header is absent. + /// Fetches one page of items. The page's is the number of rows the API scanned + /// (X-Apify-Pagination-Count), or the item count when that is larger or the header is missing, since the header + /// lags fresh pushes. /// private async Task> FetchItemsPageAsync(QueryParams q, bool desc, CancellationToken cancellationToken) { @@ -152,11 +143,8 @@ public async Task UpdateAsync(object newFields, CancellationToken cance } /// - /// Downloads dataset items serialized in the given format, returning the raw bytes. Unlike - /// (parsed items), this returns the items already serialized to JSON, CSV, - /// XLSX, XML, RSS or HTML — useful for exporting. Bytes (not a decoded string) are returned so binary - /// formats such as (a ZIP-based export) are not corrupted; decode - /// text formats yourself, e.g. System.Text.Encoding.UTF8.GetString(bytes). + /// Downloads the items serialized in the given format (JSON, CSV, XLSX, XML, RSS or HTML) as raw bytes, which keeps + /// binary formats such as intact. Decode text formats yourself. /// /// The output format. /// Optional format-specific and filtering options. @@ -172,15 +160,11 @@ public async Task DownloadItemsAsync(DownloadItemsFormat format, Dataset } /// - /// Downloads dataset items serialized in the given format as a stream, so a large export is processed - /// incrementally without holding it all in memory. Takes the same arguments as - /// ; only how the response body is consumed differs. + /// Downloads the items serialized in the given format as a stream, so a large export need not fit in memory. Takes the + /// same arguments as . /// /// - /// Unlike , this bypasses the buffered/retrying transport so the body - /// can be read incrementally as it arrives (a single attempt, not retried, matching - /// ). The caller must dispose the returned stream; disposing it also - /// releases the underlying HTTP response/connection. + /// A single attempt, not retried, like . Dispose the stream to release the connection. /// /// The output format. /// Optional format-specific and filtering options. @@ -206,11 +190,9 @@ public async Task StreamItemsAsync(DownloadItemsFormat format, DatasetDo /// Pushes one or more items to the dataset. /// - /// The API only accepts a JSON object or an array of JSON objects, so any other shape (a bare number, a - /// plain string, an array holding anything but objects) is rejected before a request is sent. A - /// is treated as already-serialized JSON and sent as it is, so it has to hold such an - /// object or array. A push that times out is not retried, because the API may already have stored the - /// items and a retry would store them twice; the is thrown instead. + /// The API accepts only a JSON object or an array of objects, so any other shape is rejected before a request is sent. + /// A is sent as already serialized JSON. A push that times out is not retried, because the API + /// may have stored the items already; an is thrown instead. /// /// A single item, an array of items, or the JSON serialization of either. /// A token to cancel the request. @@ -233,8 +215,8 @@ public async Task PushItemsAsync(object items, CancellationToken cancellationTok } /// - /// Serializes the items to UTF-8 JSON once, requiring an object or an array of objects. The shape is - /// checked by reading the tokens of the serialized bytes, so no document tree is built. + /// Serializes the items to UTF-8 JSON once and checks that they are an object or an array of objects, by reading the + /// tokens instead of building a document tree. /// private static byte[] SerializeItems(object items) { @@ -278,30 +260,22 @@ private static bool IsObjectOrArrayOfObjects(ReadOnlySpan json) return true; } - /// Returns statistical information about the dataset. - /// - /// Throws if the dataset does not exist (no longer resolves to null): the statistics endpoint has - /// no meaning apart from its parent dataset, so a missing dataset is reported as an error rather than an - /// ambiguous empty result, matching the reference client. - /// + /// Returns statistics about the dataset. + /// Throws if the dataset does not exist, as in the reference client. /// A token to cancel the request. public async Task GetStatisticsAsync(CancellationToken cancellationToken = default) { return await _ctx.GetDataObjectRequiredAsync("statistics", new QueryParams(), RequestTier.Short, cancellationToken).ConfigureAwait(false); } - /// - /// Builds a public URL for downloading this dataset's items. - /// + /// Builds a public URL for downloading the dataset's items. /// - /// It fetches the dataset, and if the dataset exposes a URL-signing secret key (i.e. it is private), - /// appends an HMAC-SHA256 signature so the URL grants access without an API token. - /// optionally bounds the validity of a signed URL (null for - /// non-expiring). The URL is built from the configured public base URL. + /// For a private dataset (one with a URL-signing secret key), the URL carries an HMAC-SHA256 signature, so it works + /// without an API token. /// - /// Optional item filtering/projection options forwarded into the URL. - /// Optional expiry in seconds for a signed URL; must be positive (omit it for a URL that never expires). - /// Output format served by the URL (null defaults to ). + /// Optional item filtering and projection, forwarded into the URL. + /// Expiry of a signed URL in seconds; must be positive. Omit it for a URL that never expires. + /// The format served by the URL (null for ). /// A token to cancel the request. public async Task CreateItemsPublicUrlAsync( DatasetListItemsOptions? options = null, @@ -349,11 +323,9 @@ private static long HeaderInt(HttpResponseMessage response, string name, long fa } /// - /// Returns this client with every subsequent call's timeout set to , - /// overriding the tier default (see the "Timeout tiers" section of docs/README.md). Pass - /// for no timeout, matching the reference client's 'noTimeout'. A - /// value above (the overall budget) is capped at it — - /// raise itself to allow a longer per-call timeout. + /// Returns this client with the timeout of every subsequent call set to , overriding the + /// tier default (see "Timeout tiers" in docs/README.md). means no timeout. A value above + /// is capped at it. /// /// The timeout to use for every call made through this client. public DatasetClient WithTimeout(TimeSpan timeout) diff --git a/src/Apify.Client/Resources/DatasetCollectionClient.cs b/src/Apify.Client/Resources/DatasetCollectionClient.cs index c723d45..4ad2d01 100644 --- a/src/Apify.Client/Resources/DatasetCollectionClient.cs +++ b/src/Apify.Client/Resources/DatasetCollectionClient.cs @@ -30,9 +30,8 @@ public Task> ListAsync(StorageListOptions? options = nul } /// - /// Gets the dataset with the given name, creating it if it does not exist. An empty/null name - /// creates a new unnamed dataset. An optional is sent when creating the - /// dataset, mirroring the reference client's getOrCreate(name, { schema }). + /// Gets the dataset with the given name, creating it if it does not exist. An empty or null name creates an + /// unnamed dataset. An optional is sent on creation. /// /// The dataset name, or null for a new unnamed dataset. /// An optional dataset schema to send on creation. @@ -56,11 +55,9 @@ public IAsyncEnumerable IterateAsync(StorageListOptions? options = null /// - /// Returns this client with every subsequent call's timeout set to , - /// overriding the tier default (see the "Timeout tiers" section of docs/README.md). Pass - /// for no timeout, matching the reference client's 'noTimeout'. A - /// value above (the overall budget) is capped at it — - /// raise itself to allow a longer per-call timeout. + /// Returns this client with the timeout of every subsequent call set to , overriding the + /// tier default (see "Timeout tiers" in docs/README.md). means no timeout. A value above + /// is capped at it. /// /// The timeout to use for every call made through this client. public DatasetCollectionClient WithTimeout(TimeSpan timeout) diff --git a/src/Apify.Client/Resources/KeyValueStoreClient.cs b/src/Apify.Client/Resources/KeyValueStoreClient.cs index b5f8d3a..dbb32cd 100644 --- a/src/Apify.Client/Resources/KeyValueStoreClient.cs +++ b/src/Apify.Client/Resources/KeyValueStoreClient.cs @@ -11,7 +11,7 @@ namespace Apify.Client.Resources; -/// A client for a specific key-value store (and run-nested variants). +/// A client for a specific key-value store. public sealed class KeyValueStoreClient { private readonly HttpClientCore _http; @@ -43,7 +43,7 @@ internal KeyValueStoreClient WithPublicBase(string publicBaseUrl) return data is System.Text.Json.Nodes.JsonObject obj ? new KeyValueStore(obj) : null; } - /// Updates the store metadata (e.g. name) and returns the updated object. + /// Updates the store metadata, such as its name, and returns the updated store. /// Any JSON-serializable set of fields to update. /// A token to cancel the request. public async Task UpdateAsync(object newFields, CancellationToken cancellationToken = default) @@ -65,23 +65,18 @@ public async Task ListKeysAsync(ListKeysOptions? options return KeyValueStoreKeysPage.FromData(await _ctx.GetResourceRequiredAsync("keys", q, RequestTier.Medium, cancellationToken).ConfigureAwait(false)); } - /// - /// Lazily iterates over the store's keys across pages, fetching each page on demand. Mirrors the - /// reference client's chunked listKeys() iteration. - /// + /// Lazily iterates the store's keys across pages, fetching each page on demand. /// - /// With no options it fetches pages of up to keys - /// until the store is exhausted. Limit caps the total number of keys yielded across all pages — - /// 0 yields none, without issuing any request (the API itself reads a literal limit=0 as - /// unbounded, not zero, so this is checked before building the request rather than left to the API), and - /// must not be negative. ChunkSize caps the page size (never sent to the API itself) and must - /// be positive (MaxPageLimit is its older name), and ExclusiveStartKey chooses the starting point (first page only). + /// Without options it fetches pages of up to keys until the store + /// is exhausted. Limit caps the total number of keys; 0 yields none without a request, and it must not be + /// negative. ChunkSize (older name MaxPageLimit) is the page size and must be positive. + /// ExclusiveStartKey chooses the starting point. /// /// Optional iteration options. /// A token to cancel the iteration. /// - /// or is zero or negative, or - /// is negative. + /// or is zero or negative, or + /// is negative. /// public async IAsyncEnumerable IterateKeysAsync( IterateKeysOptions? options = null, @@ -103,9 +98,8 @@ public async IAsyncEnumerable IterateKeysAsync( while (true) { - // Checked before building the request, not only after: the API reads a literal `limit=0` as - // unset (unbounded) rather than "zero", so a Limit of 0 (or one already exhausted by a prior - // page) must stop here instead of reaching the wire as 0 and fetching a full page regardless. + // Checked before building the request: the API reads `limit=0` as unbounded, so a Limit of 0 (or one already + // reached) must stop here. if (limit is not null && iterated >= limit.Value) { yield break; @@ -135,13 +129,8 @@ public async IAsyncEnumerable IterateKeysAsync( iterated += page.Items.Count; - // Continue only while there is a next cursor to follow — mirrors the reference client - // (apify-client-js `key_value_store.ts` listKeys iteration), which continues on - // `nextExclusiveStartKey != null` rather than on `isTruncated` alone. The API answers with - // `null` on the last page and omits the field entirely for a listing that was not truncated, - // but `isTruncated` alone is not a safe continuation signal: if it were ever `true` alongside a - // null/absent cursor, looping on it would re-fetch the same first page forever (no - // `exclusiveStartKey` reaches the wire for a null cursor), yielding duplicate keys. + // Continue only while there is a next cursor, as in the reference client. `isTruncated` alone is not safe: with a + // null cursor no `exclusiveStartKey` would be sent, and the first page would be fetched again forever. if (page.NextExclusiveStartKey is null || (limit is not null && iterated >= limit.Value)) { yield break; @@ -158,14 +147,12 @@ public Task RecordExistsAsync(string key, CancellationToken cancellationTo => _ctx.HeadExistsAsync("records/" + ResourceContext.EncodePathSegment(key, nameof(key)), new QueryParams(), RequestTier.Long, cancellationToken); /// - /// Fetches a record by key, or null if it does not exist. Like the reference client, it requests - /// the record as an attachment so the API returns the raw bytes directly. + /// Fetches a record by key, or null if it does not exist. As in the reference client, the record is requested + /// as an attachment, so the API returns the raw bytes. /// /// - /// The body is never parsed: holds the raw bytes whatever the - /// content type, so a truncated JSON body cannot fail here, and the caller decodes it according to - /// . (The reference client parses JSON, text and XML records - /// by content type; its buffer option returns the raw bytes like this method does.) + /// The body is never parsed: holds the raw bytes whatever the content type, so + /// decode them according to . /// /// The record key. /// Optional fetch options. @@ -179,7 +166,7 @@ public Task RecordExistsAsync(string key, CancellationToken cancellationTo try { using var response = await _http.CallAsync(HttpMethod.Get, url, timeout: _ctx.RequestTimeout, tier: RequestTier.Long, cancellationToken: cancellationToken).ConfigureAwait(false); - // Read the raw bytes (not a decoded string) so binary records survive the round-trip intact. + // Raw bytes keep binary records intact. var body = await response.Content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(false); var contentType = response.Content.Headers.ContentType?.ToString(); return new KeyValueStoreRecord(key, body, string.IsNullOrEmpty(contentType) ? null : contentType); @@ -190,23 +177,16 @@ public Task RecordExistsAsync(string key, CancellationToken cancellationTo } } - /// - /// Stores a record with raw bytes and the given content type, honoring the given write options - /// (TimeoutSecs, DoNotRetryTimeouts, ContentEncoding). - /// + /// Stores a record with raw bytes and a content type. /// The record key. /// - /// The raw record bytes. With set, these must already be - /// encoded that way (e.g. already gzipped) — the client sends them as-is and does not compress them - /// again. + /// The raw record bytes. With , they must already be encoded that way + /// and are sent as they are. /// /// - /// The record's MIME type. Worth setting precisely for media and archives: the client skips - /// compressing a body whose content type already carries its own compression (images, audio, video, - /// archives, office/ZIP formats, web fonts). application/octet-stream — the usual choice for - /// data of an unknown or mixed binary type — is treated as compressible, since it could hold anything. - /// This content-type-based skip does not apply when is - /// set: that always skips the client's own compression, regardless of content type. + /// The record's MIME type. A body whose content type carries its own compression (images, audio, video, archives, + /// fonts) is not compressed by the client. application/octet-stream counts as compressible. A + /// always skips compression. /// /// Optional write options. /// A token to cancel the request. @@ -250,9 +230,8 @@ public Task DeleteRecordAsync(string key, CancellationToken cancellationToken = => _ctx.DeleteResourceAsync("records/" + ResourceContext.EncodePathSegment(key, nameof(key)), RequestTier.Short, cancellationToken); /// - /// Builds a public URL for fetching the given record. It fetches the store, and if the store exposes a - /// URL-signing secret key (i.e. it is private), appends an HMAC-SHA256 signature so the URL grants - /// access without an API token. The URL is built from the configured public base URL. + /// Builds a public URL for the record. For a private store (one with a URL-signing secret key), the URL carries an + /// HMAC-SHA256 signature, so it works without an API token. /// /// The record key. /// A token to cancel the request. @@ -273,12 +252,11 @@ public async Task GetRecordPublicUrlAsync(string key, CancellationToken } /// - /// Builds a public URL for listing this store's keys, forwarding the given key-listing filters into the - /// URL. As with , a signature is appended for private stores unless - /// the caller already supplied one. optionally bounds a signed URL. + /// Builds a public URL for listing the store's keys, forwarding the filters into the URL. As with + /// , a private store's URL is signed. /// /// Optional key-listing filters forwarded into the URL. - /// Optional expiry in seconds for a signed URL; must be positive (omit it for a URL that never expires). + /// Expiry of a signed URL in seconds; must be positive. Omit it for a URL that never expires. /// A token to cancel the request. public async Task CreateKeysPublicUrlAsync(ListKeysOptions? options = null, int? expiresInSecs = null, CancellationToken cancellationToken = default) { @@ -303,11 +281,9 @@ public async Task CreateKeysPublicUrlAsync(ListKeysOptions? options = nu } /// - /// Returns this client with every subsequent call's timeout set to , - /// overriding the tier default (see the "Timeout tiers" section of docs/README.md). Pass - /// for no timeout, matching the reference client's 'noTimeout'. A - /// value above (the overall budget) is capped at it — - /// raise itself to allow a longer per-call timeout. + /// Returns this client with the timeout of every subsequent call set to , overriding the + /// tier default (see "Timeout tiers" in docs/README.md). means no timeout. A value above + /// is capped at it. /// /// The timeout to use for every call made through this client. public KeyValueStoreClient WithTimeout(TimeSpan timeout) diff --git a/src/Apify.Client/Resources/KeyValueStoreCollectionClient.cs b/src/Apify.Client/Resources/KeyValueStoreCollectionClient.cs index 0ba2252..6e9a9fa 100644 --- a/src/Apify.Client/Resources/KeyValueStoreCollectionClient.cs +++ b/src/Apify.Client/Resources/KeyValueStoreCollectionClient.cs @@ -30,8 +30,8 @@ public Task> ListAsync(StorageListOptions? options } /// - /// Gets the store with the given name, creating it if it does not exist. An empty/null name - /// creates a new unnamed store. An optional is sent when creating the store. + /// Gets the store with the given name, creating it if it does not exist. An empty or null name creates an + /// unnamed store. An optional is sent on creation. /// /// The store name, or null for a new unnamed store. /// An optional store schema to send on creation. @@ -55,11 +55,9 @@ public IAsyncEnumerable IterateAsync(StorageListOptions? options /// - /// Returns this client with every subsequent call's timeout set to , - /// overriding the tier default (see the "Timeout tiers" section of docs/README.md). Pass - /// for no timeout, matching the reference client's 'noTimeout'. A - /// value above (the overall budget) is capped at it — - /// raise itself to allow a longer per-call timeout. + /// Returns this client with the timeout of every subsequent call set to , overriding the + /// tier default (see "Timeout tiers" in docs/README.md). means no timeout. A value above + /// is capped at it. /// /// The timeout to use for every call made through this client. public KeyValueStoreCollectionClient WithTimeout(TimeSpan timeout) diff --git a/src/Apify.Client/Resources/LogClient.cs b/src/Apify.Client/Resources/LogClient.cs index 23b6032..cb90411 100644 --- a/src/Apify.Client/Resources/LogClient.cs +++ b/src/Apify.Client/Resources/LogClient.cs @@ -9,10 +9,7 @@ namespace Apify.Client.Resources; -/// -/// A client for accessing the log of an Actor build or run (/v2/logs/{buildOrRunId}, or the -/// run/build-nested .../log). -/// +/// A client for the log of an Actor build or run. public sealed class LogClient { private readonly HttpClientCore _http; @@ -30,19 +27,16 @@ internal static LogClient ForId(HttpClientCore http, string baseUrl, string id) => new(http, ResourceContext.Single(http, baseUrl, "logs", id, "buildOrRunId"), ambiguousNotFound: false); /// - /// Creates a log client nested under a run or build that has no id of its own (run.Log(), - /// build.Log()). A 404 here is ambiguous — it could mean the run/build itself or its log is - /// missing — so and throw instead of resolving - /// null, matching the reference client's catchNotFoundForResourceOrThrow(). + /// Creates a log client nested under a run or build (run.Log(), build.Log()). A 404 is ambiguous there, + /// since the run or build may be missing, so and throw instead of + /// returning null, as in the reference client. /// internal static LogClient Nested(HttpClientCore http, string baseUrl, QueryParams? inheritedParams = null, TimeSpan? timeout = null) => new(http, ResourceContext.Collection(http, baseUrl, "log", inheritedParams).WithTimeout(timeout), ambiguousNotFound: true); /// - /// Fetches the log as text. For a log addressed by its own id (), a 404 - /// resolves to null; for a log nested under a run/build with no id of its own - /// (run.Log()/build.Log()), a 404 is ambiguous (the run/build itself may be what is - /// missing) and throws an instead. + /// Fetches the log as text. A log addressed by its own ID () resolves a 404 to + /// null; a nested log throws an . /// /// Optional log-content options. /// A token to cancel the request. @@ -59,10 +53,8 @@ internal static LogClient Nested(HttpClientCore http, string baseUrl, QueryParam => await _ctx.GetRawRequiredAsync("", q, RequestTier.Long, cancellationToken).ConfigureAwait(false); /// - /// Fetches the log as raw bytes instead of a decoded string — the counterpart of - /// for a log that is not (or might not be) valid UTF-8 text. It does not change how the log is fetched, - /// only how the body is returned; the whole log is downloaded in one response either way. To read it - /// incrementally, use . + /// Fetches the log as raw bytes, for a log that may not be valid UTF-8. The whole log is downloaded in one response; + /// use to read it incrementally. /// /// Optional log-content options. /// A token to cancel the request. @@ -79,14 +71,12 @@ internal static LogClient Nested(HttpClientCore http, string baseUrl, QueryParam => await _ctx.GetRawBytesRequiredAsync("", q, RequestTier.Long, cancellationToken).ConfigureAwait(false); /// - /// Opens a live, streaming connection to the log and returns a stream over the log bytes. For a log - /// addressed by its own id, a 404 resolves to null (matching ); for a log - /// nested under a run/build with no id of its own, a 404 is ambiguous and throws instead. + /// Opens a live stream of the log bytes. A log addressed by its own ID resolves a 404 to null, like + /// ; a nested log throws. /// /// - /// Unlike , this bypasses the buffered/retrying transport so the log can be - /// followed in real time as the run produces it (the stream=1 query parameter). Because the - /// response is consumed incrementally, it is not retried. The caller must dispose the returned stream. + /// Unlike , this bypasses the buffered, retrying transport (stream=1) to follow the log + /// in real time, so it is not retried. Dispose the returned stream. /// /// Optional log-content options. /// A token to cancel the request. @@ -116,11 +106,9 @@ internal static LogClient Nested(HttpClientCore http, string baseUrl, QueryParam } /// - /// Returns this client with every subsequent call's timeout set to , - /// overriding the tier default (see the "Timeout tiers" section of docs/README.md). Pass - /// for no timeout, matching the reference client's 'noTimeout'. A - /// value above (the overall budget) is capped at it — - /// raise itself to allow a longer per-call timeout. + /// Returns this client with the timeout of every subsequent call set to , overriding the + /// tier default (see "Timeout tiers" in docs/README.md). means no timeout. A value above + /// is capped at it. /// /// The timeout to use for every call made through this client. public LogClient WithTimeout(TimeSpan timeout) diff --git a/src/Apify.Client/Resources/NestedWebhookCollectionClient.cs b/src/Apify.Client/Resources/NestedWebhookCollectionClient.cs index 067cb9b..6102eb5 100644 --- a/src/Apify.Client/Resources/NestedWebhookCollectionClient.cs +++ b/src/Apify.Client/Resources/NestedWebhookCollectionClient.cs @@ -4,10 +4,8 @@ namespace Apify.Client.Resources; /// -/// A read-only client for the webhooks nested under an Actor (GET /v2/actors/{id}/webhooks) or a -/// task (GET /v2/actor-tasks/{id}/webhooks). These endpoints only support listing; webhooks are -/// created through the account-wide (which targets an Actor or task -/// via the webhook's condition), so Create is intentionally not exposed. +/// A read-only client for the webhooks nested under an Actor or task. These endpoints only list; create webhooks with +/// the account-wide , targeting the Actor or task in the webhook's condition. /// public sealed class NestedWebhookCollectionClient : AbstractWebhookCollectionClient { @@ -17,11 +15,9 @@ internal NestedWebhookCollectionClient(HttpClientCore http, string baseUrl) } /// - /// Returns this client with every subsequent call's timeout set to , - /// overriding the tier default (see the "Timeout tiers" section of docs/README.md). Pass - /// for no timeout, matching the reference client's 'noTimeout'. A - /// value above (the overall budget) is capped at it — - /// raise itself to allow a longer per-call timeout. + /// Returns this client with the timeout of every subsequent call set to , overriding the + /// tier default (see "Timeout tiers" in docs/README.md). means no timeout. A value above + /// is capped at it. /// /// The timeout to use for every call made through this client. public NestedWebhookCollectionClient WithTimeout(TimeSpan timeout) diff --git a/src/Apify.Client/Resources/RequestQueueClient.cs b/src/Apify.Client/Resources/RequestQueueClient.cs index b660a05..cd7334e 100644 --- a/src/Apify.Client/Resources/RequestQueueClient.cs +++ b/src/Apify.Client/Resources/RequestQueueClient.cs @@ -13,22 +13,22 @@ namespace Apify.Client.Resources; -/// A client for a specific request queue (and run-nested variants). +/// A client for a specific request queue. public sealed class RequestQueueClient { - /// The API limit on requests per batch call; larger inputs are split into chunks of this size. + /// The API limit on requests per batch call. private const int MaxRequestsPerBatch = 25; /// - /// The API's maximum accepted request payload size (9 MiB). Batches are additionally split so no single - /// batch call exceeds this, matching the reference client's splitIntoJsonArrayBatches. + /// The API's maximum request payload size (9 MiB). Batches are also split to stay below it, as in the reference + /// client's splitIntoJsonArrayBatches. /// private const int MaxPayloadSizeBytes = 9 * 1024 * 1024; - /// Safety margin (0.01%) subtracted from the payload limit, matching the reference client. + /// Safety margin (0.01%) subtracted from the payload limit, as in the reference client. private const double PayloadSafetyBufferPercent = 0.0001; - /// The two bytes of the brackets that wrap a JSON array body, even one holding a single request. + /// The brackets that wrap a JSON array body. private const int JsonArrayBracketBytes = 2; private readonly HttpClientCore _http; @@ -56,9 +56,8 @@ internal static RequestQueueClient Nested(HttpClientCore http, string baseUrl, s => new(http, ResourceContext.NestedSingleton(http, baseUrl, subPath, inheritedParams).WithTimeout(timeout), null, timeout); /// - /// Returns a copy of the client that identifies its requests with . A - /// stable client key is required to operate on locks the client itself created, and lets the API detect - /// whether multiple clients access a queue. + /// Returns a copy of the client that identifies its requests with . A stable key is + /// needed to operate on locks the client itself created. /// /// The stable client key. public RequestQueueClient WithClientKey(string clientKey) => new(_http, _ctx, clientKey, _timeout); @@ -71,7 +70,7 @@ internal static RequestQueueClient Nested(HttpClientCore http, string baseUrl, s return data is JsonObject obj ? new RequestQueue(obj) : null; } - /// Updates the queue metadata (e.g. name) and returns the updated object. + /// Updates the queue metadata, such as its name, and returns the updated queue. /// Any JSON-serializable set of fields to update. /// A token to cancel the request. public async Task UpdateAsync(object newFields, CancellationToken cancellationToken = default) @@ -84,8 +83,7 @@ public async Task UpdateAsync(object newFields, CancellationToken public Task DeleteAsync(CancellationToken cancellationToken = default) => _ctx.DeleteResourceAsync("", RequestTier.Short, cancellationToken); /// - /// Returns the requests at the head (front) of the queue, up to (null - /// for the server default). + /// Returns up to requests from the head of the queue (null for the server default). /// /// The maximum number of requests to return. /// A token to cancel the request. @@ -97,7 +95,7 @@ public async Task ListHeadAsync(int? limit = null, Cancellatio return RequestQueueHead.FromData(await _ctx.GetResourceRequiredAsync("head", q, RequestTier.Short, cancellationToken).ConfigureAwait(false)); } - /// Adds a request to the queue. If is true, it is added to the front. + /// Adds a request to the queue, to the front if is true. /// The request to add. /// Whether to add to the front of the queue. /// A token to cancel the request. @@ -120,8 +118,8 @@ public async Task AddRequestAsync(RequestQueueRequest } /// - /// Updates an existing request (identified by its ID field) and returns the operation info. If - /// is true, the request is moved to the front of the queue. + /// Updates an existing request (identified by its ID) and moves it to the front of the queue if + /// is true. /// /// The request to update (must have an ID). /// Whether to move the request to the front. @@ -155,12 +153,12 @@ public async Task DeleteRequestAsync(string id, CancellationToken cancellationTo } catch (ApifyApiException e) when (HttpClientCore.IsNotFound(e)) { - // A missing request is a successful no-op for delete. + // A missing resource is a no-op. } } /// - /// Atomically returns and locks up to requests from the head of the queue for + /// Returns up to requests from the head of the queue and locks them for /// seconds. /// /// How long to lock the returned requests, in seconds. @@ -175,20 +173,14 @@ public async Task ListAndLockHeadAsync(int lockSecs, int return LockedRequestQueueHead.FromData(data); } - /// - /// Adds multiple requests to the queue. If is true, they are added to the - /// front. - /// + /// Adds multiple requests to the queue, to the front if is true. /// - /// The input is automatically split into chunks of at most 25 requests (the API count limit) that - /// additionally respect the API's ~9 MiB payload-size limit. Chunks are dispatched using up to - /// concurrent API calls (set it to 1 for sequential - /// dispatch). Requests the API returns as unprocessed in a successful response (typically rate-limited) - /// are retried with exponential backoff; the per-chunk results are merged in input order. Every request - /// must carry a non-empty UniqueKey. Consistent with the reference client, this method does not - /// throw on API errors: if a batch call fails and the transport did not retry, that chunk's - /// not-yet-processed requests are returned in . Invalid - /// input (empty uniqueKey, oversized request) is rejected up front with . + /// The input is split into chunks of at most 25 requests that also stay below the API's payload limit. Up to + /// chunks are sent concurrently (1 for sequential). Requests the API + /// returns as unprocessed are retried with exponential backoff, and the results are merged in input order. Every + /// request needs a non-empty UniqueKey; invalid input throws before anything + /// is sent. As in the reference client, an API error does not throw: the requests of the failed chunk are returned in + /// . /// /// The requests to add. /// Whether to add to the front of the queue. @@ -214,9 +206,8 @@ public async Task BatchAddRequestsAsync( var payloadSizeLimitBytes = MaxPayloadSizeBytes - (int)Math.Ceiling(MaxPayloadSizeBytes * PayloadSafetyBufferPercent); - // Serialize every request once: the byte lengths decide the batch boundaries, and the same strings are - // joined into the batch bodies, so nothing is serialized a second time when it is sent. All chunks are - // planned up front, so an oversized request is rejected before any batch is sent. + // Requests are serialized once: the byte lengths decide the batch boundaries and the same strings form the batch + // bodies. All chunks are planned first, so an oversized request is rejected before any batch is sent. var serialized = new List(list.Count); for (var i = 0; i < list.Count; i++) { @@ -239,7 +230,7 @@ public async Task BatchAddRequestsAsync( return merged; } - // Sequential path when parallelism is disabled or there is only a single chunk. + // Sequential when parallelism is off or there is a single chunk. if (options.MaxParallel <= 1 || chunks.Count == 1) { foreach (var chunk in chunks) @@ -250,9 +241,7 @@ public async Task BatchAddRequestsAsync( return merged; } - // Bounded-parallel dispatch: at most MaxParallel chunk calls run concurrently, gated by a semaphore. - // Results are merged in chunk (input) order so the output stays deterministic regardless of which - // chunk finishes first. + // At most MaxParallel chunks run at once. Results are merged in input order, whichever chunk finishes first. using var gate = new SemaphoreSlim(options.MaxParallel); var tasks = new List>(chunks.Count); foreach (var chunk in chunks) @@ -268,7 +257,7 @@ public async Task BatchAddRequestsAsync( return merged; } - /// Runs one chunk's add-with-retries under the concurrency gate. + /// Adds one chunk with retries, under the concurrency gate. private async Task DispatchChunkAsync( List chunk, bool forefront, @@ -287,20 +276,19 @@ private async Task DispatchChunkAsync( } } - /// A request together with its JSON serialization and that serialization's UTF-8 size. + /// A request with its JSON and the UTF-8 size of that JSON. private readonly record struct SerializedRequest(RequestQueueRequest Request, string Json, int ByteLength); /// - /// Splits serialized requests into batches of at most requests whose JSON - /// array bodies are at most bytes, always keeping at least one request per batch - /// so the split makes progress. Ports the reference client's splitIntoJsonArrayBatches. + /// Splits serialized requests into batches of at most requests and + /// bytes of JSON array, with at least one request per batch. Ports the reference + /// client's splitIntoJsonArrayBatches. /// private static List> SplitIntoJsonArrayBatches(List requests, int maxByteLength) { var batches = new List>(); var batch = new List(); - // One byte for the opening bracket; each request then adds its own bytes plus one for the comma or the - // closing bracket that follows it. + // One byte for the opening bracket; each request adds its bytes plus the comma or closing bracket after it. var byteLength = 1; foreach (var request in requests) { @@ -341,21 +329,18 @@ private async Task BatchAddChunkWithRetriesAsync(List serialized.Request); break; } processed.AddRange(response.ProcessedRequests); - // Only requests the API reports as unprocessed in this SUCCESSFUL response are retried. + // Only requests reported as unprocessed in a successful response are retried. unprocessed = new List(response.UnprocessedRequests); if (unprocessed.Count != 0) { - // As in the reference client, a response with unprocessed requests counts as rate limited: the - // SDK reads these counts to scale down. + // As in the reference client, a response with unprocessed requests counts as rate limited. _http.Stats.AddRateLimitError(attempt + 1); } @@ -405,7 +390,7 @@ private async Task BatchAddChunkAsync(List re return new BatchAddResult(processed, unprocessed); } - /// Joins already-serialized requests into a JSON array body without serializing them again. + /// Joins serialized requests into a JSON array body. private static string JoinAsJsonArray(List requests) => "[" + string.Join(",", requests.ConvertAll(static serialized => serialized.Json)) + "]"; @@ -439,16 +424,15 @@ private static Task SleepBackoffAsync(int attempt, int minDelayMillis, Cancellat return Task.CompletedTask; } - // (1 + random) * 2^attempt * minDelay — exponential backoff with jitter, matching the reference. + // Exponential backoff with jitter, as in the reference client: (1 + random) * 2^attempt * minDelay. var factor = (1 + Random.Shared.NextDouble()) * Math.Pow(2, attempt); var delayMillis = (int)Math.Floor(factor * minDelayMillis); return Task.Delay(delayMillis, cancellationToken); } /// - /// Deletes multiple requests in a single call. Each entry must have its - /// and/or set to identify the request to delete; other fields - /// are ignored. + /// Deletes multiple requests in one call. Each entry needs its or + /// ; other fields are ignored. /// /// The requests to delete, identified by Id and/or UniqueKey. /// A token to cancel the request. @@ -460,7 +444,7 @@ public async Task BatchDeleteRequestsAsync(IReadOnlyListEncodes a list of requests as their raw JSON objects, for a batch request body. + /// Encodes requests as their raw JSON objects for a batch body. private static List ToPayload(IReadOnlyCollection requests) { var payload = new List(requests.Count); @@ -487,8 +471,8 @@ public async Task ListRequestsAsync(ListRequestsOption } /// - /// Extends the lock on a request by seconds. If - /// is true, the request is moved to the front when its lock expires. + /// Extends the lock on a request by seconds. With , the + /// request moves to the front when the lock expires. /// /// The request ID. /// How much longer to hold the lock, in seconds. @@ -504,10 +488,7 @@ public async Task ProlongRequestLockAsync(string id, int lockSe return RequestLockInfo.FromData(await response.ReadDataObjectAsync(cancellationToken).ConfigureAwait(false)); } - /// - /// Releases the lock on a request. If is true, the request is moved to the - /// front of the queue. - /// + /// Releases the lock on a request, moving it to the front of the queue if is true. /// The request ID. /// Whether to move the request to the front. /// A token to cancel the request. @@ -523,7 +504,7 @@ public async Task DeleteRequestLockAsync(string id, bool forefront = false, Canc } catch (ApifyApiException e) when (HttpClientCore.IsNotFound(e)) { - // A missing lock is a successful no-op. + // A missing resource is a no-op. } } @@ -537,15 +518,12 @@ public async Task UnlockRequestsAsync(CancellationToken ca return UnlockRequestsResult.FromData(data); } - /// - /// Lazily iterates over the queue's requests, transparently following pagination. - /// + /// Lazily iterates the queue's requests, following pagination. /// - /// With no options it fetches pages of up to - /// requests until the queue is exhausted. The options mirror the reference client: Limit caps the - /// total number of requests yielded across all pages, ChunkSize (or its older name MaxPageLimit) caps the page size, - /// ExclusiveStartId/Cursor choose the starting point (first page only), and Filter - /// restricts to locked/pending requests. + /// Without options it fetches pages of up to requests until + /// the queue is exhausted. Limit caps the total, ChunkSize (or its older name MaxPageLimit) the + /// page size, ExclusiveStartId and Cursor choose the starting point and Filter restricts the + /// requests to locked or pending ones. /// /// Optional iteration options. /// A token to cancel the iteration. @@ -594,7 +572,7 @@ public async IAsyncEnumerable PaginateRequestsAsync( yield break; } - // After the first page, paginate purely by cursor. + // After the first page, paginate by cursor only. nextExclusiveStartId = null; } } diff --git a/src/Apify.Client/Resources/RequestQueueCollectionClient.cs b/src/Apify.Client/Resources/RequestQueueCollectionClient.cs index f0cd1ae..7356074 100644 --- a/src/Apify.Client/Resources/RequestQueueCollectionClient.cs +++ b/src/Apify.Client/Resources/RequestQueueCollectionClient.cs @@ -28,10 +28,7 @@ public Task> ListAsync(StorageListOptions? options return _ctx.ListResourceAsync("", q, static d => new RequestQueue(d), RequestTier.Medium, cancellationToken); } - /// - /// Gets the queue with the given name, creating it if it does not exist. An empty/null name - /// creates a new unnamed queue. - /// + /// Gets the queue with the given name, creating it if it does not exist. An empty or null name creates an unnamed queue. /// The queue name, or null for a new unnamed queue. /// A token to cancel the request. public async Task GetOrCreateAsync(string? name = null, CancellationToken cancellationToken = default) @@ -53,11 +50,9 @@ public IAsyncEnumerable IterateAsync(StorageListOptions? options = /// - /// Returns this client with every subsequent call's timeout set to , - /// overriding the tier default (see the "Timeout tiers" section of docs/README.md). Pass - /// for no timeout, matching the reference client's 'noTimeout'. A - /// value above (the overall budget) is capped at it — - /// raise itself to allow a longer per-call timeout. + /// Returns this client with the timeout of every subsequent call set to , overriding the + /// tier default (see "Timeout tiers" in docs/README.md). means no timeout. A value above + /// is capped at it. /// /// The timeout to use for every call made through this client. public RequestQueueCollectionClient WithTimeout(TimeSpan timeout) diff --git a/src/Apify.Client/Resources/RunClient.cs b/src/Apify.Client/Resources/RunClient.cs index 533e268..6b9cf0f 100644 --- a/src/Apify.Client/Resources/RunClient.cs +++ b/src/Apify.Client/Resources/RunClient.cs @@ -15,12 +15,9 @@ namespace Apify.Client.Resources; /// -/// A client for a specific Actor run. +/// A client for a specific Actor run: get, update, delete, abort, metamorph, reboot, resurrect, charge and wait for +/// it to finish, plus accessors for its default storages and log. /// -/// -/// It provides CRUD methods plus convenience helpers (abort, metamorph, reboot, resurrect, charge, -/// wait-for-finish) and accessors for the run's default storages and log. -/// public sealed class RunClient { /// Header the API uses to deduplicate charge requests. @@ -40,8 +37,8 @@ internal RunClient(ApifyClient root, HttpClientCore http, string baseUrl, string } /// - /// Pins the status/origin query parameters inherited by all calls on this client (used by - /// the last-run accessors). Empty values are skipped. + /// Pins the status and origin query parameters of every call on this client, for the last-run + /// accessors. Empty values are skipped. /// internal void SetLastRunParams(LastRunOptions options) { @@ -57,8 +54,8 @@ internal void SetLastRunParams(LastRunOptions options) } /// - /// Fetches the run, optionally asking the API to wait up to - /// seconds (max 60) for the run to reach a terminal state. Returns null if it does not exist. + /// Fetches the run, optionally asking the API to wait up to seconds (at most + /// 60) for it to finish. Returns null if the run does not exist. /// /// Optional server-side wait in seconds. /// A token to cancel the request. @@ -66,8 +63,7 @@ internal void SetLastRunParams(LastRunOptions options) { var q = new QueryParams(); q.AddInt("waitForFinish", _ctx.ClampServerWait(waitForFinishSecs)); - // Long: the clamped waitForFinish can hold the connection open close to the overall budget, so the - // HTTP timeout for this call must cover it (matches the reference's short-base-extended-for-wait). + // Long tier: the server may hold the connection for most of the wait. var data = await _ctx.GetResourceAsync("", q, RequestTier.Long, cancellationToken).ConfigureAwait(false); return data is JsonObject obj ? new ActorRun(obj) : null; } @@ -85,9 +81,8 @@ public async Task UpdateAsync(object newFields, CancellationToken canc public Task DeleteAsync(CancellationToken cancellationToken = default) => _ctx.DeleteResourceAsync("", RequestTier.Short, cancellationToken); /// - /// Aborts the run. If is true, the run is signalled so it can - /// finish the current request before terminating; false aborts immediately. null omits - /// the parameter and lets the server apply its default (immediate abort). + /// Aborts the run. true lets it finish the current request first, false aborts at once and null + /// leaves the choice to the server (an immediate abort). /// /// Whether to abort gracefully, or null for the server default. /// A token to cancel the request. @@ -100,10 +95,7 @@ public async Task AbortAsync(bool? gracefully = null, CancellationToke /// Transforms the run into a run of another Actor with a new input. /// The Actor to metamorph into. - /// - /// The new input: a plain object or array serialized to JSON, raw bytes (byte[]) sent as-is, or - /// null for none. - /// + /// The new input: byte[] is sent as it is, anything else as JSON, null for none. /// Optional metamorph options. /// A token to cancel the request. public async Task MetamorphAsync( @@ -124,7 +116,7 @@ public async Task MetamorphAsync( return new ActorRun(await _ctx.PostWithBodyAsync("metamorph", q, body, options.ContentTypeOrDefault(), RequestTier.Medium, cancellationToken, bodyBytes).ConfigureAwait(false)); } - /// Reboots the run (restarts its container while keeping the same run). + /// Restarts the run's container, keeping the same run. /// A token to cancel the request. public async Task RebootAsync(CancellationToken cancellationToken = default) { @@ -132,8 +124,8 @@ public async Task RebootAsync(CancellationToken cancellationToken = de } /// - /// Resurrects a finished run (status SUCCEEDED, FAILED, ABORTED or TIMED-OUT) and - /// restarts its container with the same storages. + /// Restarts a finished run (SUCCEEDED, FAILED, ABORTED or TIMED-OUT) in its container, + /// with the same storages. /// /// Optional resurrect options. /// A token to cancel the request. @@ -144,13 +136,10 @@ public async Task ResurrectAsync(RunResurrectOptions? options = null, return new ActorRun(await _ctx.PostWithBodyAsync("resurrect", q, null, "", RequestTier.Medium, cancellationToken).ConfigureAwait(false)); } - /// - /// Charges for a pay-per-event Actor run: records occurrences of a named event. Only meaningful for - /// runs of pay-per-event Actors. - /// + /// Charges for events of a pay-per-event Actor run. /// - /// An idempotency key is always sent (auto-generated if not provided), so a charge that is retried by - /// the transport is applied at most once, matching the reference client. + /// The request always carries an idempotency key (generated when not given), so a charge the transport retries is + /// applied once. /// /// The charge event details. /// A token to cancel the request. @@ -188,10 +177,7 @@ public async Task ChargeAsync(RunChargeOptions options, CancellationToken cancel cancellationToken: cancellationToken).ConfigureAwait(false); } - /// - /// Builds a per-charge idempotency key of the form "{runId}-{eventName}-{millis}-{random}". It - /// need not be cryptographically secure, only unique enough to avoid collisions within a request. - /// + /// Builds an idempotency key {runId}-{eventName}-{millis}-{random}; it only has to be unique, not secure. private string GenerateIdempotencyKey(string eventName) { return string.Format( @@ -204,8 +190,8 @@ private string GenerateIdempotencyKey(string eventName) } /// - /// Polls until the run reaches a terminal state or elapses (null - /// waits indefinitely). Returns the latest run. + /// Polls until the run reaches a terminal state or elapses (null waits + /// indefinitely) and returns the latest run. /// /// The wait budget in seconds, or null to wait indefinitely. /// A token to cancel the wait. @@ -216,8 +202,7 @@ public async Task WaitForFinishAsync(int? waitSecs = null, Cancellatio } /// - /// Builds the reference client's default sink: the run and its Actor are fetched for the - /// <Actor name> runId:<id> -> prefix. + /// Builds the default log sink of the reference client: lines prefixed with <Actor name> runId:<id> -> . /// private async Task> CreateDefaultSinkAsync(CancellationToken cancellationToken) { @@ -227,10 +212,7 @@ private async Task> CreateDefaultSinkAsync(CancellationToken canc return StreamedLog.CreatePrefixedSink(run?.Id ?? _id, actor?.Name); } - /// - /// Waits for the run to finish, optionally redirecting its live log to for the - /// duration of the wait. Shared by Actor.Call/Task.Call's log option. - /// + /// Waits for the run to finish, redirecting its live log to meanwhile. internal async Task WaitForFinishWithLogAsync(int? waitSecs, Action? log, CancellationToken cancellationToken) { if (log is null) @@ -256,18 +238,14 @@ internal async Task WaitForFinishWithLogAsync(int? waitSecs, Action - /// Lazily iterates over the items of the run's default dataset while the run is still producing them. - /// Mirrors the reference client's iterateDatasetItems(). + /// Lazily iterates the items of the run's default dataset while the run is still producing them, as the reference + /// client's iterateDatasetItems() does. /// /// - /// While the run has not finished, each poll yields the rows below the dataset's itemCount and - /// then waits up to for the run to finish, - /// so the last rows are read as soon as it does. Each page is requested with a limit that ends at - /// itemCount, so it covers exactly the rows it asks for, whatever the filters or - /// do to the items. itemCount lags a few - /// seconds behind the pushed items, so once the run reaches a terminal status the rows past it are read - /// a page at a time until none are left. On a last-run client, the iteration sticks to the run its - /// first request resolves to, so a newer run cannot swap the dataset mid-iteration. + /// While the run is active, each poll yields the rows below the dataset's itemCount, then waits up to + /// for the run to finish. itemCount lags a few + /// seconds behind the pushed items, so after the run finishes the remaining rows are read page by page. A last-run + /// client sticks to the run its first request resolved to. /// /// Optional pagination, filtering and polling options. /// A token to cancel the iteration. @@ -284,12 +262,11 @@ internal async Task WaitForFinishWithLogAsync(int? waitSecs, Action 0 ? (long)position + options.Limit.Value : null; var run = await GetAsync(cancellationToken: cancellationToken).ConfigureAwait(false); - // A last-run client resolves `runs/last` on each request, so a newer run would swap the dataset - // mid-iteration: pin to the run the first request resolved to. + // A last-run client resolves `runs/last` on each request; pin the run of the first one so a newer run cannot swap the dataset. var runClient = this; if (run?.Id is { } resolvedId && resolvedId != _id) { @@ -347,9 +324,8 @@ internal async Task WaitForFinishWithLogAsync(int? waitSecs, Action