mirror of https://github.com/dapr/quickstarts.git
Merge branch 'release-1.15' into release/bump-go
This commit is contained in:
commit
ca4e6562b3
|
|
@ -0,0 +1,3 @@
|
|||
* text=auto eol=lf
|
||||
*.{cmd,[cC][mM][dD]} text eol=crlf
|
||||
*.{bat,[bB][aA][tT]} text eol=crlf
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
DAPR_CLI_VERSION: 1.14.1
|
||||
DAPR_RUNTIME_VERSION: 1.15.0-rc.2
|
||||
DAPR_CLI_VERSION: 1.15.0-rc.5
|
||||
DAPR_RUNTIME_VERSION: 1.15.0-rc.11
|
||||
DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/v${DAPR_CLI_VERSION}/install/
|
||||
DAPR_DEFAULT_IMAGE_REGISTRY: ghcr
|
||||
|
||||
|
|
@ -11,4 +11,4 @@ GOVER: 1.22
|
|||
|
||||
KUBERNETES_VERSION: v1.32.0
|
||||
KIND_VERSION: v0.26.0
|
||||
KIND_IMAGE_SHA: sha256:c48c62eac5da28cdadcf560d1d8616cfa6783b58f0d94cf63ad1bf49600cb027
|
||||
KIND_IMAGE_SHA: sha256:c48c62eac5da28cdadcf560d1d8616cfa6783b58f0d94cf63ad1bf49600cb027
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ Let's take a look at the Dapr [Actors building block](https://docs.dapr.io/devel
|
|||
For this example, you will need:
|
||||
|
||||
- [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started).
|
||||
- [.NET 7 SDK](https://dotnet.microsoft.com/download).
|
||||
- [.NET 8 SDK](https://dotnet.microsoft.com/download).
|
||||
- Docker Desktop
|
||||
|
||||
### Step 2: Set up the environment
|
||||
|
|
@ -34,7 +34,7 @@ Run the `SmartDevice.Service`, which will start service itself and the Dapr side
|
|||
<!-- STEP
|
||||
name: Run actor service
|
||||
expected_stdout_lines:
|
||||
- "Request finished HTTP/1.1 GET http://127.0.0.1:5001/healthz - 200"
|
||||
- "Request finished HTTP/1.1 GET http://127.0.0.1:5001/dapr/config - 200"
|
||||
expected_stderr_lines:
|
||||
working_dir: .
|
||||
output_match_mode: substring
|
||||
|
|
@ -50,14 +50,23 @@ dapr run --app-id actorservice --app-port 5001 --app-protocol http --dapr-http-p
|
|||
Expected output:
|
||||
|
||||
```bash
|
||||
== APP == info: Microsoft.Hosting.Lifetime[14]
|
||||
== APP == Now listening on: http://localhost:5001
|
||||
== APP == info: Microsoft.Hosting.Lifetime[0]
|
||||
== APP == Application started. Press Ctrl+C to shut down.
|
||||
== APP == info: Microsoft.Hosting.Lifetime[0]
|
||||
== APP == Hosting environment: Production
|
||||
== APP == info: Microsoft.Hosting.Lifetime[0]
|
||||
== APP == info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
|
||||
== APP == Request starting HTTP/1.1 GET http://127.0.0.1:5001/healthz - -
|
||||
== APP == Request starting HTTP/1.1 GET http://127.0.0.1:5001/dapr/config - application/json -
|
||||
== APP == warn: Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware[3]
|
||||
== APP == Failed to determine the https port for redirect.
|
||||
== APP == info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
|
||||
== APP == Executing endpoint 'Dapr Actors Health Check'
|
||||
== APP == Executing endpoint 'Dapr Actors Config'
|
||||
== APP == info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
|
||||
== APP == Executed endpoint 'Dapr Actors Health Check'
|
||||
== APP == Executed endpoint 'Dapr Actors Config'
|
||||
== APP == info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
|
||||
== APP == Request finished HTTP/1.1 GET http://127.0.0.1:5001/healthz - - - 200 - text/plain 5.2599ms
|
||||
== APP == Request finished HTTP/1.1 GET http://127.0.0.1:5001/dapr/config - 200 - application/json 30.6175ms
|
||||
```
|
||||
|
||||
### Step 4: Run the client app
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dapr.Actors" Version="1.14.*-*" />
|
||||
<PackageReference Include="Dapr.Actors" Version="1.15.0-rc02" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dapr.Actors" Version="1.14.*-*" />
|
||||
<PackageReference Include="Dapr.Actors.AspNetCore" Version="1.14.*-*" />
|
||||
<PackageReference Include="Dapr.Actors" Version="1.15.0-rc02" />
|
||||
<PackageReference Include="Dapr.Actors.AspNetCore" Version="1.15.0-rc02" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dapr.AspNetCore" Version="1.14.*-*" />
|
||||
<PackageReference Include="Dapr.AspNetCore" Version="1.15.0-rc02" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -31,6 +31,8 @@ builder.Services.Configure<RequestLocalizationOptions>(options =>
|
|||
options.SupportedCultures = [invariantCulture];
|
||||
});
|
||||
|
||||
builder.Services.AddDaprClient();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); }
|
||||
|
|
@ -38,13 +40,12 @@ if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); }
|
|||
app.UseRequestLocalization();
|
||||
|
||||
// Triggered by Dapr input binding
|
||||
app.MapPost("/" + cronBindingName, async () =>
|
||||
app.MapPost("/" + cronBindingName, async (DaprClient client) =>
|
||||
{
|
||||
Console.WriteLine("Processing batch..");
|
||||
|
||||
string jsonFile = File.ReadAllText("../../../orders.json");
|
||||
var ordersArray = JsonSerializer.Deserialize<Orders>(jsonFile);
|
||||
using var client = new DaprClientBuilder().Build();
|
||||
foreach (Order ord in ordersArray?.orders ?? new Order[] { })
|
||||
{
|
||||
var sqlText = $"insert into orders (orderid, customer, price) values ({ord.OrderId}, '{ord.Customer}', {ord.Price});";
|
||||
|
|
@ -65,5 +66,5 @@ app.MapPost("/" + cronBindingName, async () =>
|
|||
|
||||
await app.RunAsync();
|
||||
|
||||
public record Order([property: JsonPropertyName("orderid")] int OrderId, [property: JsonPropertyName("customer")] string Customer, [property: JsonPropertyName("price")] float Price);
|
||||
public record Orders([property: JsonPropertyName("orders")] Order[] orders);
|
||||
public sealed record Order([property: JsonPropertyName("orderid")] int OrderId, [property: JsonPropertyName("customer")] string Customer, [property: JsonPropertyName("price")] float Price);
|
||||
public sealed record Orders([property: JsonPropertyName("orders")] Order[] orders);
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
dapr>=1.14.0a,<1.15.0
|
||||
dapr>=1.15.0rc2
|
||||
Flask
|
||||
typing-extensions
|
||||
typing-extensions
|
||||
werkzeug>=3.0.3 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
requests
|
||||
flask
|
||||
flask
|
||||
urllib3>=2.2.2 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
dapr>=1.14.0a,<1.15.0
|
||||
dapr>=1.15.0rc2
|
||||
typing-extensions
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: echo
|
||||
spec:
|
||||
type: conversation.echo
|
||||
version: v1
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
# Dapr Conversation API (C# HTTP)
|
||||
|
||||
In this quickstart, you'll send an input to a mock Large Language Model (LLM) using Dapr's Conversation API. This API is responsible for providing one consistent API entry point to talk to underlying LLM providers.
|
||||
|
||||
Visit [this](https://v1-15.docs.dapr.io/developing-applications/building-blocks/conversation/conversation-overview/) link for more information about Dapr and the Conversation API.
|
||||
|
||||
> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/).
|
||||
|
||||
This quickstart includes one app:
|
||||
|
||||
- Conversation, responsible for sending an input to the underlying LLM and retrieving an output.
|
||||
|
||||
## Run the app with the template file
|
||||
|
||||
This section shows how to run the application using the [multi-app run template file](https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-overview/) and Dapr CLI with `dapr run -f .`.
|
||||
|
||||
This example uses the default LLM Component provided by Dapr which simply echoes the input provided, for testing purposes. Integrate with popular LLM models by using one of the other [supported conversation components](https://v1-15.docs.dapr.io/reference/components-reference/supported-conversation/).
|
||||
|
||||
Open a new terminal window and run the multi app run template:
|
||||
|
||||
<!-- STEP
|
||||
name: Run multi app run template
|
||||
expected_stdout_lines:
|
||||
- '== APP - conversation == Input sent: What is dapr?'
|
||||
- '== APP - conversation == Output response: What is dapr?'
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
background: true
|
||||
sleep: 15
|
||||
timeout_seconds: 15
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr run -f .
|
||||
```
|
||||
|
||||
The terminal console output should look similar to this, where:
|
||||
|
||||
- The app sends an input `What is dapr?` to the `echo` Component mock LLM.
|
||||
- The mock LLM echoes `What is dapr?`.
|
||||
|
||||
```text
|
||||
== APP - conversation == Input sent: What is dapr?
|
||||
== APP - conversation == Output response: What is dapr?
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
2. Stop and clean up application processes.
|
||||
|
||||
<!-- STEP
|
||||
name: Stop multi-app run
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr stop -f .
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Run the app individually
|
||||
|
||||
1. Open a terminal and run the `conversation` app. Build the dependencies if you haven't already.
|
||||
|
||||
```bash
|
||||
cd ./conversation
|
||||
dotnet build
|
||||
```
|
||||
|
||||
2. Run the Dapr process alongside the application.
|
||||
|
||||
```bash
|
||||
dapr run --app-id conversation --resources-path ../../../components/ -- dotnet run
|
||||
```
|
||||
|
||||
The terminal console output should look similar to this, where:
|
||||
|
||||
- The app sends an input `What is dapr?` to the `echo` Component mock LLM.
|
||||
- The mock LLM echoes `What is dapr?`.
|
||||
|
||||
```text
|
||||
== APP - conversation == Input sent: What is dapr?
|
||||
== APP - conversation == Output response: What is dapr?
|
||||
```
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
Copyright 2024 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Text;
|
||||
|
||||
class Program
|
||||
{
|
||||
private const string ConversationComponentName = "echo";
|
||||
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
var daprHost = Environment.GetEnvironmentVariable("DAPR_HOST") ?? "http://localhost";
|
||||
var daprHttpPort = Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500";
|
||||
|
||||
var client = new HttpClient
|
||||
{
|
||||
Timeout = TimeSpan.FromSeconds(15)
|
||||
};
|
||||
|
||||
var inputBody = new
|
||||
{
|
||||
name = "echo",
|
||||
inputs = new[] { new { message = "What is dapr?" } },
|
||||
parameters = new { },
|
||||
metadata = new { }
|
||||
};
|
||||
|
||||
var daprUrl = $"{daprHost}:{daprHttpPort}/v1.0-alpha1/conversation/{ConversationComponentName}/converse";
|
||||
|
||||
try
|
||||
{
|
||||
var content = new StringContent(JsonSerializer.Serialize(inputBody), Encoding.UTF8, "application/json");
|
||||
|
||||
// Send a request to the echo mock LLM component
|
||||
var response = await client.PostAsync(daprUrl, content);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
Console.WriteLine("Input sent: " + inputBody.inputs[0].message);
|
||||
|
||||
var responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// Parse the response
|
||||
var data = JsonSerializer.Deserialize<Dictionary<string, List<Dictionary<string, string>>>>(responseBody);
|
||||
var result = data?["outputs"]?[0]?["result"];
|
||||
|
||||
Console.WriteLine("Output response: " + result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error: " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Text.Json" Version="9.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
version: 1
|
||||
common:
|
||||
resourcesPath: ../../components/
|
||||
apps:
|
||||
- appDirPath: ./conversation/
|
||||
appID: conversation
|
||||
daprHTTPPort: 3500
|
||||
command: ["dotnet", "run"]
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
# Dapr Conversation API (C# SDK)
|
||||
|
||||
In this quickstart, you'll send an input to a mock Large Language Model (LLM) using Dapr's Conversation API. This API is responsible for providing one consistent API entry point to talk to underlying LLM providers.
|
||||
|
||||
Visit [this](https://v1-15.docs.dapr.io/developing-applications/building-blocks/conversation/conversation-overview/) link for more information about Dapr and the Conversation API.
|
||||
|
||||
> **Note:** This example leverages the Dapr SDK. If you are looking for the example using the HTTP API [click here](../http/).
|
||||
|
||||
This quickstart includes one app:
|
||||
|
||||
- Conversation, responsible for sending an input to the underlying LLM and retrieving an output.
|
||||
|
||||
## Run the app with the template file
|
||||
|
||||
This section shows how to run the application using the [multi-app run template file](https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-overview/) and Dapr CLI with `dapr run -f .`.
|
||||
|
||||
This example uses the default LLM Component provided by Dapr which simply echoes the input provided, for testing purposes. Integrate with popular LLM models by using one of the other [supported conversation components](https://v1-15.docs.dapr.io/reference/components-reference/supported-conversation/).
|
||||
|
||||
Open a new terminal window and run the multi app run template:
|
||||
|
||||
<!-- STEP
|
||||
name: Run multi app run template
|
||||
expected_stdout_lines:
|
||||
- '== APP - conversation == Input sent: What is dapr?'
|
||||
- '== APP - conversation == Output response: What is dapr?'
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
background: true
|
||||
sleep: 15
|
||||
timeout_seconds: 15
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr run -f .
|
||||
```
|
||||
|
||||
The terminal console output should look similar to this, where:
|
||||
|
||||
- The app sends an input `What is dapr?` to the `echo` Component mock LLM.
|
||||
- The mock LLM echoes `What is dapr?`.
|
||||
|
||||
```text
|
||||
== APP - conversation == Input sent: What is dapr?
|
||||
== APP - conversation == Output response: What is dapr?
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
2. Stop and clean up application processes.
|
||||
|
||||
<!-- STEP
|
||||
name: Stop multi-app run
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr stop -f .
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Run the app individually
|
||||
|
||||
1. Open a terminal and run the `conversation` app. Build the dependencies if you haven't already.
|
||||
|
||||
```bash
|
||||
cd ./conversation
|
||||
dotnet build
|
||||
```
|
||||
|
||||
2. Run the Dapr process alongside the application.
|
||||
|
||||
```bash
|
||||
dapr run --app-id conversation --resources-path ../../../components/ -- dotnet run
|
||||
```
|
||||
|
||||
The terminal console output should look similar to this, where:
|
||||
|
||||
- The app sends an input `What is dapr?` to the `echo` Component mock LLM.
|
||||
- The mock LLM echoes `What is dapr?`.
|
||||
|
||||
```text
|
||||
== APP - conversation == Input sent: What is dapr?
|
||||
== APP - conversation == Output response: What is dapr?
|
||||
```
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
Copyright 2024 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
using Dapr.AI.Conversation;
|
||||
using Dapr.AI.Conversation.Extensions;
|
||||
|
||||
class Program
|
||||
{
|
||||
private const string ConversationComponentName = "echo";
|
||||
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
const string prompt = "What is dapr?";
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Services.AddDaprConversationClient();
|
||||
var app = builder.Build();
|
||||
|
||||
//Instantiate Dapr Conversation Client
|
||||
var conversationClient = app.Services.GetRequiredService<DaprConversationClient>();
|
||||
|
||||
try
|
||||
{
|
||||
// Send a request to the echo mock LLM component
|
||||
var response = await conversationClient.ConverseAsync(ConversationComponentName, [new(prompt, DaprConversationRole.Generic)]);
|
||||
Console.WriteLine("Input sent: " + prompt);
|
||||
|
||||
if (response != null)
|
||||
{
|
||||
Console.Write("Output response:");
|
||||
foreach (var resp in response.Outputs)
|
||||
{
|
||||
Console.WriteLine($" {resp.Result}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error: " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dapr.AI" Version="1.15.0-rc02" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
version: 1
|
||||
common:
|
||||
resourcesPath: ../../components/
|
||||
apps:
|
||||
- appDirPath: ./conversation/
|
||||
appID: conversation
|
||||
daprHTTPPort: 3500
|
||||
command: ["dotnet", "run"]
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# Dapr Conversation API (Go HTTP)
|
||||
|
||||
In this quickstart, you'll send an input to a mock Large Language Model (LLM) using Dapr's Conversation API. This API is responsible for providing one consistent API entry point to talk to underlying LLM providers.
|
||||
|
||||
Visit [this](https://v1-15.docs.dapr.io/developing-applications/building-blocks/conversation/conversation-overview/) link for more information about Dapr and the Conversation API.
|
||||
|
||||
> **Note:** This example leverages HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/).
|
||||
|
||||
This quickstart includes one app:
|
||||
|
||||
- `conversation.go`, responsible for sending an input to the underlying LLM and retrieving an output.
|
||||
|
||||
## Run the app with the template file
|
||||
|
||||
This section shows how to run the application using the [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 example uses the default LLM Component provided by Dapr which simply echoes the input provided, for testing purposes. Here are other [supported Conversation components](https://v1-15.docs.dapr.io/reference/components-reference/supported-conversation/).
|
||||
|
||||
Open a new terminal window and run the multi app run template:
|
||||
|
||||
<!-- STEP
|
||||
name: Run multi app run template
|
||||
expected_stdout_lines:
|
||||
- '== APP - conversation == Input sent: What is dapr?'
|
||||
- '== APP - conversation == Output response: What is dapr?'
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
background: false
|
||||
sleep: 15
|
||||
timeout_seconds: 30
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr run -f .
|
||||
```
|
||||
|
||||
The terminal console output should look similar to this, where:
|
||||
|
||||
- The app sends an input `What is dapr?` to the `echo` Component mock LLM.
|
||||
- The mock LLM echoes `What is dapr?`.
|
||||
|
||||
```text
|
||||
== APP - conversation == Input sent: What is dapr?
|
||||
== APP - conversation == Output response: What is dapr?
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
2. Stop and clean up application processes.
|
||||
|
||||
<!-- STEP
|
||||
name: Stop multi-app run
|
||||
sleep: 5
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr stop -f .
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
Copyright 2024 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const conversationComponentName = "echo"
|
||||
|
||||
func main() {
|
||||
daprHost := os.Getenv("DAPR_HOST")
|
||||
if daprHost == "" {
|
||||
daprHost = "http://localhost"
|
||||
}
|
||||
daprHttpPort := os.Getenv("DAPR_HTTP_PORT")
|
||||
if daprHttpPort == "" {
|
||||
daprHttpPort = "3500"
|
||||
}
|
||||
|
||||
client := http.Client{
|
||||
Timeout: 15 * time.Second,
|
||||
}
|
||||
|
||||
var inputBody = `{
|
||||
"name": "echo",
|
||||
"inputs": [{"message":"What is dapr?"}],
|
||||
"parameters": {},
|
||||
"metadata": {}
|
||||
}`
|
||||
|
||||
reqURL := daprHost + ":" + daprHttpPort + "/v1.0-alpha1/conversation/" + conversationComponentName + "/converse"
|
||||
|
||||
req, err := http.NewRequest("POST", reqURL, strings.NewReader(inputBody))
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// Send a request to the echo LLM component
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
fmt.Println("Input sent: What is dapr?")
|
||||
|
||||
bodyBytes, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Unmarshal the response
|
||||
var data map[string][]map[string]string
|
||||
if err := json.Unmarshal(bodyBytes, &data); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
result := data["outputs"][0]["result"]
|
||||
fmt.Println("Output response:", result)
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
module conversation
|
||||
|
||||
go 1.23.5
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
version: 1
|
||||
common:
|
||||
resourcesPath: ../../components/
|
||||
apps:
|
||||
- appDirPath: ./conversation/
|
||||
appID: conversation
|
||||
daprHTTPPort: 3501
|
||||
command: ["go", "run", "."]
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# Dapr Conversation API (Go SDK)
|
||||
|
||||
In this quickstart, you'll send an input to a mock Large Language Model (LLM) using Dapr's Conversation API. This API is responsible for providing one consistent API entry point to talk to underlying LLM providers.
|
||||
|
||||
Visit [this](https://v1-15.docs.dapr.io/developing-applications/building-blocks/conversation/conversation-overview/) link for more information about Dapr and the Conversation API.
|
||||
|
||||
> **Note:** This example leverages the Dapr SDK. If you are looking for the example using the HTTP API [click here](../http/).
|
||||
|
||||
This quickstart includes one app:
|
||||
|
||||
- `conversation.go`, responsible for sending an input to the underlying LLM and retrieving an output.
|
||||
|
||||
## Run the app with the template file
|
||||
|
||||
This section shows how to run the application using the [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 example uses the default LLM Component provided by Dapr which simply echoes the input provided, for testing purposes. Here are other [supported Conversation components](https://v1-15.docs.dapr.io/reference/components-reference/supported-conversation/).
|
||||
|
||||
Open a new terminal window and run the multi app run template:
|
||||
|
||||
<!-- STEP
|
||||
name: Run multi app run template
|
||||
expected_stdout_lines:
|
||||
- '== APP - conversation == Input sent: What is dapr?'
|
||||
- '== APP - conversation == Output response: What is dapr?'
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
background: false
|
||||
sleep: 15
|
||||
timeout_seconds: 30
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr run -f .
|
||||
```
|
||||
|
||||
The terminal console output should look similar to this, where:
|
||||
|
||||
- The app sends an input `What is dapr?` to the `echo` Component mock LLM.
|
||||
- The mock LLM echoes `What is dapr?`.
|
||||
|
||||
```text
|
||||
== APP - conversation == Input sent: What is dapr?
|
||||
== APP - conversation == Output response: What is dapr?
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
2. Stop and clean up application processes.
|
||||
|
||||
<!-- STEP
|
||||
name: Stop multi-app run
|
||||
sleep: 5
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr stop -f .
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
Copyright 2024 The Dapr Authors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
dapr "github.com/dapr/go-sdk/client"
|
||||
)
|
||||
|
||||
func main() {
|
||||
client, err := dapr.NewClient()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
input := dapr.ConversationInput{
|
||||
Message: "What is dapr?",
|
||||
// Role: nil, // Optional
|
||||
// ScrubPII: nil, // Optional
|
||||
}
|
||||
|
||||
fmt.Println("Input sent:", input.Message)
|
||||
|
||||
var conversationComponent = "echo"
|
||||
|
||||
request := dapr.NewConversationRequest(conversationComponent, []dapr.ConversationInput{input})
|
||||
|
||||
resp, err := client.ConverseAlpha1(context.Background(), request)
|
||||
if err != nil {
|
||||
log.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println("Output response:", resp.Outputs[0].Result)
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
module conversation
|
||||
|
||||
go 1.23.5
|
||||
|
||||
require github.com/dapr/go-sdk v1.12.0-rc.1
|
||||
|
||||
require (
|
||||
github.com/dapr/dapr v1.15.0-rc.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
go.opentelemetry.io/otel v1.32.0 // indirect
|
||||
golang.org/x/net v0.32.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect
|
||||
google.golang.org/grpc v1.68.1 // indirect
|
||||
google.golang.org/protobuf v1.35.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/dapr/dapr v1.15.0-rc.1 h1:7JP3zSannxQwV27A9pPR2b/DSNmgcSjJOhRDwM4eFpQ=
|
||||
github.com/dapr/dapr v1.15.0-rc.1/go.mod h1:SycZrBWgfmog+C5T4p0X6VIpnREQ3xajrYxdih+gn9w=
|
||||
github.com/dapr/go-sdk v1.12.0-rc.1 h1:KK92BahLmwGowVRjFxsjySl25M6wwuJSjesYIIF6h0c=
|
||||
github.com/dapr/go-sdk v1.12.0-rc.1/go.mod h1:OxCF7Eh8IZvmNv6Euk+mnLrehJyLQRYb4TAU7uHq7Ow=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
||||
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
|
||||
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
|
||||
google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0=
|
||||
google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw=
|
||||
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
||||
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
version: 1
|
||||
common:
|
||||
resourcesPath: ../../components/
|
||||
apps:
|
||||
- appDirPath: ./conversation/
|
||||
appID: conversation
|
||||
command: ["go", "run", "."]
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
# Dapr Conversation API (JS HTTP)
|
||||
|
||||
In this quickstart, you'll send an input to a mock Large Language Model (LLM) using Dapr's Conversation API. This API is responsible for providing one consistent API entry point to talk to underlying LLM providers.
|
||||
|
||||
Visit [this](https://v1-15.docs.dapr.io/developing-applications/building-blocks/conversation/conversation-overview/) link for more information about Dapr and the Conversation API.
|
||||
|
||||
> **Note:** This example leverages HTTP `requests` only.
|
||||
|
||||
This quickstart includes one app:
|
||||
|
||||
- `index.js`, responsible for sending an input to the underlying LLM and retrieving an output.
|
||||
|
||||
## Run the app with the template file
|
||||
|
||||
This section shows how to run the application using the [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 example uses the default LLM Component provided by Dapr which simply echoes the input provided, for testing purposes. Here are other [supported Conversation components](https://v1-15.docs.dapr.io/reference/components-reference/supported-conversation/).
|
||||
|
||||
1. Install dependencies:
|
||||
|
||||
<!-- STEP
|
||||
name: Install Node dependencies for conversation
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./conversation
|
||||
npm install
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
2. Open a new terminal window and run the multi app run template:
|
||||
|
||||
<!-- STEP
|
||||
name: Run multi app run template
|
||||
expected_stdout_lines:
|
||||
- '== APP - conversation == Input sent: What is dapr?'
|
||||
- '== APP - conversation == Output response: What is dapr?'
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
background: true
|
||||
sleep: 15
|
||||
timeout_seconds: 30
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr run -f .
|
||||
```
|
||||
|
||||
The terminal console output should look similar to this, where:
|
||||
|
||||
- The app sends an input `What is dapr?` to the `echo` Component mock LLM.
|
||||
- The mock LLM echoes `What is dapr?`.
|
||||
|
||||
```text
|
||||
== APP - conversation == Input sent: What is dapr?
|
||||
== APP - conversation == Output response: What is dapr?
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
3. Stop and clean up application processes.
|
||||
|
||||
<!-- STEP
|
||||
name: Stop multi-app run
|
||||
sleep: 5
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr stop -f .
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Run the app individually
|
||||
|
||||
1. Open a terminal and navigate to the `conversation` app. Install the dependencies if you haven't already.
|
||||
|
||||
```bash
|
||||
cd ./conversation
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Run the Dapr process alongside the application.
|
||||
|
||||
```bash
|
||||
dapr run --app-id conversation --resources-path ../../../components/ -- npm run start
|
||||
```
|
||||
|
||||
The terminal console output should look similar to this, where:
|
||||
|
||||
- The app sends an input `What is dapr?` to the `echo` Component mock LLM.
|
||||
- The mock LLM echoes `What is dapr?`.
|
||||
|
||||
```text
|
||||
== APP - conversation == Input sent: What is dapr?
|
||||
== APP - conversation == Output response: What is dapr?
|
||||
```
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
const conversationComponentName = "echo";
|
||||
|
||||
async function main() {
|
||||
const daprHost = process.env.DAPR_HOST || "http://localhost";
|
||||
const daprHttpPort = process.env.DAPR_HTTP_PORT || "3500";
|
||||
|
||||
const inputBody = {
|
||||
name: "echo",
|
||||
inputs: [{ message: "What is dapr?" }],
|
||||
parameters: {},
|
||||
metadata: {},
|
||||
};
|
||||
|
||||
const reqURL = `${daprHost}:${daprHttpPort}/v1.0-alpha1/conversation/${conversationComponentName}/converse`;
|
||||
|
||||
try {
|
||||
const response = await fetch(reqURL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(inputBody),
|
||||
});
|
||||
|
||||
console.log("Input sent: What is dapr?");
|
||||
|
||||
const data = await response.json();
|
||||
const result = data.outputs[0].result;
|
||||
console.log("Output response:", result);
|
||||
} catch (error) {
|
||||
console.error("Error:", error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error("Unhandled error:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "conversation",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "conversation",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "conversation",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"start:dapr": "dapr run --app-id conversation --resources-path ../../../components/ -- npm start"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
version: 1
|
||||
common:
|
||||
resourcesPath: ../../components/
|
||||
apps:
|
||||
- appID: conversation
|
||||
appDirPath: ./conversation/
|
||||
daprHTTPPort: 3502
|
||||
command: ["npm", "run", "start"]
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
# Dapr Conversation API (Python HTTP)
|
||||
|
||||
In this quickstart, you'll send an input to a mock Large Language Model (LLM) using Dapr's Conversation API. This API is responsible for providing one consistent API entry point to talk to underlying LLM providers.
|
||||
|
||||
Visit [this](https://v1-15.docs.dapr.io/developing-applications/building-blocks/conversation/conversation-overview/) link for more information about Dapr and the Conversation API.
|
||||
|
||||
> **Note:** This example leverages HTTP `requests` only.
|
||||
|
||||
This quickstart includes one app:
|
||||
|
||||
- `app.py`, responsible for sending an input to the underlying LLM and retrieving an output.
|
||||
|
||||
## Run the app with the template file
|
||||
|
||||
This section shows how to run the application using the [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 example uses the default LLM Component provided by Dapr which simply echoes the input provided, for testing purposes. Here are other [supported Conversation components](https://v1-15.docs.dapr.io/reference/components-reference/supported-conversation/).
|
||||
|
||||
1. Install dependencies:
|
||||
|
||||
<!-- STEP
|
||||
name: Install Python dependencies
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./conversation
|
||||
pip3 install -r requirements.txt
|
||||
cd ..
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
2. Open a new terminal window and run the multi app run template:
|
||||
|
||||
<!-- STEP
|
||||
name: Run multi app run template
|
||||
expected_stdout_lines:
|
||||
- '== APP - conversation == INFO:root:Input sent: What is dapr?'
|
||||
- '== APP - conversation == INFO:root:Output response: What is dapr?'
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
background: true
|
||||
sleep: 15
|
||||
timeout_seconds: 30
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr run -f .
|
||||
```
|
||||
|
||||
The terminal console output should look similar to this, where:
|
||||
|
||||
- The app sends an input `What is dapr?` to the `echo` Component mock LLM.
|
||||
- The mock LLM echoes `What is dapr?`.
|
||||
|
||||
```text
|
||||
== APP - conversation == Input sent: What is dapr?
|
||||
== APP - conversation == Output response: What is dapr?
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
2. Stop and clean up application processes.
|
||||
|
||||
<!-- STEP
|
||||
name: Stop multi-app run
|
||||
sleep: 5
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr stop -f .
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Run the app with the Dapr CLI
|
||||
|
||||
1. Install dependencies:
|
||||
|
||||
Open a terminal and run:
|
||||
|
||||
```bash
|
||||
cd ./conversation
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
2. Run the application:
|
||||
|
||||
```bash
|
||||
dapr run --app-id conversation --resources-path ../../../components -- python3 app.py
|
||||
```
|
||||
|
||||
You should see the output:
|
||||
|
||||
```bash
|
||||
== APP == INFO:root:Input sent: What is dapr?
|
||||
== APP == INFO:root:Output response: What is dapr?
|
||||
```
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import logging
|
||||
import requests
|
||||
import os
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
base_url = os.getenv('BASE_URL', 'http://localhost') + ':' + os.getenv(
|
||||
'DAPR_HTTP_PORT', '3500')
|
||||
|
||||
CONVERSATION_COMPONENT_NAME = 'echo'
|
||||
|
||||
input = {
|
||||
'name': 'echo',
|
||||
'inputs': [{'message':'What is dapr?'}],
|
||||
'parameters': {},
|
||||
'metadata': {}
|
||||
}
|
||||
|
||||
# Send input to conversation endpoint
|
||||
result = requests.post(
|
||||
url='%s/v1.0-alpha1/conversation/%s/converse' % (base_url, CONVERSATION_COMPONENT_NAME),
|
||||
json=input
|
||||
)
|
||||
|
||||
logging.info('Input sent: What is dapr?')
|
||||
|
||||
# Parse conversation output
|
||||
data = result.json()
|
||||
output = data["outputs"][0]["result"]
|
||||
|
||||
logging.info('Output response: ' + output)
|
||||
|
|
@ -0,0 +1 @@
|
|||
requests
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
version: 1
|
||||
common:
|
||||
resourcesPath: ../../components/
|
||||
apps:
|
||||
- appID: conversation
|
||||
appDirPath: ./conversation/
|
||||
command: ["python3", "app.py"]
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
# Dapr cryptography (Dapr SDK)
|
||||
|
||||
In this quickstart, you'll create an application that encrypts, and then decrypts, data using the Dapr cryptography APIs (high-level). We will:
|
||||
|
||||
- Encrypt and then decrypt a short string, reading the result in-memory
|
||||
- Encrypt and then decrypt a large file, storing the encrypted and decrypted data to files
|
||||
|
||||
Visit the documentation to learn more about the [Cryptography building block](https://v1-11.docs.dapr.io/developing-applications/building-blocks/cryptography/) in Dapr.
|
||||
|
||||
> **Note:** This example uses the Dapr SDK. Using the Dapr SDK, which leverages gRPC internally, is **strongly** recommended when using the high-level cryptography APIs (to encrypt and decrypt messages).
|
||||
|
||||
This quickstart includes one application:
|
||||
|
||||
- Python application `crypto-quickstart`
|
||||
|
||||
### Run Python service with Dapr
|
||||
|
||||
> In order to run this sample, make sure that OpenSSL is available on your system.
|
||||
|
||||
1. Navigate into the folder with the source code:
|
||||
|
||||
<!-- STEP
|
||||
name: Navigate into folder
|
||||
expected_stdout_lines:
|
||||
expected_stderr_lines:
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./crypto-quickstart
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
2. This sample requires a private RSA key and a 256-bit symmetric (AES) key. We will generate them using OpenSSL:
|
||||
|
||||
<!-- STEP
|
||||
name: Generate keys
|
||||
working_dir: crypto-quickstart
|
||||
expected_stdout_lines:
|
||||
expected_stderr_lines:
|
||||
-->
|
||||
|
||||
```bash
|
||||
mkdir -p keys
|
||||
# Generate a private RSA key, 4096-bit keys
|
||||
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out keys/rsa-private-key.pem
|
||||
# Generate a 256-bit key for AES
|
||||
openssl rand -out keys/symmetric-key-256 32
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
3. Run the Python service app with Dapr:
|
||||
|
||||
<!-- STEP
|
||||
name: Run order-processor service
|
||||
working_dir: crypto-quickstart
|
||||
expected_stdout_lines:
|
||||
- '== APP == Encrypted the message, got 856 bytes'
|
||||
- '== APP == Decrypted the message, got 24 bytes'
|
||||
- '== APP == The secret is "passw0rd"'
|
||||
- '== APP == Wrote encrypted data to encrypted.out'
|
||||
- '== APP == Wrote decrypted data to decrypted.out.jpg'
|
||||
- "Exited App successfully"
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr run --app-id crypto-quickstart --resources-path ../../../components/ -- python3 app.py
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# Output files
|
||||
encrypted.out
|
||||
decrypted.out.jpg
|
||||
|
||||
# Generated keys
|
||||
keys/
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
from dapr.clients import DaprClient
|
||||
from dapr.clients.grpc._crypto import EncryptOptions, DecryptOptions
|
||||
|
||||
# Name of the crypto component to use
|
||||
CRYPTO_COMPONENT_NAME = 'localstorage'
|
||||
# Name of the RSA private key to use
|
||||
RSA_KEY_NAME = 'rsa-private-key.pem'
|
||||
# Name of the symmetric (AES) key to use
|
||||
SYMMETRIC_KEY_NAME = 'symmetric-key-256'
|
||||
|
||||
|
||||
def main():
|
||||
print('Running gRPC client synchronous API')
|
||||
|
||||
with DaprClient() as dapr:
|
||||
# Step 1: encrypt a string using the RSA key, then decrypt it and show the output in the terminal
|
||||
print('Running encrypt/decrypt operation on string')
|
||||
encrypt_decrypt_string(dapr)
|
||||
|
||||
# Step 2: encrypt a large file and then decrypt it, using the AES key
|
||||
print('Running encrypt/decrypt operation on file')
|
||||
encrypt_decrypt_file(dapr)
|
||||
|
||||
|
||||
def encrypt_decrypt_string(dapr: DaprClient):
|
||||
message = 'The secret is "passw0rd"'
|
||||
|
||||
# Encrypt the message
|
||||
resp = dapr.encrypt(
|
||||
data=message.encode(),
|
||||
options=EncryptOptions(
|
||||
component_name=CRYPTO_COMPONENT_NAME,
|
||||
key_name=RSA_KEY_NAME,
|
||||
key_wrap_algorithm='RSA',
|
||||
),
|
||||
)
|
||||
|
||||
# The method returns a readable stream, which we read in full in memory
|
||||
encrypt_bytes = resp.read()
|
||||
print(f'Encrypted the message, got {len(encrypt_bytes)} bytes')
|
||||
|
||||
# Decrypt the encrypted data
|
||||
resp = dapr.decrypt(
|
||||
data=encrypt_bytes,
|
||||
options=DecryptOptions(
|
||||
component_name=CRYPTO_COMPONENT_NAME,
|
||||
key_name=RSA_KEY_NAME,
|
||||
),
|
||||
)
|
||||
|
||||
# The method returns a readable stream, which we read in full in memory
|
||||
decrypt_bytes = resp.read()
|
||||
print(f'Decrypted the message, got {len(decrypt_bytes)} bytes')
|
||||
|
||||
print(decrypt_bytes.decode())
|
||||
assert message == decrypt_bytes.decode()
|
||||
|
||||
|
||||
def encrypt_decrypt_file(dapr: DaprClient):
|
||||
file_name = 'desert.jpg'
|
||||
|
||||
# Encrypt the file
|
||||
with open(file_name, 'r+b') as target_file:
|
||||
encrypt_stream = dapr.encrypt(
|
||||
data=target_file.read(),
|
||||
options=EncryptOptions(
|
||||
component_name=CRYPTO_COMPONENT_NAME,
|
||||
key_name=SYMMETRIC_KEY_NAME,
|
||||
key_wrap_algorithm='AES',
|
||||
),
|
||||
)
|
||||
|
||||
# Write the encrypted data to a file "encrypted.out"
|
||||
with open('encrypted.out', 'w+b') as encrypted_file:
|
||||
encrypted_file.write(encrypt_stream.read())
|
||||
print('Wrote encrypted data to encrypted.out')
|
||||
|
||||
# Decrypt the encrypted data
|
||||
with open('encrypted.out', 'r+b') as encrypted_file:
|
||||
decrypt_stream = dapr.decrypt(
|
||||
data=encrypted_file.read(),
|
||||
options=DecryptOptions(
|
||||
component_name=CRYPTO_COMPONENT_NAME,
|
||||
key_name=SYMMETRIC_KEY_NAME,
|
||||
),
|
||||
)
|
||||
|
||||
# Write the decrypted data to a file "decrypted.out.jpg"
|
||||
with open('decrypted.out.jpg', 'w+b') as decrypted_file:
|
||||
decrypted_file.write(decrypt_stream.read())
|
||||
print('Wrote decrypted data to decrypted.out.jpg')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.7 MiB |
|
|
@ -0,0 +1,2 @@
|
|||
dapr>=1.14
|
||||
typing-extensions
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
||||
# Remove generated files
|
||||
.PHONY: clean
|
||||
clean:
|
||||
-rm -r crypto-quickstart/keys
|
||||
-rm crypto-quickstart/encrypted.out
|
||||
-rm crypto-quickstart/decrypted.out.jpg
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
# Dapr Jobs API (HTTP Client)
|
||||
|
||||
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 HTTP requests only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/).
|
||||
|
||||
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-scheduler == Job Scheduled: R2-D2'
|
||||
- '== APP - job-scheduler == 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'
|
||||
- '== APP - job-service == Received job request...'
|
||||
- '== APP - job-service == Starting droid: C-3PO'
|
||||
- '== APP - job-service == Executing maintenance job: Limb Calibration'
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
background: false
|
||||
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 == 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 == Received job request...
|
||||
== APP - job-service == Starting droid: R2-D2
|
||||
== APP - job-service == Executing maintenance job: Oil Change
|
||||
```
|
||||
|
||||
After 20 seconds, the terminal output should present the `C-3PO` job being processed:
|
||||
|
||||
```text
|
||||
== APP - job-service == Received job request...
|
||||
== APP - job-service == Starting droid: C-3PO
|
||||
== APP - job-service == 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 --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:6280/v1.0-alpha1/jobs/r2-d2 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"data": {
|
||||
"Value": "R2-D2:Oil Change"
|
||||
},
|
||||
"dueTime": "2s"
|
||||
}'
|
||||
```
|
||||
|
||||
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:6280/v1.0-alpha1/jobs/c-3po \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"data": {
|
||||
"Value": "C-3PO:Limb Calibration"
|
||||
},
|
||||
"dueTime": "30s"
|
||||
}'
|
||||
```
|
||||
|
||||
### 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:6280/v1.0-alpha1/jobs/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:6280/v1.0-alpha1/jobs/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:6280/v1.0-alpha1/jobs/c-3po -H "Content-Type: application/json"
|
||||
```
|
||||
|
||||
In the `job-service` terminal window, the output should be similar to the following:
|
||||
|
||||
```text
|
||||
ERRO[0568] Error getting job c-3po due to: rpc error: code = Unknown desc = job not found: c-3po instance=local scope=dapr.api type=log ver=1.15.0
|
||||
```
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
version: 1
|
||||
apps:
|
||||
- appDirPath: ./job-service/
|
||||
appID: job-service
|
||||
appPort: 6200
|
||||
daprHTTPPort: 6280
|
||||
schedulerHostAddress: localhost
|
||||
command: ["dotnet", "run"]
|
||||
- appDirPath: ./job-scheduler/
|
||||
appID: job-scheduler
|
||||
appPort: 6300
|
||||
daprHTTPPort: 6380
|
||||
command: ["dotnet", "run"]
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
// Job request bodies
|
||||
var c3poJobBody = new
|
||||
{
|
||||
data = new { Value = "C-3PO:Limb Calibration" },
|
||||
dueTime = "20s"
|
||||
};
|
||||
|
||||
var r2d2JobBody = new
|
||||
{
|
||||
data = new { Value = "R2-D2:Oil Change" },
|
||||
dueTime = "15s"
|
||||
};
|
||||
|
||||
var daprHost = Environment.GetEnvironmentVariable("DAPR_HOST") ?? "http://localhost";
|
||||
var schedulerDaprHttpPort = "6280";
|
||||
|
||||
var httpClient = new HttpClient();
|
||||
|
||||
await Task.Delay(5000); // Wait for job-service to start
|
||||
|
||||
try
|
||||
{
|
||||
// Schedule R2-D2 job
|
||||
await ScheduleJob("R2-D2", r2d2JobBody);
|
||||
await Task.Delay(5000);
|
||||
// Get R2-D2 job details
|
||||
await GetJobDetails("R2-D2");
|
||||
|
||||
// Schedule C-3PO job
|
||||
await ScheduleJob("C-3PO", c3poJobBody);
|
||||
await Task.Delay(5000);
|
||||
// Get C-3PO job details
|
||||
await GetJobDetails("C-3PO");
|
||||
|
||||
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(string jobName, object jobBody)
|
||||
{
|
||||
var reqURL = $"{daprHost}:{schedulerDaprHttpPort}/v1.0-alpha1/jobs/{jobName}";
|
||||
var jsonBody = JsonSerializer.Serialize(jobBody);
|
||||
var content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
|
||||
|
||||
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}");
|
||||
}
|
||||
|
||||
async Task GetJobDetails(string jobName)
|
||||
{
|
||||
var reqURL = $"{daprHost}:{schedulerDaprHttpPort}/v1.0-alpha1/jobs/{jobName}";
|
||||
|
||||
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}");
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace>jobs_scheduler</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http.Json;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System.Text.Json;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.Configure<JsonOptions>(options =>
|
||||
{
|
||||
options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
var appPort = Environment.GetEnvironmentVariable("APP_PORT") ?? "6200";
|
||||
|
||||
//Job handler route
|
||||
app.MapPost("/job/{*path}", async (HttpRequest request, HttpResponse response) =>
|
||||
{
|
||||
Console.WriteLine("Received job request...");
|
||||
|
||||
try
|
||||
{
|
||||
// Parse the incoming JSON body
|
||||
var jobData = await JsonSerializer.DeserializeAsync<JobData>(request.Body);
|
||||
if (jobData == null || string.IsNullOrEmpty(jobData.Value))
|
||||
{
|
||||
throw new Exception("Invalid job data. 'value' field is required.");
|
||||
}
|
||||
|
||||
// Creating Droid Job from decoded value
|
||||
var droidJob = SetDroidJob(jobData.Value);
|
||||
Console.WriteLine($"Starting droid: {droidJob.Droid}");
|
||||
Console.WriteLine($"Executing maintenance job: {droidJob.Task}");
|
||||
response.StatusCode = 200;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Error processing job: {ex.Message}");
|
||||
response.StatusCode = 400; // Bad Request
|
||||
var errorResponse = new { error = $"Error processing request: {ex.Message}" };
|
||||
await response.WriteAsJsonAsync(errorResponse);
|
||||
}
|
||||
});
|
||||
|
||||
// Start the server
|
||||
app.Run($"http://localhost:{appPort}");
|
||||
|
||||
static DroidJob SetDroidJob(string droidStr)
|
||||
{
|
||||
var parts = droidStr.Split(":");
|
||||
if (parts.Length != 2)
|
||||
{
|
||||
throw new Exception("Invalid droid job format. Expected format: 'Droid:Task'");
|
||||
}
|
||||
|
||||
return new DroidJob
|
||||
{
|
||||
Droid = parts[0],
|
||||
Task = parts[1]
|
||||
};
|
||||
}
|
||||
|
||||
// Classes for request and response models
|
||||
public class JobData
|
||||
{
|
||||
public string? Value { get; set; }
|
||||
}
|
||||
|
||||
public class DroidJob
|
||||
{
|
||||
public string? Droid { get; set; }
|
||||
public string? Task { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:5305",
|
||||
"sslPort": 44346
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5023",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7073;http://localhost:5023",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>job_service</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
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://v1-14.docs.dapr.io/developing-applications/building-blocks/jobs/) link for more information about Dapr and the Jobs API.
|
||||
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 HTTP `requests` only. If you are looking for the example using the Dapr Client SDK (recommended) [click here](../sdk/).
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ This quickstart includes two apps:
|
|||
- `job-scheduler.go`, responsible for scheduling, retrieving and deleting jobs.
|
||||
- `job-service.go`, responsible for handling the triggered jobs.
|
||||
|
||||
## Run the app with the template file
|
||||
## 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.
|
||||
|
||||
|
|
@ -20,11 +20,11 @@ Open a new terminal window and run the multi app run template:
|
|||
<!-- STEP
|
||||
name: Run multi app run template
|
||||
expected_stdout_lines:
|
||||
- '== APP - job-service == Received job request...'
|
||||
- '== APP - job-service == Executing maintenance job: Oil Change'
|
||||
- '== APP - job-scheduler == Job Scheduled: C-3PO'
|
||||
- '== APP - job-service == Received job request...'
|
||||
- '== APP - job-service == Executing maintenance job: Limb Calibration'
|
||||
- '== APP - job-service-http == Received job request...'
|
||||
- '== APP - job-service-http == Executing maintenance job: Oil Change'
|
||||
- '== APP - job-scheduler-http == Job Scheduled: C-3PO'
|
||||
- '== APP - job-service-http == Received job request...'
|
||||
- '== APP - job-service-http == Executing maintenance job: Limb Calibration'
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
|
|
@ -45,20 +45,20 @@ The terminal console output should look similar to this, where:
|
|||
- The `C-3PO` job is being retrieved.
|
||||
|
||||
```text
|
||||
== APP - job-scheduler == Job Scheduled: R2-D2
|
||||
== APP - job-service == Received job request...
|
||||
== APP - job-service == Starting droid: R2-D2
|
||||
== APP - job-service == Executing maintenance job: Oil Change
|
||||
== APP - job-scheduler == Job Scheduled: C-3PO
|
||||
== APP - job-scheduler == Job details: {"name":"C-3PO", "dueTime":"30s", "data":{"@type":"ttype.googleapis.com/google.protobuf.StringValue", "expression":"C-3PO:Limb Calibration"}}
|
||||
== APP - job-scheduler-http == Job Scheduled: R2-D2
|
||||
== APP - job-service-http == Received job request...
|
||||
== APP - job-service-http == Starting droid: R2-D2
|
||||
== APP - job-service-http == Executing maintenance job: Oil Change
|
||||
== APP - job-scheduler-http == Job Scheduled: C-3PO
|
||||
== APP - job-scheduler-http == Job details: {"name":"C-3PO", "dueTime":"30s", "data":{"@type":"ttype.googleapis.com/google.protobuf.StringValue", "expression":"C-3PO:Limb Calibration"}}
|
||||
```
|
||||
|
||||
After 30 seconds, the terminal output should present the `C-3PO` job being processed:
|
||||
|
||||
```text
|
||||
== APP - job-service == Received job request...
|
||||
== APP - job-service == Starting droid: C-3PO
|
||||
== APP - job-service == Executing maintenance job: Limb Calibration
|
||||
== APP - job-service-http == Received job request...
|
||||
== APP - job-service-http == Starting droid: C-3PO
|
||||
== APP - job-service-http == Executing maintenance job: Limb Calibration
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
|
@ -76,14 +76,14 @@ dapr stop -f .
|
|||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Run the Jobs APIs individually
|
||||
## Run apps individually
|
||||
|
||||
### Schedule Jobs
|
||||
|
||||
1. Open a terminal and run the `job-service` app:
|
||||
|
||||
```bash
|
||||
dapr run --app-id job-service --app-port 6200 --dapr-http-port 6280 -- go run .
|
||||
dapr run --app-id job-service-http --app-port 6200 --dapr-http-port 6280 -- go run .
|
||||
```
|
||||
|
||||
2. On a new terminal window, schedule the `R2-D2` Job using the Jobs API.
|
||||
|
|
@ -104,9 +104,9 @@ curl -X POST \
|
|||
Back at the `job-service` app 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
|
||||
== APP - job-service-http == Received job request...
|
||||
== APP - job-service-http == Starting droid: R2-D2
|
||||
== APP - job-service-http == Executing maintenance job: Oil Change
|
||||
```
|
||||
|
||||
3. On the same terminal window, schedule the `C-3PO` Job using the Jobs API.
|
||||
|
|
@ -155,5 +155,5 @@ curl -X GET http://localhost:6280/v1.0-alpha1/jobs/c-3po -H "Content-Type: appli
|
|||
Back at the `job-service` app terminal window, the output should be:
|
||||
|
||||
```text
|
||||
ERRO[0249] Error getting job c-3po due to: rpc error: code = Unknown desc = job not found: app||default||job-service||c-3po instance=diagrid.local scope=dapr.api type=log ver=1.14.0-rc.2
|
||||
ERRO[0249] Error getting job c-3po due to: rpc error: code = Unknown desc = job not found: app||default||job-service-http||c-3po instance=diagrid.local scope=dapr.api type=log ver=1.14.0-rc.2
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
version: 1
|
||||
apps:
|
||||
- appDirPath: ./job-service/
|
||||
appID: job-service
|
||||
- appID: job-service-http
|
||||
appDirPath: ./job-service/
|
||||
appPort: 6200
|
||||
daprHTTPPort: 6280
|
||||
schedulerHostAddress: localhost
|
||||
command: ["go", "run", "."]
|
||||
- appDirPath: ./job-scheduler/
|
||||
appID: job-scheduler
|
||||
- appID: job-scheduler-http
|
||||
appDirPath: ./job-scheduler/
|
||||
appPort: 6300
|
||||
daprHTTPPort: 6380
|
||||
command: ["go", "run", "."]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
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://v1-14.docs.dapr.io/developing-applications/building-blocks/jobs/) link for more information about Dapr and the Job API.
|
||||
Visit [this](https://docs.dapr.io/developing-applications/building-blocks/jobs/) link for more information about Dapr and the Job API.
|
||||
|
||||
> **Note:** This example leverages the SDK only. If you are looking for the example using the HTTP requests [click here](../http/).
|
||||
|
||||
|
|
@ -20,10 +20,10 @@ Open a new terminal window and run the multi app run template:
|
|||
<!-- STEP
|
||||
name: Run multi app run template
|
||||
expected_stdout_lines:
|
||||
- '== APP - job-service == Starting droid: R2-D2'
|
||||
- '== APP - job-service == Executing maintenance job: Oil Change'
|
||||
- '== APP - job-service == Starting droid: C-3PO'
|
||||
- '== APP - job-service == Executing maintenance job: Memory Wipe'
|
||||
- '== 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: Memory Wipe'
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
|
|
@ -49,33 +49,33 @@ The terminal console output should look similar to this, where:
|
|||
|
||||
|
||||
```text
|
||||
== APP - job-service == dapr client initializing for: 127.0.0.1:6481
|
||||
== APP - job-service == Registered job handler for: R2-D2
|
||||
== APP - job-service == Registered job handler for: C-3PO
|
||||
== APP - job-service == Registered job handler for: BB-8
|
||||
== APP - job-service == Starting server on port: 6400
|
||||
== APP - job-service == Job scheduled: R2-D2
|
||||
== APP - job-service == Job scheduled: C-3PO
|
||||
== APP - job-service == 2024/07/17 18:09:59 job:{name:"C-3PO" due_time:"10s" data:{value:"{\"droid\":\"C-3PO\",\"Task\":\"Memory Wipe\"}"}}
|
||||
== APP - job-scheduler == Get job response: {"droid":"C-3PO","Task":"Memory Wipe"}
|
||||
== APP - job-service == Job scheduled: BB-8
|
||||
== APP - job-service == 2024/07/17 18:09:59 job:{name:"BB-8" due_time:"15s" data:{value:"{\"droid\":\"BB-8\",\"Task\":\"Internal Gyroscope Check\"}"}}
|
||||
== APP - job-scheduler == Get job response: {"droid":"BB-8","Task":"Internal Gyroscope Check"}
|
||||
== APP - job-scheduler == Deleted job: BB-8
|
||||
== APP - job-service-sdk == dapr client initializing for: 127.0.0.1:6481
|
||||
== APP - job-service-sdk == Registered job handler for: R2-D2
|
||||
== APP - job-service-sdk == Registered job handler for: C-3PO
|
||||
== APP - job-service-sdk == Registered job handler for: BB-8
|
||||
== APP - job-service-sdk == Starting server on port: 6400
|
||||
== APP - job-service-sdk == Job scheduled: R2-D2
|
||||
== APP - job-service-sdk == Job scheduled: C-3PO
|
||||
== APP - job-service-sdk == 2024/07/17 18:09:59 job:{name:"C-3PO" due_time:"10s" data:{value:"{\"droid\":\"C-3PO\",\"Task\":\"Memory Wipe\"}"}}
|
||||
== APP - job-scheduler-sdk == Get job response: {"droid":"C-3PO","Task":"Memory Wipe"}
|
||||
== APP - job-service-sdk == Job scheduled: BB-8
|
||||
== APP - job-service-sdk == 2024/07/17 18:09:59 job:{name:"BB-8" due_time:"15s" data:{value:"{\"droid\":\"BB-8\",\"Task\":\"Internal Gyroscope Check\"}"}}
|
||||
== APP - job-scheduler-sdk == Get job response: {"droid":"BB-8","Task":"Internal Gyroscope Check"}
|
||||
== APP - job-scheduler-sdk == Deleted job: BB-8
|
||||
```
|
||||
|
||||
After 5 seconds, the terminal output should present the `R2-D2` job being processed:
|
||||
|
||||
```text
|
||||
== APP - job-service == Starting droid: R2-D2
|
||||
== APP - job-service == Executing maintenance job: Oil Change
|
||||
== APP - job-service-sdk == Starting droid: R2-D2
|
||||
== APP - job-service-sdk == Executing maintenance job: Oil Change
|
||||
```
|
||||
|
||||
After 10 seconds, the terminal output should present the `C3-PO` job being processed:
|
||||
|
||||
```text
|
||||
== APP - job-service == Starting droid: C-3PO
|
||||
== APP - job-service == Executing maintenance job: Memory Wipe
|
||||
== APP - job-service-sdk == Starting droid: C-3PO
|
||||
== APP - job-service-sdk == Executing maintenance job: Memory Wipe
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
|
@ -99,7 +99,7 @@ dapr stop -f .
|
|||
1. Open a terminal and run the `job-service` app:
|
||||
|
||||
```bash
|
||||
dapr run --app-id job-service --app-port 6400 --dapr-grpc-port 6481 --app-protocol grpc -- go run .
|
||||
dapr run --app-id job-service-sdk --app-port 6400 --dapr-grpc-port 6481 --app-protocol grpc -- go run .
|
||||
```
|
||||
|
||||
The output should be:
|
||||
|
|
@ -115,7 +115,7 @@ The output should be:
|
|||
2. On a new terminal window, run the `job-scheduler` app:
|
||||
|
||||
```bash
|
||||
dapr run --app-id job-scheduler --app-port 6500 -- go run .
|
||||
dapr run --app-id job-scheduler-sdk --app-port 6500 -- go run .
|
||||
```
|
||||
|
||||
The output should be:
|
||||
|
|
|
|||
|
|
@ -3,14 +3,13 @@ common:
|
|||
daprdLogDestination: console
|
||||
logLevel: info
|
||||
apps:
|
||||
- appDirPath: ./job-service/
|
||||
appID: job-service
|
||||
- appID: job-service-sdk
|
||||
appDirPath: ./job-service/
|
||||
appPort: 6400
|
||||
daprGRPCPort: 6481
|
||||
appProtocol: grpc
|
||||
schedulerHostAddress: localhost
|
||||
command: ["go", "run", "."]
|
||||
- appDirPath: ./job-scheduler/
|
||||
appID: job-scheduler
|
||||
- appID: job-scheduler-sdk
|
||||
appDirPath: ./job-scheduler/
|
||||
appPort: 6500
|
||||
command: ["go", "run", "."]
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ func schedule(droidJob DroidJob) error {
|
|||
}
|
||||
|
||||
// Schedule Job
|
||||
_, err = app.daprClient.InvokeMethodWithContent(context.Background(), "job-service", "scheduleJob", "POST", content)
|
||||
_, err = app.daprClient.InvokeMethodWithContent(context.Background(), "job-service-sdk", "scheduleJob", "POST", content)
|
||||
if err != nil {
|
||||
fmt.Println("Error invoking method: ", err)
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -0,0 +1,184 @@
|
|||
# Dapr Jobs
|
||||
|
||||
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.
|
||||
|
||||
This quickstart includes two apps:
|
||||
|
||||
- `job-scheduler`, responsible for scheduling, retrieving and deleting jobs.
|
||||
- `job-service`, responsible for handling the triggered jobs.
|
||||
|
||||
## Run the app with the 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. Install dependencies:
|
||||
|
||||
<!-- STEP
|
||||
name: Install Node dependencies for job-service
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./job-service
|
||||
npm install
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
<!-- STEP
|
||||
name: Install Node dependencies for job-scheduler
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./job-scheduler
|
||||
npm install
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
2. Open a new terminal window and 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 == Received job request...'
|
||||
- '== APP - job-service == Starting droid: R2-D2'
|
||||
- '== APP - job-service == Executing maintenance job: Oil Change'
|
||||
- '== APP - job-service == Received job request...'
|
||||
- '== APP - job-service == Starting droid: C-3PO'
|
||||
- '== APP - job-service == 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 == Job Scheduled: R2-D2
|
||||
== APP - job-scheduler == Job details: {"name":"R2-D2", "dueTime":"15s", "data":{"value":{"value":"R2-D2:Oil Change"}}}
|
||||
== APP - job-scheduler == Job Scheduled: C-3PO
|
||||
== APP - job-scheduler == Job details: {"name":"C-3PO", "dueTime":"20s", "data":{"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
|
||||
```
|
||||
|
||||
After 20 seconds, the terminal output should present the `C-3PO` job being processed:
|
||||
|
||||
```text
|
||||
== APP - job-service == Received job request...
|
||||
== APP - job-service == Starting droid: C-3PO
|
||||
== APP - job-service == Executing maintenance job: Limb Calibration
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
3. Stop and clean up application processes.
|
||||
|
||||
<!-- STEP
|
||||
name: Stop multi-app run
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr stop -f .
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Run the apps individually
|
||||
|
||||
### Schedule Jobs
|
||||
|
||||
1. Open a terminal and run the `job-service` app:
|
||||
|
||||
```bash
|
||||
dapr run --app-id job-service --app-port 6200 --dapr-http-port 6280 -- npm run start
|
||||
```
|
||||
|
||||
2. On a new terminal window, schedule the `R2-D2` Job using the Jobs API.
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:6280/v1.0-alpha1/jobs/r2-d2 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"data": {
|
||||
"value": "R2-D2:Oil Change"
|
||||
},
|
||||
"dueTime": "2s"
|
||||
}'
|
||||
```
|
||||
|
||||
Back at the `job-service` app 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:6280/v1.0-alpha1/jobs/c-3po \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"data": {
|
||||
"value": "C-3PO:Limb Calibration"
|
||||
},
|
||||
"dueTime": "30s"
|
||||
}'
|
||||
```
|
||||
|
||||
### 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:6280/v1.0-alpha1/jobs/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.StringValue", "expression":"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:6280/v1.0-alpha1/jobs/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:6280/v1.0-alpha1/jobs/c-3po -H "Content-Type: application/json"
|
||||
```
|
||||
|
||||
Back at the `job-service` app terminal window, the output should be:
|
||||
|
||||
```text
|
||||
ERRO[0249] Error getting job c-3po due to: rpc error: code = Unknown desc = job not found: app||default||job-service||c-3po instance=diagrid.local scope=dapr.api type=log ver=1.14.0-rc.2
|
||||
```
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
version: 1
|
||||
apps:
|
||||
- appID: job-service
|
||||
appDirPath: ./job-service/
|
||||
appPort: 6200
|
||||
daprHTTPPort: 6280
|
||||
schedulerHostAddress: localhost
|
||||
command: ["npm", "run", "start"]
|
||||
- appID: job-scheduler
|
||||
appDirPath: ./job-scheduler/
|
||||
appPort: 6300
|
||||
daprHTTPPort: 6380
|
||||
command: ["npm", "run", "start"]
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
// Job request bodies
|
||||
const c3poJobBody = {
|
||||
data: {
|
||||
value: "C-3PO:Limb Calibration",
|
||||
},
|
||||
dueTime: "20s",
|
||||
};
|
||||
|
||||
const r2d2JobBody = {
|
||||
data: {
|
||||
value: "R2-D2:Oil Change",
|
||||
},
|
||||
dueTime: "15s",
|
||||
};
|
||||
const daprHost = process.env.DAPR_HOST || "http://localhost";
|
||||
const schedulerDaprHttpPort = "6280";
|
||||
|
||||
async function main() {
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
// Wait for job-service to start
|
||||
await sleep(5000);
|
||||
|
||||
try {
|
||||
// Schedule R2-D2 job
|
||||
await scheduleJob("R2-D2", r2d2JobBody);
|
||||
await sleep(5000);
|
||||
// Get R2-D2 job details
|
||||
await getJobDetails("R2-D2");
|
||||
|
||||
// Schedule C-3PO job
|
||||
await scheduleJob("C-3PO", c3poJobBody);
|
||||
await sleep(5000);
|
||||
// Get C-3PO job details
|
||||
await getJobDetails("C-3PO");
|
||||
|
||||
await sleep(30000);
|
||||
} catch (error) {
|
||||
if (error.name === "AbortError") {
|
||||
console.error("Request timed out");
|
||||
} else {
|
||||
console.error("Error:", error.message);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function scheduleJob(jobName, jobBody) {
|
||||
const reqURL = `${daprHost}:${schedulerDaprHttpPort}/v1.0-alpha1/jobs/${jobName}`;
|
||||
const response = await fetch(reqURL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(jobBody),
|
||||
});
|
||||
|
||||
if (response.status !== 204) {
|
||||
throw new Error(
|
||||
`Failed to register job event handler. Status code: ${response.status}`
|
||||
);
|
||||
}
|
||||
|
||||
console.log("Job Scheduled:", jobName);
|
||||
}
|
||||
|
||||
async function getJobDetails(jobName) {
|
||||
const reqURL = `${daprHost}:${schedulerDaprHttpPort}/v1.0-alpha1/jobs/${jobName}`;
|
||||
const response = await fetch(reqURL, {
|
||||
method: "GET",
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const jobDetails = await response.text();
|
||||
console.log("Job details:", jobDetails);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
|
|
@ -0,0 +1,765 @@
|
|||
{
|
||||
"name": "job-scheduler",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "job-scheduler",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.2"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||
"dependencies": {
|
||||
"mime-types": "~2.1.34",
|
||||
"negotiator": "0.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.3",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
||||
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.5",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.13.0",
|
||||
"raw-body": "2.5.2",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
|
||||
"integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bound": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
|
||||
"integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"get-intrinsic": "^1.2.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/content-type": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/destroy": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.1.tgz",
|
||||
"integrity": "sha512-BPOBuyUF9QIVhuNLhbToCLHP6+0MHwZ7xLBkPPCZqK4JmpJgGnv10035STzzQwFpqdzNFMB3irvDI63IagvDwA==",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
||||
},
|
||||
"node_modules/etag": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.21.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
|
||||
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.3",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.7.1",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "1.3.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"merge-descriptors": "1.0.3",
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.12",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.13.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "0.19.0",
|
||||
"serve-static": "1.16.2",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"type-is": "~1.6.18",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
|
||||
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"statuses": "2.0.1",
|
||||
"unpipe": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
|
||||
"integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.0.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.0",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
||||
"dependencies": {
|
||||
"depd": "2.0.0",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"toidentifier": "1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
|
||||
"integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"dependencies": {
|
||||
"ee-first": "1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||
"dependencies": {
|
||||
"forwarded": "0.2.0",
|
||||
"ipaddr.js": "1.9.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
||||
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "0.19.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
|
||||
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"mime": "1.6.0",
|
||||
"ms": "2.1.3",
|
||||
"on-finished": "2.4.1",
|
||||
"range-parser": "~1.2.1",
|
||||
"statuses": "2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "1.16.2",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
||||
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
|
||||
"dependencies": {
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"parseurl": "~1.3.3",
|
||||
"send": "0.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/setprototypeof": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
||||
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"object-inspect": "^1.13.3",
|
||||
"side-channel-list": "^1.0.0",
|
||||
"side-channel-map": "^1.0.1",
|
||||
"side-channel-weakmap": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-list": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
||||
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"object-inspect": "^1.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-map": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
||||
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.5",
|
||||
"object-inspect": "^1.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-weakmap": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
||||
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.5",
|
||||
"object-inspect": "^1.13.3",
|
||||
"side-channel-map": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||
"dependencies": {
|
||||
"media-typer": "0.3.0",
|
||||
"mime-types": "~2.1.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "job-scheduler",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"start:dapr": "dapr run --app-port 6200 --app-id job-scheduler --app-protocol http --dapr-http-port 6280 -- npm run start"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"express": "^4.17.2",
|
||||
"body-parser": "^1.19.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import express from "express";
|
||||
|
||||
const app = express();
|
||||
|
||||
// Middleware to parse JSON bodies
|
||||
app.use(express.json());
|
||||
|
||||
// Start server
|
||||
const appPort = process.env.APP_PORT || "6200";
|
||||
app
|
||||
.listen(appPort, () => {
|
||||
console.log(`Server started on port ${appPort}`);
|
||||
})
|
||||
.on("error", (err) => {
|
||||
console.error("Failed to start server:", err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
function setDroidJob(droidStr) {
|
||||
const [droid, task] = droidStr.split(":");
|
||||
|
||||
return {
|
||||
droid,
|
||||
task,
|
||||
};
|
||||
}
|
||||
|
||||
// Job handler route
|
||||
app.post("/job/*", (req, res) => {
|
||||
console.log("Received job request...");
|
||||
|
||||
try {
|
||||
const jobData = req.body;
|
||||
|
||||
// Creating Droid Job from decoded value
|
||||
const droidJob = setDroidJob(jobData.value);
|
||||
|
||||
console.log("Starting droid:", droidJob.droid);
|
||||
console.log("Executing maintenance job:", droidJob.task);
|
||||
res.status(200).end();
|
||||
} catch (error) {
|
||||
console.error("Error processing job:", error);
|
||||
res.status(400).json({
|
||||
error: `Error processing request: ${error.message}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,765 @@
|
|||
{
|
||||
"name": "job-service",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "job-service",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.2"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||
"dependencies": {
|
||||
"mime-types": "~2.1.34",
|
||||
"negotiator": "0.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.3",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
||||
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.5",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.13.0",
|
||||
"raw-body": "2.5.2",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
|
||||
"integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bound": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
|
||||
"integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"get-intrinsic": "^1.2.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/content-type": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/destroy": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.1.tgz",
|
||||
"integrity": "sha512-BPOBuyUF9QIVhuNLhbToCLHP6+0MHwZ7xLBkPPCZqK4JmpJgGnv10035STzzQwFpqdzNFMB3irvDI63IagvDwA==",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
||||
},
|
||||
"node_modules/etag": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.21.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
|
||||
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.3",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.7.1",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "1.3.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"merge-descriptors": "1.0.3",
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.12",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.13.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "0.19.0",
|
||||
"serve-static": "1.16.2",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"type-is": "~1.6.18",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
|
||||
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"statuses": "2.0.1",
|
||||
"unpipe": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
|
||||
"integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.0.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.0",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
||||
"dependencies": {
|
||||
"depd": "2.0.0",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"toidentifier": "1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
|
||||
"integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"dependencies": {
|
||||
"ee-first": "1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||
"dependencies": {
|
||||
"forwarded": "0.2.0",
|
||||
"ipaddr.js": "1.9.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
||||
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "0.19.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
|
||||
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"mime": "1.6.0",
|
||||
"ms": "2.1.3",
|
||||
"on-finished": "2.4.1",
|
||||
"range-parser": "~1.2.1",
|
||||
"statuses": "2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "1.16.2",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
||||
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
|
||||
"dependencies": {
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"parseurl": "~1.3.3",
|
||||
"send": "0.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/setprototypeof": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
||||
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"object-inspect": "^1.13.3",
|
||||
"side-channel-list": "^1.0.0",
|
||||
"side-channel-map": "^1.0.1",
|
||||
"side-channel-weakmap": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-list": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
||||
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"object-inspect": "^1.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-map": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
||||
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.5",
|
||||
"object-inspect": "^1.13.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-weakmap": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
||||
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.5",
|
||||
"object-inspect": "^1.13.3",
|
||||
"side-channel-map": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||
"dependencies": {
|
||||
"media-typer": "0.3.0",
|
||||
"mime-types": "~2.1.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "job-service",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"start:dapr": "dapr run --app-port 6200 --app-id job-service --app-protocol http --dapr-http-port 6281 -- npm run start"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"express": "^4.17.2",
|
||||
"body-parser": "^1.19.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
# Dapr Jobs API (HTTP Client)
|
||||
|
||||
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.
|
||||
|
||||
|
||||
This quickstart includes two apps:
|
||||
|
||||
- `job-scheduler/app.py`, responsible for scheduling, retrieving and deleting jobs.
|
||||
- `job-service/app.py`, responsible for handling the triggered jobs.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Python 3.8+](https://www.python.org/downloads/)
|
||||
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/)
|
||||
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/)
|
||||
|
||||
## Install dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## 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 you to test the interactions between multiple applications and will `schedule`, `run`, `get`, and `delete` jobs within a single process.
|
||||
|
||||
Open a new terminal window and run the multi app run template:
|
||||
|
||||
<!-- STEP
|
||||
name: Run multi app run template
|
||||
expected_stdout_lines:
|
||||
- '== APP - job-service == Received job request...'
|
||||
- '== APP - job-service == Executing maintenance job: Oil Change'
|
||||
- '== APP - job-scheduler == Job Scheduled: C-3PO'
|
||||
- '== APP - job-service == Received job request...'
|
||||
- '== APP - job-service == 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 executed after 2 seconds.
|
||||
- The `C-3PO` job is being scheduled.
|
||||
- The `C-3PO` job is being retrieved.
|
||||
|
||||
```text
|
||||
== APP - job-scheduler == Job Scheduled: R2-D2
|
||||
== APP - job-service == Received job request...
|
||||
== APP - job-service == Starting droid: R2-D2
|
||||
== APP - job-service == Executing maintenance job: Oil Change
|
||||
== APP - job-scheduler == Job Scheduled: C-3PO
|
||||
== APP - job-scheduler == Job details: {"name":"C-3PO", "dueTime":"30s", "data":{"@type":"ttype.googleapis.com/google.protobuf.StringValue", "expression":"C-3PO:Limb Calibration"}}
|
||||
```
|
||||
|
||||
After 30 seconds, the terminal output should present the `C-3PO` job being processed:
|
||||
|
||||
```text
|
||||
== APP - job-service == Received job request...
|
||||
== APP - job-service == Starting droid: C-3PO
|
||||
== APP - job-service == Executing maintenance job: Limb Calibration
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
2. Stop and clean up application processes
|
||||
|
||||
<!-- STEP
|
||||
name: Stop multi-app run
|
||||
sleep: 5
|
||||
-->
|
||||
|
||||
```bash
|
||||
dapr stop -f .
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Run apps individually
|
||||
|
||||
### Start the job service
|
||||
|
||||
1. Open a terminal and run the `job-service` app:
|
||||
|
||||
```bash
|
||||
cd job-service
|
||||
dapr run --app-id job-service --app-port 6200 --dapr-http-port 6280 -- python app.py
|
||||
```
|
||||
|
||||
### Schedule jobs
|
||||
|
||||
1. On a new terminal window, schedule the `R2-D2` Job using the Jobs API:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:6280/v1.0-alpha1/jobs/R2D2 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"data": {
|
||||
"@type": "type.googleapis.com/google.protobuf.StringValue",
|
||||
"value": "R2-D2:Oil Change"
|
||||
},
|
||||
"dueTime": "2s"
|
||||
}'
|
||||
```
|
||||
|
||||
Back at the `job-service` app terminal window, the output should be:
|
||||
|
||||
```text
|
||||
== APP - job-service == Received job request...
|
||||
== APP - job-service == Starting droid: R2-D2
|
||||
== APP - job-service == Executing maintenance job: Oil Change
|
||||
```
|
||||
|
||||
2. On the same terminal window, schedule the `C-3PO` Job using the Jobs API:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:6280/v1.0-alpha1/jobs/c-3po \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"data": {
|
||||
"@type": "type.googleapis.com/google.protobuf.StringValue",
|
||||
"value": "C-3PO:Limb Calibration"
|
||||
},
|
||||
"dueTime": "30s"
|
||||
}'
|
||||
```
|
||||
|
||||
### 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:6280/v1.0-alpha1/jobs/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.StringValue", "expression":"C-3PO:Limb Calibration"}}
|
||||
```
|
||||
|
||||
### Delete a scheduled job
|
||||
|
||||
1. On the same terminal window, run the command below to delete the recently scheduled `C-3PO` job:
|
||||
|
||||
```bash
|
||||
curl -X DELETE http://localhost:6280/v1.0-alpha1/jobs/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:6280/v1.0-alpha1/jobs/c-3po -H "Content-Type: application/json"
|
||||
```
|
||||
|
||||
You should see an error message indicating that the job was not found:
|
||||
|
||||
```text
|
||||
{"errorCode":"ERR_JOBS_NOT_FOUND","message":"job not found: app||default||job-service||c-3po"}
|
||||
```
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
version: 1
|
||||
apps:
|
||||
- appDirPath: ./job-service/
|
||||
appID: job-service
|
||||
appPort: 6200
|
||||
daprHTTPPort: 6280
|
||||
command: ["python3", "app.py"]
|
||||
- appDirPath: ./job-scheduler/
|
||||
appID: job-scheduler
|
||||
appPort: 6300
|
||||
daprHTTPPort: 6380
|
||||
command: ["python3", "app.py"]
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
import os
|
||||
import time
|
||||
import requests
|
||||
import json
|
||||
|
||||
C3PO_JOB_BODY = {
|
||||
"data": {"@type": "type.googleapis.com/google.protobuf.StringValue", "value": "C-3PO:Limb Calibration"},
|
||||
"dueTime": "10s",
|
||||
}
|
||||
|
||||
R2D2_JOB_BODY = {
|
||||
"data": {"@type": "type.googleapis.com/google.protobuf.StringValue", "value": "R2-D2:Oil Change"},
|
||||
"dueTime": "2s"
|
||||
}
|
||||
|
||||
def schedule_job(host: str, port: str, job_name: str, job_body: dict) -> None:
|
||||
req_url = f"{host}:{port}/v1.0-alpha1/jobs/{job_name}"
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
req_url,
|
||||
json=job_body,
|
||||
headers={"Content-Type": "application/json"},
|
||||
timeout=15
|
||||
)
|
||||
|
||||
# Accept both 200 and 204 as success codes
|
||||
if response.status_code not in [200, 204]:
|
||||
raise Exception(f"Failed to schedule job. Status code: {response.status_code}, Response: {response.text}")
|
||||
|
||||
print(f"Job Scheduled: {job_name}")
|
||||
if response.text:
|
||||
print(f"Response: {response.text}")
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error scheduling job {job_name}: {str(e)}")
|
||||
raise
|
||||
|
||||
def get_job_details(host: str, port: str, job_name: str) -> None:
|
||||
req_url = f"{host}:{port}/v1.0-alpha1/jobs/{job_name}"
|
||||
|
||||
try:
|
||||
response = requests.get(req_url, timeout=15)
|
||||
if response.status_code in [200, 204]:
|
||||
print(f"Job details for {job_name}: {response.text}")
|
||||
else:
|
||||
print(f"Failed to get job details. Status code: {response.status_code}, Response: {response.text}")
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error getting job details for {job_name}: {str(e)}")
|
||||
raise
|
||||
|
||||
def main():
|
||||
# Wait for services to be ready
|
||||
time.sleep(5)
|
||||
|
||||
dapr_host = os.getenv('DAPR_HOST', 'http://localhost')
|
||||
scheduler_dapr_http_port = os.getenv('SCHEDULER_DAPR_HTTP_PORT', '6280')
|
||||
|
||||
# Schedule R2-D2 job
|
||||
schedule_job(dapr_host, scheduler_dapr_http_port, "R2-D2", R2D2_JOB_BODY)
|
||||
time.sleep(5)
|
||||
|
||||
# Schedule C-3PO job
|
||||
schedule_job(dapr_host, scheduler_dapr_http_port, "C-3PO", C3PO_JOB_BODY)
|
||||
time.sleep(5)
|
||||
|
||||
# Get C-3PO job details
|
||||
get_job_details(dapr_host, scheduler_dapr_http_port, "C-3PO")
|
||||
time.sleep(5)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import os
|
||||
import json
|
||||
import traceback
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
|
||||
class DroidJob:
|
||||
def __init__(self, droid: str, task: str):
|
||||
self.droid = droid
|
||||
self.task = task
|
||||
|
||||
def set_droid_job(decoded_value: str) -> DroidJob:
|
||||
# Remove newlines from decoded value and split into droid and task
|
||||
droid_str = decoded_value.replace('\n', '')
|
||||
droid_array = droid_str.split(':')
|
||||
return DroidJob(droid_array[0], droid_array[1])
|
||||
|
||||
class JobHandler(BaseHTTPRequestHandler):
|
||||
def _send_response(self, status_code: int, message: str = ""):
|
||||
self.send_response(status_code)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.end_headers()
|
||||
if message:
|
||||
self.wfile.write(json.dumps({"message": message}).encode('utf-8'))
|
||||
|
||||
def do_POST(self):
|
||||
print('Received job request...', flush=True)
|
||||
|
||||
try:
|
||||
# Check if path starts with /job/
|
||||
if not self.path.startswith('/job/'):
|
||||
self._send_response(404, "Not Found")
|
||||
return
|
||||
|
||||
# Read request body
|
||||
content_length = int(self.headers.get('Content-Length', 0))
|
||||
raw_data = self.rfile.read(content_length).decode('utf-8')
|
||||
|
||||
# Parse outer JSON data
|
||||
job_data = json.loads(raw_data)
|
||||
|
||||
# Extract value directly from the job data
|
||||
value = job_data.get('value', '')
|
||||
|
||||
# Create DroidJob from value
|
||||
droid_job = set_droid_job(value)
|
||||
|
||||
print("Starting droid: " + droid_job.droid, flush=True)
|
||||
print("Executing maintenance job: " + droid_job.task, flush=True)
|
||||
|
||||
self._send_response(200)
|
||||
|
||||
except Exception as e:
|
||||
print("Error processing job request:", flush= True)
|
||||
print(traceback.format_exc())
|
||||
self._send_response(400, f"Error processing job: {str(e)}")
|
||||
|
||||
def run_server(port: int):
|
||||
server_address = ('', port)
|
||||
httpd = HTTPServer(server_address, JobHandler)
|
||||
print("Server started on port " + str(port), flush=True)
|
||||
httpd.serve_forever()
|
||||
|
||||
if __name__ == '__main__':
|
||||
app_port = int(os.getenv('APP_PORT', '6200'))
|
||||
run_server(app_port)
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1 @@
|
|||
requests==2.31.0
|
||||
|
|
@ -23,7 +23,7 @@ This section shows how to run both applications at once using [multi-app run tem
|
|||
<!-- STEP
|
||||
name: Run multi app run template
|
||||
expected_stdout_lines:
|
||||
- 'Started Dapr with app id "order-processor"'
|
||||
- 'Started Dapr with app id "order-processor-sdk"'
|
||||
- 'Started Dapr with app id "checkout-sdk"'
|
||||
- 'Published data: Order { OrderId = 2 }'
|
||||
- 'Subscriber received : Order { OrderId = 2 }'
|
||||
|
|
@ -43,25 +43,25 @@ The terminal console output should look similar to this:
|
|||
|
||||
```text
|
||||
== APP - checkout-sdk == Published data: Order { OrderId = 1 }
|
||||
== APP - order-processor == Subscriber received : Order { OrderId = 1 }
|
||||
== APP - order-processor-sdk == Subscriber received : Order { OrderId = 1 }
|
||||
== APP - checkout-sdk == Published data: Order { OrderId = 2 }
|
||||
== APP - order-processor == Subscriber received : Order { OrderId = 2 }
|
||||
== APP - order-processor-sdk == Subscriber received : Order { OrderId = 2 }
|
||||
== APP - checkout-sdk == Published data: Order { OrderId = 3 }
|
||||
== APP - order-processor == Subscriber received : Order { OrderId = 3 }
|
||||
== APP - order-processor-sdk == Subscriber received : Order { OrderId = 3 }
|
||||
== APP - checkout-sdk == Published data: Order { OrderId = 4 }
|
||||
== APP - order-processor == Subscriber received : Order { OrderId = 4 }
|
||||
== APP - order-processor-sdk == Subscriber received : Order { OrderId = 4 }
|
||||
== APP - checkout-sdk == Published data: Order { OrderId = 5 }
|
||||
== APP - order-processor == Subscriber received : Order { OrderId = 5 }
|
||||
== APP - order-processor-sdk == Subscriber received : Order { OrderId = 5 }
|
||||
== APP - checkout-sdk == Published data: Order { OrderId = 6 }
|
||||
== APP - order-processor == Subscriber received : Order { OrderId = 6 }
|
||||
== APP - order-processor-sdk == Subscriber received : Order { OrderId = 6 }
|
||||
== APP - checkout-sdk == Published data: Order { OrderId = 7 }
|
||||
== APP - order-processor == Subscriber received : Order { OrderId = 7 }
|
||||
== APP - order-processor-sdk == Subscriber received : Order { OrderId = 7 }
|
||||
== APP - checkout-sdk == Published data: Order { OrderId = 8 }
|
||||
== APP - order-processor == Subscriber received : Order { OrderId = 8 }
|
||||
== APP - order-processor-sdk == Subscriber received : Order { OrderId = 8 }
|
||||
== APP - checkout-sdk == Published data: Order { OrderId = 9 }
|
||||
== APP - order-processor == Subscriber received : Order { OrderId = 9 }
|
||||
== APP - order-processor-sdk == Subscriber received : Order { OrderId = 9 }
|
||||
== APP - checkout-sdk == Published data: Order { OrderId = 10 }
|
||||
== APP - order-processor == Subscriber received : Order { OrderId = 10 }
|
||||
== APP - order-processor-sdk == Subscriber received : Order { OrderId = 10 }
|
||||
```
|
||||
|
||||
2. Stop and clean up application processes
|
||||
|
|
@ -81,7 +81,7 @@ An alternative to running all or multiple applications at once is to run single
|
|||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
dapr run --app-id order-processor --resources-path ../../../components/ --app-port 7006 -- dotnet run
|
||||
dapr run --app-id order-processor-sdk --resources-path ../../../components/ --app-port 7006 -- dotnet run
|
||||
```
|
||||
|
||||
### Run Dotnet message publisher with Dapr
|
||||
|
|
@ -97,6 +97,6 @@ dapr run --app-id checkout-sdk --resources-path ../../../components/ -- dotnet r
|
|||
2. Stop and clean up application processes
|
||||
|
||||
```bash
|
||||
dapr stop --app-id order-processor
|
||||
dapr stop --app-id order-processor-sdk
|
||||
dapr stop --app-id checkout-sdk
|
||||
```
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ version: 1
|
|||
common:
|
||||
resourcesPath: ../../components/
|
||||
apps:
|
||||
- appID: order-processor
|
||||
- appID: order-processor-sdk
|
||||
appDirPath: ./order-processor/
|
||||
appPort: 7006
|
||||
command: ["dotnet", "run"]
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ This section shows how to run both applications at once using [multi-app run tem
|
|||
<!-- STEP
|
||||
name: Run multi app run template
|
||||
expected_stdout_lines:
|
||||
- 'Started Dapr with app id "order-processor"'
|
||||
- 'Started Dapr with app id "order-processor-sdk"'
|
||||
- 'Started Dapr with app id "checkout-sdk"'
|
||||
- '== APP - checkout-sdk == Published data: {"orderId":1}'
|
||||
- '== APP - order-processor == Subscriber received: map[orderId:1]'
|
||||
- '== APP - order-processor-sdk == Subscriber received: map[orderId:1]'
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
|
|
@ -39,25 +39,25 @@ The terminal console output should look similar to this:
|
|||
|
||||
```text
|
||||
== APP - checkout-sdk == Published data: {"orderId":1}
|
||||
== APP - order-processor == Subscriber received: map[orderId:1]
|
||||
== APP - order-processor-sdk == Subscriber received: map[orderId:1]
|
||||
== APP - checkout-sdk == Published data: {"orderId":2}
|
||||
== APP - order-processor == Subscriber received: map[orderId:2]
|
||||
== APP - order-processor-sdk == Subscriber received: map[orderId:2]
|
||||
== APP - checkout-sdk == Published data: {"orderId":3}
|
||||
== APP - order-processor == Subscriber received: map[orderId:3]
|
||||
== APP - order-processor-sdk == Subscriber received: map[orderId:3]
|
||||
== APP - checkout-sdk == Published data: {"orderId":4}
|
||||
== APP - order-processor == Subscriber received: map[orderId:4]
|
||||
== APP - order-processor-sdk == Subscriber received: map[orderId:4]
|
||||
== APP - checkout-sdk == Published data: {"orderId":5}
|
||||
== APP - order-processor == Subscriber received: map[orderId:5]
|
||||
== APP - order-processor-sdk == Subscriber received: map[orderId:5]
|
||||
== APP - checkout-sdk == Published data: {"orderId":6}
|
||||
== APP - order-processor == Subscriber received: map[orderId:6]
|
||||
== APP - order-processor-sdk == Subscriber received: map[orderId:6]
|
||||
== APP - checkout-sdk == Published data: {"orderId":7}
|
||||
== APP - order-processor == Subscriber received: map[orderId:7]
|
||||
== APP - order-processor-sdk == Subscriber received: map[orderId:7]
|
||||
== APP - checkout-sdk == Published data: {"orderId":8}
|
||||
== APP - order-processor == Subscriber received: map[orderId:8]
|
||||
== APP - order-processor-sdk == Subscriber received: map[orderId:8]
|
||||
== APP - checkout-sdk == Published data: {"orderId":9}
|
||||
== APP - order-processor == Subscriber received: map[orderId:9]
|
||||
== APP - order-processor-sdk == Subscriber received: map[orderId:9]
|
||||
== APP - checkout-sdk == Published data: {"orderId":10}
|
||||
== APP - order-processor == Subscriber received: map[orderId:10]
|
||||
== APP - order-processor-sdk == Subscriber received: map[orderId:10]
|
||||
```
|
||||
<!-- END_STEP -->
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ version: 1
|
|||
common:
|
||||
resourcesPath: ../../components/
|
||||
apps:
|
||||
- appID: order-processor
|
||||
- appID: order-processor-sdk
|
||||
appDirPath: ./order-processor/
|
||||
appPort: 6005
|
||||
command: ["go", "run", "."]
|
||||
|
|
|
|||
|
|
@ -39,10 +39,10 @@ cd ..
|
|||
<!-- STEP
|
||||
name: Run multi app run template
|
||||
expected_stdout_lines:
|
||||
- 'Started Dapr with app id "order-processor"'
|
||||
- 'Started Dapr with app id "order-processor-sdk"'
|
||||
- 'Started Dapr with app id "checkout-sdk"'
|
||||
- '== APP - checkout-sdk == Published data: {"orderId":1}'
|
||||
- '== APP - order-processor == Subscriber received: {"orderId":1}'
|
||||
- '== APP - order-processor-sdk == Subscriber received: {"orderId":1}'
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
|
|
@ -59,25 +59,25 @@ The terminal console output should look similar to this:
|
|||
|
||||
```text
|
||||
== APP - checkout-sdk == Published data: {"orderId":1}
|
||||
== APP - order-processor == Subscriber received: {"orderId":1}
|
||||
== APP - order-processor-sdk == Subscriber received: {"orderId":1}
|
||||
== APP - checkout-sdk == Published data: {"orderId":2}
|
||||
== APP - order-processor == Subscriber received: {"orderId":2}
|
||||
== APP - order-processor-sdk == Subscriber received: {"orderId":2}
|
||||
== APP - checkout-sdk == Published data: {"orderId":3}
|
||||
== APP - order-processor == Subscriber received: {"orderId":3}
|
||||
== APP - order-processor-sdk == Subscriber received: {"orderId":3}
|
||||
== APP - checkout-sdk == Published data: {"orderId":4}
|
||||
== APP - order-processor == Subscriber received: {"orderId":4}
|
||||
== APP - order-processor-sdk == Subscriber received: {"orderId":4}
|
||||
== APP - checkout-sdk == Published data: {"orderId":5}
|
||||
== APP - order-processor == Subscriber received: {"orderId":5}
|
||||
== APP - order-processor-sdk == Subscriber received: {"orderId":5}
|
||||
== APP - checkout-sdk == Published data: {"orderId":6}
|
||||
== APP - order-processor == Subscriber received: {"orderId":6}
|
||||
== APP - order-processor-sdk == Subscriber received: {"orderId":6}
|
||||
== APP - checkout-sdk == Published data: {"orderId":7}
|
||||
== APP - order-processor == Subscriber received: {"orderId":7}
|
||||
== APP - order-processor-sdk == Subscriber received: {"orderId":7}
|
||||
== APP - checkout-sdk == Published data: {"orderId":8}
|
||||
== APP - order-processor == Subscriber received: {"orderId":8}
|
||||
== APP - order-processor-sdk == Subscriber received: {"orderId":8}
|
||||
== APP - checkout-sdk == Published data: {"orderId":9}
|
||||
== APP - order-processor == Subscriber received: {"orderId":9}
|
||||
== APP - order-processor-sdk == Subscriber received: {"orderId":9}
|
||||
== APP - checkout-sdk == Published data: {"orderId":10}
|
||||
== APP - order-processor == Subscriber received: {"orderId":10}
|
||||
== APP - order-processor-sdk == Subscriber received: {"orderId":10}
|
||||
```
|
||||
|
||||
3. Stop and clean up application processes
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ version: 1
|
|||
common:
|
||||
resourcesPath: ../../components/
|
||||
apps:
|
||||
- appID: order-processor
|
||||
- appID: order-processor-sdk
|
||||
appDirPath: ./order-processor/
|
||||
appPort: 5002
|
||||
command: ["npm", "run", "start"]
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
Flask
|
||||
dapr
|
||||
cloudevents
|
||||
werkzeug>=3.0.3 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
dapr>=1.14.0a,<1.15.0
|
||||
dapr>=1.15.0rc2
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@ fastapi
|
|||
dapr-ext-fastapi-dev
|
||||
cloudevents
|
||||
uvicorn
|
||||
anyio>=4.4.0 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
Flask
|
||||
dapr>=1.14.0a,<1.15.0
|
||||
dapr>=1.15.0rc2
|
||||
cloudevents
|
||||
uvicorn
|
||||
typing-extensions
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
dapr>=1.14.0a,<1.15.0
|
||||
dapr>=1.15.0rc2
|
||||
typing-extensions
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
version: 1
|
||||
apps:
|
||||
- appDirPath: ./order-processor/
|
||||
appID: order-processor
|
||||
- appID: order-processor
|
||||
appDirPath: ./order-processor/
|
||||
appPort: 7001
|
||||
command: ["dotnet", "run"]
|
||||
- appID: checkout
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
version: 1
|
||||
apps:
|
||||
- appDirPath: ./order-processor/
|
||||
appID: order-processor
|
||||
- appID: order-processor
|
||||
appDirPath: ./order-processor/
|
||||
appPort: 6006
|
||||
command: ["go", "run", "."]
|
||||
- appID: checkout
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
version: 1
|
||||
apps:
|
||||
- appDirPath: ./order-processor/
|
||||
appID: order-processor
|
||||
- appID: order-processor
|
||||
appDirPath: ./order-processor/
|
||||
appPort: 9001
|
||||
command: ["java", "-jar", "target/OrderProcessingService-0.0.1-SNAPSHOT.jar"]
|
||||
- appID: checkout
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
version: 1
|
||||
apps:
|
||||
- appDirPath: ./order-processor/
|
||||
appID: order-processor
|
||||
- appID: order-processor
|
||||
appDirPath: ./order-processor/
|
||||
appPort: 5001
|
||||
command: ["npm", "start"]
|
||||
- appID: checkout
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
version: 1
|
||||
apps:
|
||||
- appDirPath: ./order-processor/
|
||||
appID: order-processor
|
||||
- appID: order-processor
|
||||
appDirPath: ./order-processor/
|
||||
appPort: 8001
|
||||
command: ["python3", "app.py"]
|
||||
- appID: checkout
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
Flask
|
||||
dapr
|
||||
typing-extensions
|
||||
dapr>=1.15.0rc2
|
||||
typing-extensions
|
||||
werkzeug>=3.0.3 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
requests
|
||||
urllib3>=2.2.2 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
dapr>=1.14.0a,<1.15.0
|
||||
dapr>=1.15.0rc2
|
||||
typing-extensions
|
||||
|
|
@ -13,7 +13,8 @@
|
|||
],
|
||||
"program": "${workspaceFolder}/node/app.js",
|
||||
"preLaunchTask": "daprd-debug-node",
|
||||
"postDebugTask": "daprd-down-node"
|
||||
"postDebugTask": "daprd-down-node",
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"type": "python",
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue