Skip to content

Commit 0b7cf74

Browse files
Adds examples
Signed-off-by: Elena Kolevska <[email protected]>
1 parent bd04980 commit 0b7cf74

File tree

3 files changed

+213
-0
lines changed

3 files changed

+213
-0
lines changed

examples/jobs/README.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Example - Jobs API
2+
3+
This example demonstrates the [Jobs API](https://docs.dapr.io/developing-applications/building-blocks/scheduler/) in Dapr.
4+
It demonstrates the following APIs:
5+
- **schedule_job_alpha1**: Schedule a job to run at specified times
6+
- **get_job_alpha1**: Retrieve details about a scheduled job
7+
- **delete_job_alpha1**: Delete a scheduled job
8+
9+
It creates a client using `DaprClient` and calls all the Jobs API methods available as example.
10+
11+
> **Note:** The Jobs API is currently in Alpha and subject to change. Make sure to use the latest proto bindings.
12+
13+
## Prerequisites
14+
15+
- [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started)
16+
- [Install Python 3.9+](https://www.python.org/downloads/)
17+
- Dapr runtime v1.15+ (Jobs API support)
18+
19+
## Install Dapr python-SDK
20+
21+
<!-- Our CI/CD pipeline automatically installs the correct version, so we can skip this step in the automation -->
22+
23+
```bash
24+
pip3 install dapr dapr-ext-grpc
25+
```
26+
27+
## Run the example
28+
29+
To run this example, the following code can be utilized:
30+
31+
<!-- STEP
32+
name: Run jobs example
33+
expected_stdout_lines:
34+
- "== APP == 0. Scheduling a simple job without data..."
35+
- "== APP == ✓ Simple job scheduled successfully"
36+
- "== APP == 1. Scheduling a recurring job with cron schedule..."
37+
- "== APP == ✓ Recurring job scheduled successfully"
38+
- "== APP == 2. Scheduling a one-time job with due_time..."
39+
- "== APP == ✓ One-time job scheduled successfully"
40+
- "== APP == 3. Getting job details..."
41+
- "== APP == ✓ Retrieved job details:"
42+
- "== APP == 4. Cleaning up - deleting jobs..."
43+
- "== APP == ✓ Deleted job: simple-job"
44+
- "== APP == ✓ Deleted job: recurring-hello-job"
45+
- "== APP == ✓ Deleted job: one-time-hello-job"
46+
timeout_seconds: 10
47+
-->
48+
49+
```bash
50+
dapr run --app-id jobs-example -- python3 simple_job.py
51+
```
52+
53+
<!-- END_STEP -->
54+
55+
The output should be as follows:
56+
57+
```
58+
0. Scheduling a simple job without data...
59+
✓ Simple job scheduled successfully
60+
1. Scheduling a recurring job with cron schedule...
61+
✓ Recurring job scheduled successfully
62+
2. Scheduling a one-time job with due_time...
63+
✓ One-time job scheduled successfully
64+
3. Getting job details...
65+
✓ Retrieved job details:
66+
- Name: recurring-hello-job
67+
- Schedule: @every 30s
68+
- TTL: 5m
69+
- Data: {'message': 'Hello from recurring job!'}
70+
4. Cleaning up - deleting jobs...
71+
✓ Deleted job: simple-job
72+
✓ Deleted job: recurring-hello-job
73+
✓ Deleted job: one-time-hello-job
74+
```
75+
76+
## Job Scheduling Features
77+
78+
### Schedule Formats
79+
80+
The Jobs API supports multiple schedule formats:
81+
82+
**Cron Expressions (systemd timer style)**
83+
- `"0 30 * * * *"` - Every hour on the half hour
84+
- `"0 15 3 * * *"` - Every day at 03:15
85+
86+
**Human-readable Period Strings**
87+
- `"@every 1h30m"` - Every 1 hour and 30 minutes
88+
- `"@yearly"` or `"@annually"` - Once a year, midnight, Jan. 1st
89+
- `"@monthly"` - Once a month, midnight, first of month
90+
- `"@weekly"` - Once a week, midnight on Sunday
91+
- `"@daily"` or `"@midnight"` - Once a day, midnight
92+
- `"@hourly"` - Once an hour, beginning of hour
93+
94+
### Job Properties
95+
96+
- **name**: Unique identifier for the job
97+
- **schedule**: Cron expression or period string (optional if due_time is provided)
98+
- **due_time**: Specific time for one-shot execution (optional if schedule is provided)
99+
- **repeats**: Number of times to repeat the job (optional)
100+
- **ttl**: Time-to-live for the job (optional)
101+
- **data**: Payload data to send when the job is triggered (optional, empty Any proto used if not provided)
102+
- **overwrite**: If true, allows this job to overwrite an existing job with the same name (default: false)
103+
104+
105+
## Additional Information
106+
107+
- The Jobs API is currently in **Alpha** and subject to change
108+
- Jobs are persistent and will survive Dapr sidecar restarts
109+
- Job names must be unique within the Dapr application
110+
- Both `schedule` and `due_time` are optional - if neither is provided, the job will trigger immediately
111+
- Requires Dapr runtime v1.14+ for Jobs API support
112+
113+
For more information about the Jobs API, see:
114+
- [Dapr Scheduler Building Block](https://docs.dapr.io/developing-applications/building-blocks/scheduler/)
115+
- [Dapr Jobs API Proposal](https://github.com/dapr/proposals/blob/main/0012-BIRS-distributed-scheduler.md)

examples/jobs/simple_job.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import json
2+
from datetime import datetime, timedelta
3+
4+
from dapr.clients import DaprClient, Job
5+
from google.protobuf.any_pb2 import Any as GrpcAny
6+
7+
8+
def create_job_data(message: str):
9+
"""Helper function to create job payload data."""
10+
data = GrpcAny()
11+
data.value = json.dumps({"message": message}).encode('utf-8')
12+
return data
13+
14+
15+
def main():
16+
with DaprClient() as client:
17+
# Example 0: Simple job without data (works without protobuf)
18+
print("0. Scheduling a simple job without data...", flush=True)
19+
simple_job = Job(
20+
name="simple-job",
21+
schedule="@every 30s",
22+
overwrite=True
23+
)
24+
25+
try:
26+
client.schedule_job_alpha1(simple_job)
27+
print(f"✓ Simple job scheduled successfully", flush=True)
28+
except Exception as e:
29+
print(f"✗ Failed to schedule simple job: {e}", flush=True)
30+
return
31+
32+
# Example 1: Schedule a recurring job with cron schedule
33+
print("1. Scheduling a recurring job with cron schedule...", flush=True)
34+
job_data = create_job_data("Hello from recurring job!")
35+
recurring_job = Job(
36+
name="recurring-hello-job",
37+
schedule="@every 30s",
38+
data=job_data,
39+
ttl="5m",
40+
overwrite=True
41+
)
42+
43+
try:
44+
client.schedule_job_alpha1(recurring_job)
45+
print(f"✓ Recurring job scheduled successfully", flush=True)
46+
except Exception as e:
47+
print(f"✗ Failed to schedule recurring job: {e}", flush=True)
48+
return
49+
50+
# Example 2: Schedule a one-time job with due_time
51+
print("\n2. Scheduling a one-time job with due_time...", flush=True)
52+
due_time = (datetime.now() + timedelta(seconds=10)).isoformat() + "Z"
53+
one_time_job = Job(
54+
name="one-time-hello-job",
55+
due_time=due_time,
56+
data=create_job_data("Hello from one-time job!")
57+
)
58+
59+
try:
60+
client.schedule_job_alpha1(one_time_job)
61+
print(f"✓ One-time job scheduled successfully", flush=True)
62+
except Exception as e:
63+
print(f"✗ Failed to schedule one-time job: {e}", flush=True)
64+
return
65+
66+
# Example 3: Get job details
67+
print("\n3. Getting job details...", flush=True)
68+
try:
69+
job = client.get_job_alpha1("recurring-hello-job")
70+
print(f"✓ Retrieved job details:", flush=True)
71+
print(f" - Name: {job.name}", flush=True)
72+
print(f" - Schedule: {job.schedule}", flush=True)
73+
print(f" - TTL: {job.ttl}", flush=True)
74+
if job.data:
75+
try:
76+
payload = json.loads(job.data.value.decode('utf-8'))
77+
print(f" - Data: {payload}", flush=True)
78+
except Exception:
79+
print(f" - Data: <binary data, {len(job.data.value)} bytes>", flush=True)
80+
else:
81+
print(f" - Data: None", flush=True)
82+
except Exception as e:
83+
print(f"✗ Failed to get job details: {e}", flush=True)
84+
85+
# Example 4: Delete jobs
86+
print("\n4. Cleaning up - deleting jobs...", flush=True)
87+
for job_name in ["simple-job", "recurring-hello-job", "one-time-hello-job"]:
88+
try:
89+
client.delete_job_alpha1(job_name)
90+
print(f"✓ Deleted job: {job_name}", flush=True)
91+
except Exception as e:
92+
print(f"✗ Failed to delete job {job_name}: {e}", flush=True)
93+
94+
95+
96+
if __name__ == "__main__":
97+
main()

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ commands =
6666
./validate.sh configuration
6767
./validate.sh demo_workflow
6868
./validate.sh workflow
69+
./validate.sh jobs
6970
./validate.sh ../
7071
commands_pre =
7172
pip3 install -e {toxinidir}/

0 commit comments

Comments
 (0)