Skip to content

Commit

Permalink
fix: aborting of last Actor/task run (#192)
Browse files Browse the repository at this point in the history
## Problem description

### Aborting the last Actor run does not work

- Resulting in:
```
apify_client._errors.ApifyApiError: We have bad news: there is no API endpoint at this URL. 
Did you specify it correctly?
```

- Code to reproduce it:

```python
# sync version
from apify_client import ApifyClient

TOKEN = '...'
ACTOR_ID = '...'

def actor_run_abort(apify_client: ApifyClient, actor_id: str) -> None:
    actor_client = apify_client.actor(actor_id)
    actor_client.call(wait_secs=1)
    last_run = actor_client.last_run(status='RUNNING', origin='API')
    aborted_info = last_run.abort()
    print(f'aborted_info: {aborted_info}')
    
if __name__ == '__main__':
    apify_client = ApifyClient(TOKEN)
    actor_run_abort(apify_client, ACTOR_ID)   
```

```python
# async version
import asyncio
from apify_client import ApifyClientAsync

TOKEN = '...'
ACTOR_ID = '...'

async def actor_run_abort_async(apify_client: ApifyClientAsync, actor_id: str) -> None:
    actor_client = apify_client.actor(actor_id)
    await actor_client.call(wait_secs=1)
    last_run = await actor_client.last_run(status='RUNNING', origin='API')
    aborted_info = await last_run.abort()
    print(f'aborted_info: {aborted_info}')

if __name__ == '__main__':
    apify_client = ApifyClientAsync(TOKEN)
    asyncio.run(actor_run_abort_async(apify_client, ACTOR_ID))
```

### Aborting of the last task run does not work

- Resulting in:
```
apify_client._errors.ApifyApiError: We have bad news: there is no API endpoint at this URL. 
Did you specify it correctly?
```

- Code to reproduce it:

```python
# sync version
from apify_client import ApifyClient

TOKEN = '...'
TASK_ID = '...'

def task_run_abort(apify_client: ApifyClient, task_id: str) -> None:
    task_client = apify_client.task(task_id)
    task_client.call(wait_secs=1)
    last_run = task_client.last_run(status='RUNNING', origin='API')
    aborted_info = last_run.abort()
    print(f'aborted_info: {aborted_info}')

if __name__ == '__main__':
    apify_client = ApifyClient(TOKEN)
    task_run_abort(apify_client, TASK_ID)

```

```python
# async version
import asyncio
from apify_client import ApifyClientAsync

TOKEN = '...'
TASK_ID = '...'

async def task_run_abort_async(apify_client: ApifyClientAsync, task_id: str) -> None:
    task_client = apify_client.task(task_id)
    await task_client.call(wait_secs=1)
    last_run = await task_client.last_run(status='RUNNING', origin='API')
    aborted_info = await last_run.abort()
    print(f'aborted_info: {aborted_info}')

if __name__ == '__main__':
    apify_client = ApifyClientAsync(TOKEN)
    asyncio.run(task_run_abort_async(apify_client, TASK_ID))
```

### Related issues

- The aborting of the last task run was reported in
#190. The aborting of
the last Actor run does not work as well as was described above.

### Solution

- Using additional API call to be able to call
`https://api.apify.com/v2/acts/{actorId}/runs/{runId}/abort` in both
cases.
- In the async version it results in `last_run` method being `async`
(should not be released as patch version).
- Copied from source code (Actor run):

```python
# Note:
# The API does not provide a direct endpoint for aborting the last Actor run using a URL like:
# https://api.apify.com/v2/acts/{actor_id}/runs/last/abort
# To achieve this, we need to implement a workaround using the following URL format:
# https://api.apify.com/v2/acts/{actorId}/runs/{runId}/abort
```

- Copied from source code (task run):

```python
# Note:
# The API does not provide a direct endpoint for aborting the last task run using a URL like:
# https://api.apify.com/v2/actor-tasks/{task_id}/runs/last/abort
# To achieve this, we need to implement a workaround using the following URL format:
# https://api.apify.com/v2/acts/{actorId}/runs/{runId}/abort
```

### Testing

- As we do not have a proper testing framework here, it was tested just
manually with the code examples provided at the beginning.
  • Loading branch information
vdusek authored May 20, 2024
1 parent 87b64bf commit 186afe7
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 9 deletions.
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# Changelog

## [1.6.5](../../releases/tag/v1.6.5) - Not released yet
## [1.7.0](../../releases/tag/v1.7.0) - Not released yet

...
### Fixed

- fix abort of last task run
- fix abort of last Actor run

## [1.6.4](../../releases/tag/v1.6.4) - 2024-02-27

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "apify_client"
version = "1.6.5"
version = "1.7.0"
description = "Apify API client for Python"
readme = "README.md"
license = { text = "Apache Software License" }
Expand Down
48 changes: 45 additions & 3 deletions src/apify_client/clients/resource_clients/actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,13 @@ def last_run(
Returns:
RunClient: The resource client for the last run of this actor.
"""
return RunClient(
# Note:
# The API does not provide a direct endpoint for aborting the last Actor run using a URL like:
# https://api.apify.com/v2/acts/{actor_id}/runs/last/abort
# To achieve this, we need to implement a workaround using the following URL format:
# https://api.apify.com/v2/acts/{actorId}/runs/{runId}/abort

last_run_client = RunClient(
**self._sub_resource_init_options(
resource_id='last',
resource_path='runs',
Expand All @@ -355,6 +361,21 @@ def last_run(
)
)

last_run_client_info = last_run_client.get()
actor_id = last_run_client_info['actId'] # type: ignore
actor_run_id = last_run_client_info['id'] # type: ignore

return RunClient(
**self._sub_resource_init_options(
base_url='https://api.apify.com/v2',
resource_path=f'acts/{actor_id}/runs/{actor_run_id}',
params=self._params(
status=maybe_extract_enum_member_value(status),
origin=maybe_extract_enum_member_value(origin),
),
)
)

def versions(self: ActorClient) -> ActorVersionCollectionClient:
"""Retrieve a client for the versions of this actor."""
return ActorVersionCollectionClient(**self._sub_resource_init_options())
Expand Down Expand Up @@ -634,7 +655,7 @@ def runs(self: ActorClientAsync) -> RunCollectionClientAsync:
"""Retrieve a client for the runs of this actor."""
return RunCollectionClientAsync(**self._sub_resource_init_options(resource_path='runs'))

def last_run(self: ActorClientAsync, *, status: ActorJobStatus | None = None, origin: MetaOrigin | None = None) -> RunClientAsync:
async def last_run(self: ActorClientAsync, *, status: ActorJobStatus | None = None, origin: MetaOrigin | None = None) -> RunClientAsync:
"""Retrieve the client for the last run of this actor.
Last run is retrieved based on the start time of the runs.
Expand All @@ -646,7 +667,13 @@ def last_run(self: ActorClientAsync, *, status: ActorJobStatus | None = None, or
Returns:
RunClientAsync: The resource client for the last run of this actor.
"""
return RunClientAsync(
# Note:
# The API does not provide a direct endpoint for aborting the last Actor run using a URL like:
# https://api.apify.com/v2/acts/{actor_id}/runs/last/abort
# To achieve this, we need to implement a workaround using the following URL format:
# https://api.apify.com/v2/acts/{actorId}/runs/{runId}/abort

last_run_client = RunClientAsync(
**self._sub_resource_init_options(
resource_id='last',
resource_path='runs',
Expand All @@ -657,6 +684,21 @@ def last_run(self: ActorClientAsync, *, status: ActorJobStatus | None = None, or
)
)

last_run_client_info = await last_run_client.get()
actor_id = last_run_client_info['actId'] # type: ignore
actor_run_id = last_run_client_info['id'] # type: ignore

return RunClientAsync(
**self._sub_resource_init_options(
base_url='https://api.apify.com/v2',
resource_path=f'acts/{actor_id}/runs/{actor_run_id}',
params=self._params(
status=maybe_extract_enum_member_value(status),
origin=maybe_extract_enum_member_value(origin),
),
)
)

def versions(self: ActorClientAsync) -> ActorVersionCollectionClientAsync:
"""Retrieve a client for the versions of this actor."""
return ActorVersionCollectionClientAsync(**self._sub_resource_init_options())
Expand Down
48 changes: 45 additions & 3 deletions src/apify_client/clients/resource_clients/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,13 @@ def last_run(self: TaskClient, *, status: ActorJobStatus | None = None, origin:
Returns:
RunClient: The resource client for the last run of this task.
"""
return RunClient(
# Note:
# The API does not provide a direct endpoint for aborting the last task run using a URL like:
# https://api.apify.com/v2/actor-tasks/{task_id}/runs/last/abort
# To achieve this, we need to implement a workaround using the following URL format:
# https://api.apify.com/v2/acts/{actorId}/runs/{runId}/abort

last_run_client = RunClient(
**self._sub_resource_init_options(
resource_id='last',
resource_path='runs',
Expand All @@ -277,6 +283,21 @@ def last_run(self: TaskClient, *, status: ActorJobStatus | None = None, origin:
)
)

last_run_client_info = last_run_client.get()
actor_id = last_run_client_info['actId'] # type: ignore
actor_run_id = last_run_client_info['id'] # type: ignore

return RunClient(
**self._sub_resource_init_options(
base_url='https://api.apify.com/v2',
resource_path=f'acts/{actor_id}/runs/{actor_run_id}',
params=self._params(
status=maybe_extract_enum_member_value(status),
origin=maybe_extract_enum_member_value(origin),
),
)
)

def webhooks(self: TaskClient) -> WebhookCollectionClient:
"""Retrieve a client for webhooks associated with this task."""
return WebhookCollectionClient(**self._sub_resource_init_options())
Expand Down Expand Up @@ -491,7 +512,7 @@ def runs(self: TaskClientAsync) -> RunCollectionClientAsync:
"""Retrieve a client for the runs of this task."""
return RunCollectionClientAsync(**self._sub_resource_init_options(resource_path='runs'))

def last_run(self: TaskClientAsync, *, status: ActorJobStatus | None = None, origin: MetaOrigin | None = None) -> RunClientAsync:
async def last_run(self: TaskClientAsync, *, status: ActorJobStatus | None = None, origin: MetaOrigin | None = None) -> RunClientAsync:
"""Retrieve the client for the last run of this task.
Last run is retrieved based on the start time of the runs.
Expand All @@ -503,7 +524,13 @@ def last_run(self: TaskClientAsync, *, status: ActorJobStatus | None = None, ori
Returns:
RunClientAsync: The resource client for the last run of this task.
"""
return RunClientAsync(
# Note:
# The API does not provide a direct endpoint for aborting the last task run using a URL like:
# https://api.apify.com/v2/actor-tasks/{task_id}/runs/last/abort
# To achieve this, we need to implement a workaround using the following URL format:
# https://api.apify.com/v2/acts/{actorId}/runs/{runId}/abort

last_run_client = RunClientAsync(
**self._sub_resource_init_options(
resource_id='last',
resource_path='runs',
Expand All @@ -514,6 +541,21 @@ def last_run(self: TaskClientAsync, *, status: ActorJobStatus | None = None, ori
)
)

last_run_client_info = await last_run_client.get()
actor_id = last_run_client_info['actId'] # type: ignore
actor_run_id = last_run_client_info['id'] # type: ignore

return RunClientAsync(
**self._sub_resource_init_options(
base_url='https://api.apify.com/v2',
resource_path=f'acts/{actor_id}/runs/{actor_run_id}',
params=self._params(
status=maybe_extract_enum_member_value(status),
origin=maybe_extract_enum_member_value(origin),
),
)
)

def webhooks(self: TaskClientAsync) -> WebhookCollectionClientAsync:
"""Retrieve a client for webhooks associated with this task."""
return WebhookCollectionClientAsync(**self._sub_resource_init_options())

0 comments on commit 186afe7

Please sign in to comment.