Skip to content
Open
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
24 changes: 6 additions & 18 deletions jobs/csharp/http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,13 @@ 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-scheduler == Job Scheduled: R2-D2'
- '== APP - job-scheduler == Job Scheduled: C-3PO'
- '== APP - job-service == Job Scheduled: R2-D2'
- '== APP - job-service == Job Scheduled: C-3PO'
- '== APP - job-service == Received job request...'
- '== APP - job-service == Starting droid: R2-D2'
- '== APP - job-service == Executing maintenance job: Oil Change'
Expand Down Expand Up @@ -76,10 +64,10 @@ The terminal console output should look similar to this, where:
- The `C-3PO` job is being executed after 20 seconds.

```text
== APP - job-scheduler == Job Scheduled: R2-D2
== APP - job-scheduler == Job details: {"name":"R2-D2", "dueTime":"15s", "data":{"@type":"type.googleapis.com/google.protobuf.Value", "value":{"Value":"R2-D2:Oil Change"}}}
== APP - job-scheduler == Job Scheduled: C-3PO
== APP - job-scheduler == Job details: {"name":"C-3PO", "dueTime":"20s", "data":{"@type":"type.googleapis.com/google.protobuf.Value", "value":{"Value":"C-3PO:Limb Calibration"}}}
== APP - job-service == Job Scheduled: R2-D2
== APP - job-service == Job details: {"name":"R2-D2", "dueTime":"15s", "data":{"@type":"type.googleapis.com/google.protobuf.Value", "value":{"Value":"R2-D2:Oil Change"}}}
== APP - job-service == Job Scheduled: C-3PO
== APP - job-service == Job details: {"name":"C-3PO", "dueTime":"20s", "data":{"@type":"type.googleapis.com/google.protobuf.Value", "value":{"Value":"C-3PO:Limb Calibration"}}}
== APP - job-service == Received job request...
== APP - job-service == Starting droid: R2-D2
== APP - job-service == Executing maintenance job: Oil Change
Expand Down
5 changes: 0 additions & 5 deletions jobs/csharp/http/dapr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,4 @@ apps:
appPort: 6200
daprHTTPPort: 6280
schedulerHostAddress: localhost
command: ["dotnet", "run"]
- appDirPath: ./job-scheduler/
appID: job-scheduler
appPort: 6300
daprHTTPPort: 6380
command: ["dotnet", "run"]
78 changes: 0 additions & 78 deletions jobs/csharp/http/job-scheduler/Program.cs

This file was deleted.

11 changes: 0 additions & 11 deletions jobs/csharp/http/job-scheduler/jobs-scheduler.csproj

This file was deleted.

110 changes: 91 additions & 19 deletions jobs/csharp/http/job-service/Program.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using System.Net;
using System.Text;
using Microsoft.AspNetCore.Http.Json;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Text.Json;

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel((context, serverOptions) =>
{
serverOptions.Listen(IPAddress.Any, 6200);
});
builder.Services.Configure<JsonOptions>(options =>
{
options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
builder.Services.AddHostedService<DroidWorkService>();

var app = builder.Build();
var appPort = Environment.GetEnvironmentVariable("APP_PORT") ?? "6200";
app.UseRouting();

//Job handler route
app.MapPost("/job/{*path}", async (HttpRequest request, HttpResponse response) =>
Expand Down Expand Up @@ -45,7 +48,8 @@
});

// Start the server
app.Run($"http://localhost:{appPort}");
app.Run();
return;

static DroidJob SetDroidJob(string droidStr)
{
Expand All @@ -55,21 +59,89 @@ static DroidJob SetDroidJob(string droidStr)
throw new Exception("Invalid droid job format. Expected format: 'Droid:Task'");
}

return new DroidJob
{
Droid = parts[0],
Task = parts[1]
};
return new DroidJob(parts[0], parts[1]);
}

// Classes for request and response models
public class JobData
internal sealed class DroidWorkService : IHostedService
{
public string? Value { get; set; }
}
private readonly string _daprHost = Environment.GetEnvironmentVariable("DAPR_HOST") ?? "http://localhost";
private readonly string _appPort = Environment.GetEnvironmentVariable("APP_PORT") ?? "6280";

/// <summary>
/// Triggered when the application host is ready to start the service.
/// </summary>
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous Start operation.</returns>
public async Task StartAsync(CancellationToken cancellationToken)
{
// Job request bodies
var c3poJobBody = new
{
data = new { Value = "C-3PO:Limb Calibration" },
dueTime = "20s"
};

public class DroidJob
{
public string? Droid { get; set; }
public string? Task { get; set; }
var r2d2JobBody = new
{
data = new { Value = "R2-D2:Oil Change" },
dueTime = "15s"
};

// Schedule the R2-D2 job
await ScheduleJob("R2-D2", r2d2JobBody);
await Task.Delay(5000, cancellationToken);
// Get the R2-D2 job details
await GetJobDetails("R2-D2");

// Schedule C-3PC job
await ScheduleJob("C-3PO", c3poJobBody);
await Task.Delay(5000, cancellationToken);
// Get C-3PO job details
await GetJobDetails("C-3PO");
await Task.Delay(30000, cancellationToken);
}

private async Task ScheduleJob(string jobName, object jobBody)
{
var reqUrl = $"{_daprHost}:{_appPort}/v1.0-alpha1/jobs/{jobName}";
var jsonBody = JsonSerializer.Serialize(jobBody);
var content = new StringContent(jsonBody, Encoding.UTF8, "application/json");

var httpClient = new HttpClient();
var response = await httpClient.PostAsync(reqUrl, content);

if (response.StatusCode != System.Net.HttpStatusCode.NoContent)
{
throw new Exception($"Failed to register job event handler. Status code: {response.StatusCode}");
}

Console.WriteLine($"Job Scheduled: {jobName}");
}

private async Task GetJobDetails(string jobName)
{
var reqUrl = $"{_daprHost}:{_appPort}/v1.0-alpha1/jobs/{jobName}";
var httpClient = new HttpClient();
var response = await httpClient.GetAsync(reqUrl);

if (!response.IsSuccessStatusCode)
{
throw new Exception($"HTTP error! Status: {response.StatusCode}");
}

var jobDetails = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Job details: {jobDetails}");
}

/// <summary>
/// Triggered when the application host is performing a graceful shutdown.
/// </summary>
/// <param name="cancellationToken">Indicates that the shutdown process should no longer be graceful.</param>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /> that represents the asynchronous Stop operation.</returns>
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

// Classes for request and response models
internal sealed record JobData(string? Value = null);

internal sealed record DroidJob(string Droid, string Task);
Loading