Skip to content

Commit baa2565

Browse files
Python (feat): Sift client jobs resource (#458)
1 parent feb1fe7 commit baa2565

14 files changed

Lines changed: 1453 additions & 3 deletions

File tree

python/lib/sift_client/_internal/low_level_wrappers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
)
55
from sift_client._internal.low_level_wrappers.channels import ChannelsLowLevelClient
66
from sift_client._internal.low_level_wrappers.ingestion import IngestionLowLevelClient
7+
from sift_client._internal.low_level_wrappers.jobs import JobsLowLevelClient
78
from sift_client._internal.low_level_wrappers.ping import PingLowLevelClient
89
from sift_client._internal.low_level_wrappers.remote_files import RemoteFilesLowLevelClient
910
from sift_client._internal.low_level_wrappers.reports import ReportsLowLevelClient
@@ -18,6 +19,7 @@
1819
"CalculatedChannelsLowLevelClient",
1920
"ChannelsLowLevelClient",
2021
"IngestionLowLevelClient",
22+
"JobsLowLevelClient",
2123
"PingLowLevelClient",
2224
"RemoteFilesLowLevelClient",
2325
"ReportsLowLevelClient",
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
"""Low-level wrapper for the JobService API.
2+
3+
This module provides thin wrappers around the autogenerated bindings for the JobService API.
4+
It handles common concerns like error handling and pagination.
5+
6+
It provides an asynchronous client for the JobService API.
7+
"""
8+
9+
from __future__ import annotations
10+
11+
from typing import Any, cast
12+
13+
from sift.jobs.v1.jobs_pb2 import (
14+
CancelJobRequest,
15+
ListJobsRequest,
16+
ListJobsResponse,
17+
RetryJobRequest,
18+
RetryJobResponse,
19+
)
20+
from sift.jobs.v1.jobs_pb2_grpc import JobServiceStub
21+
22+
from sift_client._internal.low_level_wrappers.base import LowLevelClientBase
23+
from sift_client.sift_types.job import Job
24+
from sift_client.transport import GrpcClient, WithGrpcClient
25+
26+
27+
class JobsLowLevelClient(LowLevelClientBase, WithGrpcClient):
28+
"""Low-level client for the JobService API.
29+
30+
This class provides a thin wrapper around the autogenerated bindings for the JobService API.
31+
"""
32+
33+
def __init__(self, grpc_client: GrpcClient):
34+
"""Initialize the JobsLowLevelClient.
35+
36+
Args:
37+
grpc_client: The gRPC client to use for making API calls.
38+
"""
39+
super().__init__(grpc_client)
40+
41+
async def list_jobs(
42+
self,
43+
*,
44+
page_size: int | None = None,
45+
page_token: str | None = None,
46+
query_filter: str | None = None,
47+
organization_id: str | None = None,
48+
order_by: str | None = None,
49+
) -> tuple[list[Job], str]:
50+
"""List jobs with pagination.
51+
52+
Args:
53+
page_size: Maximum number of jobs to return.
54+
page_token: Page token from a previous list call.
55+
query_filter: CEL filter string.
56+
organization_id: Organization ID (required if user belongs to multiple orgs).
57+
order_by: How to order results.
58+
59+
Returns:
60+
The ListJobsResponse containing jobs and next page token.
61+
"""
62+
request_kwargs: dict[str, Any] = {}
63+
if page_size is not None:
64+
request_kwargs["page_size"] = page_size
65+
if page_token is not None:
66+
request_kwargs["page_token"] = page_token
67+
if query_filter is not None:
68+
request_kwargs["filter"] = query_filter
69+
if organization_id is not None:
70+
request_kwargs["organization_id"] = organization_id
71+
if order_by is not None:
72+
request_kwargs["order_by"] = order_by
73+
74+
request = ListJobsRequest(**request_kwargs)
75+
response = await self._grpc_client.get_stub(JobServiceStub).ListJobs(request)
76+
response = cast("ListJobsResponse", response)
77+
jobs = [Job._from_proto(job) for job in response.jobs]
78+
return jobs, response.next_page_token
79+
80+
async def list_all_jobs(
81+
self,
82+
*,
83+
query_filter: str | None = None,
84+
organization_id: str | None = None,
85+
order_by: str | None = None,
86+
max_results: int | None = None,
87+
) -> list[Job]:
88+
"""List all jobs, handling pagination automatically.
89+
90+
Args:
91+
query_filter: CEL filter string.
92+
organization_id: Organization ID (required if user belongs to multiple orgs).
93+
order_by: How to order results.
94+
max_results: Maximum number of results to return across all pages.
95+
96+
Returns:
97+
A list of Job objects.
98+
"""
99+
kwargs: dict[str, Any] = {}
100+
if query_filter is not None:
101+
kwargs["query_filter"] = query_filter
102+
if organization_id is not None:
103+
kwargs["organization_id"] = organization_id
104+
105+
jobs = await self._handle_pagination(
106+
func=self.list_jobs,
107+
kwargs=kwargs,
108+
order_by=order_by,
109+
max_results=max_results,
110+
)
111+
112+
return jobs
113+
114+
async def get_job(self, job_id: str) -> Job:
115+
"""Get a job by ID.
116+
117+
Args:
118+
job_id: The ID of the job to retrieve.
119+
120+
Returns:
121+
The Job object.
122+
"""
123+
jobs = await self.list_all_jobs(
124+
query_filter=f'job_id == "{job_id}"',
125+
max_results=1,
126+
)
127+
if not jobs:
128+
raise ValueError(f"Job not found: {job_id}")
129+
return jobs[0]
130+
131+
async def cancel_job(self, job_id: str) -> None:
132+
"""Cancel a job.
133+
134+
Args:
135+
job_id: The ID of the job to cancel.
136+
"""
137+
request = CancelJobRequest(job_id=job_id)
138+
await self._grpc_client.get_stub(JobServiceStub).CancelJob(request)
139+
140+
async def retry_job(self, job_id: str) -> Job:
141+
"""Retry a job.
142+
143+
Args:
144+
job_id: The ID of the job to retry.
145+
146+
Returns:
147+
The updated Job object.
148+
"""
149+
request = RetryJobRequest(job_id=job_id)
150+
response = await self._grpc_client.get_stub(JobServiceStub).RetryJob(request)
151+
response = cast("RetryJobResponse", response)
152+
return Job._from_proto(response.job)

python/lib/sift_client/_tests/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ def mock_client():
4848
client.tags = MagicMock()
4949
client.test_results = MagicMock()
5050
client.file_attachments = MagicMock()
51+
client.jobs = MagicMock()
5152
client.async_ = MagicMock(spec=AsyncAPIs)
5253
client.async_.ingestion = MagicMock()
5354
return client

0 commit comments

Comments
 (0)