Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,36 @@ All notable changes to the Apify Go client are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.4.0] - 2026-06-26

Updated to Apify OpenAPI specification `v2-2026-06-25T142310Z` (previously
`v2-2026-06-24T105326Z`). An operation- and parameter-level audit against the new specification
found no changes to the in-scope API surface; the spec update itself is a version bump only. This
release adds an `origin` filter to the last-run convenience accessors (additively, with no
breaking change) for parity with the `apify-client-js` reference, and cleans up stale in-code
comments.

### Added

- `ActorClient.LastRunWithOptions` and `TaskClient.LastRunWithOptions` accept a `LastRunOptions`
with `Status` and `Origin` filters, matching the reference client's `lastRun({ status, origin })`.
The existing `LastRun(status string)` accessors are unchanged (they delegate to the new methods
with only `Status` set), so this is a purely additive, non-breaking change. `Origin` is a
reference-client convenience threaded to the same `runs/last` endpoint; the OpenAPI spec does not
document it as a query parameter.

### Changed

- Bumped `API_SPEC_VERSION` to `v2-2026-06-25T142310Z`.
- Bumped `CLIENT_VERSION` to `0.4.0` (minor bump per SemVer for the additive `LastRunWithOptions`
API).

### Fixed

- Cleaned up stale in-code comments around the `isAtHome` User-Agent flag that quoted the older,
capitalized requirement wording; the comments now match the current lowercase requirement text.
No behavior change — the flag already rendered lowercase (`true`/`false`).

## [0.3.0] - 2026-06-25

Updated to Apify OpenAPI specification `v2-2026-06-24T105326Z` (previously
Expand Down
13 changes: 10 additions & 3 deletions actor.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,18 @@ func (c *ActorClient) ValidateInputForBuild(ctx context.Context, input any, buil

// LastRun returns a client for the last run of this Actor, optionally filtered by status
// (e.g. "SUCCEEDED"). Pass an empty status for no filter.
//
// To also filter by run origin, use LastRunWithOptions.
func (c *ActorClient) LastRun(status string) *RunClient {
return c.LastRunWithOptions(LastRunOptions{Status: status})
}

// LastRunWithOptions returns a client for the last run of this Actor, optionally filtered by
// status and/or origin. See LastRunOptions. Mirrors the reference client's
// lastRun({ status, origin }).
func (c *ActorClient) LastRunWithOptions(options LastRunOptions) *RunClient {
client := newRunClient(c.root, c.ctx.http, c.ctx.subURL(""), "runs", "last")
if status != "" {
client.setStatusParam(status)
}
client.setLastRunParams(options)
return client
}

Expand Down
2 changes: 1 addition & 1 deletion client.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func withIsAtHomeFn(fn func() bool) Option {
// Environment variable that signals the client is running on the Apify platform.
//
// Per client_requirements.md the flag is based solely on APIFY_IS_AT_HOME ("based on the
// environment variable `APIFY_IS_AT_HOME`, `False` if env variable is missing"), which also
// environment variable `APIFY_IS_AT_HOME`, `false` if env variable is missing"), which also
// matches the JS reference (it reads only APIFY_IS_AT_HOME via @apify/consts).
const envIsAtHome = "APIFY_IS_AT_HOME"

Expand Down
12 changes: 2 additions & 10 deletions common.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,16 +213,8 @@ type PaginationList[T any] struct {
// `ApifyClient/{version} ({os}; {language version}); isAtHome/{isAtHome}`.
//
// isAtHome is driven solely by the platform's APIFY_IS_AT_HOME environment variable (matching
// the requirements and the reference JS client, which reads it via @apify/consts); it is set to
// any non-empty value when the client runs on the Apify platform.
//
// Casing note (deliberate): the flag is rendered lowercase (true/false). The requirements'
// worked example shows the capitalized form (isAtHome/True | isAtHome/False), but that example
// is Python-specific (Python's str(bool) is "True"/"False"). The "consistent with the JS
// reference" requirement is same-priority, and the JS client interpolates a JS boolean, which
// stringifies lowercase; the Rust sibling client emits lowercase too. Lowercase is therefore the
// cross-client-consistent choice, and the literal capitalized example is treated as illustrative
// rather than normative. See CHANGELOG.md.
// the requirements and the reference JS client, which reads it via @apify/consts) and is
// rendered lowercase (true/false), consistent with the JS and Rust sibling clients.
func BuildUserAgent(suffix string, isAtHomeFn func() bool) string {
atHome := "false"
if isAtHomeFn() {
Expand Down
33 changes: 33 additions & 0 deletions docs/actors.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ The `Actor` value returned by `Get`/`Create`/`Update` and listed by `List`:
| `ValidateInput(ctx, input any) (json.RawMessage, error)` | Validate input against the `latest` build's schema. |
| `ValidateInputForBuild(ctx, input any, build string) (json.RawMessage, error)` | Validate input against a specific build's schema (tag or number). |
| `LastRun(status string) *RunClient` | Client for the last run (optional status filter). |
| `LastRunWithOptions(options LastRunOptions) *RunClient` | Client for the last run, filtered by status and/or origin. |
| `Builds() *BuildCollectionClient` | This Actor's builds. |
| `Runs() *RunCollectionClient` | This Actor's runs. |
| `Version(n) *ActorVersionClient` / `Versions() *ActorVersionCollectionClient` | Versions. |
Expand All @@ -96,6 +97,30 @@ omits the parameter, so the API validates against the build tagged `latest` (per
specification). Both return the raw JSON validation result from the API — a JSON object
reporting whether the input is valid and, if not, the schema violations.

`LastRun(status)` returns the Actor's most recent run, optionally narrowed to a status. Use
`LastRunWithOptions` to also narrow by origin (how the run was started). Both return a
`RunClient` for the resolved run, so you can chain `.Get(ctx)`, `.Dataset()`, etc.

`LastRunOptions` (all fields optional; an empty field leaves that filter unset):

| Field | Type | Meaning |
|---|---|---|
| `Status` | `string` | Filter by run status (e.g. `SUCCEEDED`, `FAILED`, `RUNNING`). |
| `Origin` | `string` | Filter by how the run was started: `DEVELOPMENT`, `WEB`, `API`, `SCHEDULER`, `TEST`, `WEBHOOK`, `ACTOR`, `CLI`, `CI`, `STANDBY`, `MCP`. |

```go
// Most recent run that both SUCCEEDED and was started via the API.
lastRun, ok, err := client.Actor("apify/hello-world").
LastRunWithOptions(apify.LastRunOptions{Status: "SUCCEEDED", Origin: "API"}).
Get(ctx)
if err != nil {
log.Fatal(err)
}
if ok {
fmt.Printf("last API run: %s (%s)\n", lastRun.ID, lastRun.Status)
}
```

```go
// Validate input against a specific build's input schema.
result, err := client.Actor("apify/hello-world").ValidateInputForBuild(ctx,
Expand Down Expand Up @@ -153,6 +178,14 @@ run, err := client.Actor("apify/hello-world").Call(ctx,
| `Version(n).EnvVars().List(ctx)` / `.Create(ctx, ActorEnvVar)` | List/create env vars. |
| `Version(n).EnvVar(name).Get/Update/Delete(ctx)` | Manage a single env var. |

`ActorEnvVar` fields:

| Field | Type | Meaning |
|---|---|---|
| `Name` | `string` | The environment variable name (required). |
| `Value` | `string` | The environment variable value. |
| `IsSecret` | `*bool` | Whether the value is stored as a secret (encrypted at rest). |

```go
v := client.Actor(actorID).Version("0.0")
_, err := v.EnvVars().Create(ctx, apify.ActorEnvVar{Name: "API_KEY", Value: "secret", IsSecret: apify.Ptr(true)})
Expand Down
18 changes: 18 additions & 0 deletions docs/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ A task is a pre-configured Actor run with stored input. Access the task collecti
| `GetInput(ctx) (json.RawMessage, bool, error)` | Fetch the stored input. |
| `UpdateInput(ctx, input any) (json.RawMessage, error)` | Replace the stored input. |
| `LastRun(status string) *RunClient` | Client for the last run (optional status filter). |
| `LastRunWithOptions(options LastRunOptions) *RunClient` | Client for the last run, filtered by status and/or origin. |
| `Runs() *RunCollectionClient` | This task's runs. |
| `Webhooks() *WebhookCollectionClient` | This task's webhooks. |

Expand All @@ -54,3 +55,20 @@ run, err := client.Task(task.ID).Call(ctx, nil, apify.TaskStartOptions{}, apify.
`TaskStartOptions` mirrors `ActorStartOptions` (see [actors.md](actors.md)) but omits the
Actor-only `ContentType` and `ForcePermissionLevel`, which the task run endpoint does not
accept.

`LastRun(status)` / `LastRunWithOptions(LastRunOptions{Status, Origin})` resolve the task's most
recent run, optionally narrowed by status and/or origin. `LastRunOptions` is the same type used
by the Actor client — see [actors.md](actors.md#single-actor) for its field reference.

```go
// Most recent task run that SUCCEEDED.
lastRun, ok, err := client.Task("my-task-id").
LastRunWithOptions(apify.LastRunOptions{Status: "SUCCEEDED"}).
Get(ctx)
if err != nil {
log.Fatal(err)
}
if ok {
fmt.Printf("last run: %s (%s)\n", lastRun.ID, lastRun.Status)
}
```
26 changes: 22 additions & 4 deletions run.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,28 @@ func newRunClient(root *ApifyClient, hc *httpClient, baseURL, resourcePath, id s
}
}

// setStatusParam pins a `status` query parameter inherited by all calls on this client.
// Used by ActorClient.LastRun / TaskClient.LastRun to filter the "last" run by status.
func (c *RunClient) setStatusParam(status string) {
c.ctx.baseParams.addRaw("status", status)
// LastRunOptions filters which "last" run the ActorClient.LastRunWithOptions /
// TaskClient.LastRunWithOptions accessors resolve to. An empty field leaves that filter unset.
//
// Origin is an Apify-platform convenience exposed by the reference client (lastRun({ origin }))
// but not documented as a query parameter in the OpenAPI spec; it is included for parity with the
// reference, which threads it to the same runs/last endpoint.
type LastRunOptions struct {
// Status filters by run status (e.g. "SUCCEEDED", "FAILED", "RUNNING").
Status string
// Origin filters by how the run was started (e.g. "DEVELOPMENT", "WEB", "API", "SCHEDULER").
Origin string
}

// setLastRunParams pins the `status` and/or `origin` query parameters inherited by all calls on
// this client. Empty values are skipped so they leave the corresponding filter unset.
func (c *RunClient) setLastRunParams(options LastRunOptions) {
if options.Status != "" {
c.ctx.baseParams.addRaw("status", options.Status)
}
if options.Origin != "" {
c.ctx.baseParams.addRaw("origin", options.Origin)
}
}

// Get fetches the run object. The bool reports whether it exists.
Expand Down
13 changes: 10 additions & 3 deletions task.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,18 @@ func (c *TaskClient) UpdateInput(ctx context.Context, input any) (json.RawMessag

// LastRun returns a client for the last run of this task, optionally filtered by status
// (e.g. "SUCCEEDED"). Pass an empty status for no filter.
//
// To also filter by run origin, use LastRunWithOptions.
func (c *TaskClient) LastRun(status string) *RunClient {
return c.LastRunWithOptions(LastRunOptions{Status: status})
}

// LastRunWithOptions returns a client for the last run of this task, optionally filtered by
// status and/or origin. See LastRunOptions. Mirrors the reference client's
// lastRun({ status, origin }).
func (c *TaskClient) LastRunWithOptions(options LastRunOptions) *RunClient {
client := newRunClient(c.root, c.ctx.http, c.ctx.subURL(""), "runs", "last")
if status != "" {
client.setStatusParam(status)
}
client.setLastRunParams(options)
return client
}

Expand Down
11 changes: 11 additions & 0 deletions tests/actor_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,15 @@ func TestLastRunAccess(t *testing.T) {
if lastRun.Status != "SUCCEEDED" {
t.Fatalf("expected last succeeded run, got %q", lastRun.Status)
}

// The run was started via the API, so filtering the last run by both status and origin
// must still resolve it. This exercises the origin filter on LastRunWithOptions.
lastRunByOrigin, ok, err := client.Actor("apify/hello-world").
LastRunWithOptions(apify.LastRunOptions{Status: "SUCCEEDED", Origin: "API"}).Get(ctx)
if err != nil || !ok {
t.Fatalf("last run by origin: ok=%v err=%v", ok, err)
}
if lastRunByOrigin.Status != "SUCCEEDED" {
t.Fatalf("expected last succeeded run filtered by origin, got %q", lastRunByOrigin.Status)
}
}
4 changes: 2 additions & 2 deletions version.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ package apify
//
// It follows Semantic Versioning (https://semver.org/). Changes to the public
// interface (other than additive ones) are considered breaking changes.
const CLIENT_VERSION = "0.3.0"
const CLIENT_VERSION = "0.4.0"

// API_SPEC_VERSION is the version of the Apify OpenAPI specification that this
// client was generated and verified against.
//
// It corresponds to the `info.version` field of the Apify OpenAPI document.
const API_SPEC_VERSION = "v2-2026-06-24T105326Z"
const API_SPEC_VERSION = "v2-2026-06-25T142310Z"
Loading