Skip to content

Commit

Permalink
Merge pull request #1166 from alicejgibbons/release-1.15
Browse files Browse the repository at this point in the history
Dotnet Jobs SDK qs
  • Loading branch information
rochabr authored Feb 27, 2025
2 parents 7b4c935 + 04e9c2a commit e87a963
Show file tree
Hide file tree
Showing 10 changed files with 554 additions and 0 deletions.
185 changes: 185 additions & 0 deletions jobs/csharp/sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Dapr Jobs API (SDK)

In this quickstart, you'll schedule, get, and delete a job using Dapr's Job API. This API is responsible for scheduling and running jobs at a specific time or interval.

Visit [this](https://docs.dapr.io/developing-applications/building-blocks/jobs/) link for more information about Dapr and the Jobs API.

> **Note:** This example leverages the Dotnet SDK. If you are looking for the example using only HTTP requests, [click here](../http/).
This quickstart includes two apps:

- Jobs Scheduler, responsible for scheduling, retrieving and deleting jobs.
- Jobs Service, responsible for handling the triggered jobs.

## Run all apps with multi-app run template file

This section shows how to run both applications at once using [multi-app run template files](https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-overview/) with `dapr run -f .`. This enables to you test the interactions between multiple applications and will `schedule`, `run`, `get`, and `delete` jobs within a single process.

1. Build the apps:

<!-- STEP
name: Build dependencies for job-service
sleep: 1
-->

```bash
cd ./job-service
dotnet build
```

<!-- END_STEP -->

<!-- STEP
name: Build dependencies for job-scheduler
sleep: 1
-->

```bash
cd ./job-scheduler
dotnet build
```

<!-- END_STEP -->

2. Run the multi app run template:

<!-- STEP
name: Run multi app run template
expected_stdout_lines:
- '== APP - job-service-sdk == Job Scheduled: R2-D2'
- '== APP - job-service-sdk == Job Scheduled: C-3PO'
- '== APP - job-service-sdk == Starting droid: R2-D2'
- '== APP - job-service-sdk == Executing maintenance job: Oil Change'
- '== APP - job-service-sdk == Starting droid: C-3PO'
- '== APP - job-service-sdk == Executing maintenance job: Limb Calibration'
expected_stderr_lines:
output_match_mode: substring
match_order: none
background: true
sleep: 60
timeout_seconds: 120
-->

```bash
dapr run -f .
```

The terminal console output should look similar to this, where:

- The `R2-D2` job is being scheduled.
- The `R2-D2` job is being retrieved.
- The `C-3PO` job is being scheduled.
- The `C-3PO` job is being retrieved.
- The `R2-D2` job is being executed after 15 seconds.
- The `C-3PO` job is being executed after 20 seconds.

```text
== APP - job-scheduler-sdk == Scheduling job...
== APP - job-service-sdk == Job Scheduled: R2-D2
== APP - job-scheduler-sdk == Job scheduled: {"name":"R2-D2","job":"Oil Change","dueTime":15}
== APP - job-scheduler-sdk == Getting job: R2-D2
== APP - job-service-sdk == Getting job...
== APP - job-scheduler-sdk == Job details: {"schedule":"@every 15s","repeatCount":1,"dueTime":null,"ttl":null,"payload":"ChtkYXByLmlvL3NjaGVkdWxlL2pvYnBheWxvYWQSJXsiZHJvaWQiOiJSMi1EMiIsInRhc2siOiJPaWwgQ2hhbmdlIn0="}
== APP - job-scheduler-sdk == Scheduling job...
== APP - job-service-sdk == Job Scheduled: C-3PO
== APP - job-scheduler-sdk == Job scheduled: {"name":"C-3PO","job":"Limb Calibration","dueTime":20}
== APP - job-scheduler-sdk == Getting job: C-3PO
== APP - job-service-sdk == Getting job...
== APP - job-scheduler-sdk == Job details: {"schedule":"@every 20s","repeatCount":1,"dueTime":null,"ttl":null,"payload":"ChtkYXByLmlvL3NjaGVkdWxlL2pvYnBheWxvYWQSK3siZHJvaWQiOiJDLTNQTyIsInRhc2siOiJMaW1iIENhbGlicmF0aW9uIn0="}
== APP - job-service-sdk == Handling job...
== APP - job-service-sdk == Starting droid: R2-D2
== APP - job-service-sdk == Executing maintenance job: Oil Change
```

After 20 seconds, the terminal output should present the `C-3PO` job being processed:

```text
== APP - job-service-sdk == Handling job...
== APP - job-service-sdk == Starting droid: C-3PO
== APP - job-service-sdk == Executing maintenance job: Limb Calibration
```

<!-- END_STEP -->

## Run apps individually

### Schedule Jobs

1. Open a terminal and run the `job-service` app. Build the dependencies if you haven't already.

```bash
cd ./job-service
dotnet build
```

```bash
dapr run --app-id job-service-sdk --app-port 6200 --dapr-http-port 6280 -- dotnet run
```

2. In a new terminal window, schedule the `R2-D2` Job using the Jobs API.

```bash
curl -X POST \
http://localhost:6200/scheduleJob \
-H "Content-Type: application/json" \
-d '{
"name": "R2-D2",
"job": "Oil Change",
"dueTime": 2
}'
```

In the `job-service` terminal window, the output should be:

```text
== APP - job-app == Received job request...
== APP - job-app == Starting droid: R2-D2
== APP - job-app == Executing maintenance job: Oil Change
```

3. On the same terminal window, schedule the `C-3PO` Job using the Jobs API.

```bash
curl -X POST \
http://localhost:6200/scheduleJob \
-H "Content-Type: application/json" \
-d '{
"name": "C-3PO",
"job": "Limb Calibration",
"dueTime": 30
}'
```

### Get a scheduled job

1. On the same terminal window, run the command below to get the recently scheduled `C-3PO` job.

```bash
curl -X GET http://localhost:6200/getJob/C-3PO -H "Content-Type: application/json"
```

You should see the following:

```text
{"name":"c-3po", "dueTime":"30s", "data":{"@type":"type.googleapis.com/google.protobuf.Value", "value":{"Value":"C-3PO:Limb Calibration"}}}
```

### Delete a scheduled job

1. On the same terminal window, run the command below to deleted the recently scheduled `C-3PO` job.

```bash
curl -X DELETE http://localhost:6200/deleteJob/C-3PO -H "Content-Type: application/json"
```

2. Run the command below to attempt to retrieve the deleted job:

```bash
curl -X GET http://localhost:6200/getJob/C-3PO -H "Content-Type: application/json"
```

In the `job-service` terminal window, the output should be similar to the following:

```text
ERRO[0157] Error getting job C-3PO due to: rpc error: code = NotFound desc = job not found: C-3PO instance=local scope=dapr.api type=log ver=1.15.0
```
16 changes: 16 additions & 0 deletions jobs/csharp/sdk/dapr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: 1
apps:
- appDirPath: ./job-service/
appID: job-service-sdk
appPort: 6200
daprHTTPPort: 6280
command: ["dotnet", "run"]
appLogDestination: console
daprdLogDestination: console
- appDirPath: ./job-scheduler/
appID: job-scheduler-sdk
appPort: 6300
daprHTTPPort: 6380
command: ["dotnet", "run"]
appLogDestination: console
daprdLogDestination: console
103 changes: 103 additions & 0 deletions jobs/csharp/sdk/job-scheduler/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#pragma warning disable CS0618 // Type or member is obsolete

using System.Text.Json.Serialization;
using Dapr.Client;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

await Task.Delay(5000); // Allow time for the job-service-sdk to start

// Instantiate an HTTP client for invoking the job-service-sdk application
var httpClient = DaprClient.CreateInvokeHttpClient(appId: "job-service-sdk");

// Job details
var r2d2Job = new DroidJob
{
Name = "R2-D2",
Job = "Oil Change",
DueTime = 15
};

var c3poJob = new DroidJob
{
Name = "C-3PO",
Job = "Limb Calibration",
DueTime = 20
};

await Task.Delay(50);

try
{
// Schedule R2-D2 job
await ScheduleJob(r2d2Job);
await Task.Delay(5000);
// Get R2-D2 job details
await GetJobDetails(r2d2Job);

// Schedule C-3PO job
await ScheduleJob(c3poJob);
await Task.Delay(5000);
// Get C-3PO job details
await GetJobDetails(c3poJob);

await Task.Delay(30000); // Allow time for jobs to complete
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error: {ex.Message}");
Environment.Exit(1);
}

async Task ScheduleJob(DroidJob job)
{
Console.WriteLine($"Scheduling job...");

try
{
var response = await httpClient.PostAsJsonAsync("/scheduleJob", job);
var result = await response.Content.ReadAsStringAsync();

response.EnsureSuccessStatusCode();
Console.WriteLine($"Job scheduled: {result}");
}
catch (Exception e)
{
Console.WriteLine($"Error scheduling job: " + e);
}
}

async Task GetJobDetails(DroidJob job)
{
Console.WriteLine($"Getting job: " + job.Name);

try
{
var response = await httpClient.GetAsync($"/getJob/{job.Name}");
var jobDetails = await response.Content.ReadAsStringAsync();

response.EnsureSuccessStatusCode();
Console.WriteLine($"Job details: {jobDetails}");
}
catch (Exception e)
{
Console.WriteLine($"Error getting job: " + e);
}
}

await app.RunAsync();

public class DroidJob
{
[JsonPropertyName("name")]
public string? Name { get; set; }

[JsonPropertyName("job")]
public string? Job { get; set; }

[JsonPropertyName("dueTime")]
public int DueTime { get; set; }
}

#pragma warning restore CS0618 // Type or member is obsolete
16 changes: 16 additions & 0 deletions jobs/csharp/sdk/job-scheduler/jobs-scheduler.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>jobs_scheduler</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Dapr.Client" Version="1.15.0-rc07" />
<PackageReference Include="Dapr.Jobs" Version="1.15.0-rc07" />
</ItemGroup>

</Project>
Loading

0 comments on commit e87a963

Please sign in to comment.