mirror of https://github.com/dapr/quickstarts.git
Merge branch 'dapr:master' into fix-556
This commit is contained in:
commit
2358f3e0a1
|
|
@ -0,0 +1,103 @@
|
|||
#
|
||||
# Copyright 2022 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.
|
||||
#
|
||||
|
||||
name: Validate Configuration API
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- feature/new_quickstarts
|
||||
- release-*
|
||||
tags:
|
||||
- v*
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- feature/new_quickstarts
|
||||
- release-*
|
||||
jobs:
|
||||
validate:
|
||||
name: Validate quickstart for `${{ matrix.quickstart_language }}` with `${{ matrix.quickstart_variant }}` on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/master/install
|
||||
GOVER: 1.18
|
||||
KUBERNETES_VERSION: v1.25.3
|
||||
KIND_VERSION: v0.17.0
|
||||
KIND_IMAGE_SHA: sha256:f52781bc0d7a19fb6c405c2af83abfeb311f130707a0e219175677e366cc45d1
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-10.15]
|
||||
quickstart_language: [go, csharp, javascript, python, java]
|
||||
quickstart_variant: [http, sdk]
|
||||
steps:
|
||||
- name: Install docker - MacOS
|
||||
if: matrix.os == 'macos-10.15'
|
||||
uses: docker-practice/actions-setup-docker@v1
|
||||
with:
|
||||
docker_buildx: false
|
||||
docker_version: 20.10.21
|
||||
- name: Set up Go ${{ env.GOVER }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GOVER }}
|
||||
- name: Set up OpenJDK 11
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: 11
|
||||
- name: Install .NET Core
|
||||
uses: actions/setup-dotnet@v1.9.0
|
||||
with:
|
||||
dotnet-version: |
|
||||
6.0.x
|
||||
- name: Determine latest Dapr Runtime version including Pre-releases
|
||||
run: |
|
||||
helm repo add dapr https://dapr.github.io/helm-charts/ && helm repo update && export RUNTIME_VERSION=$(helm search repo dapr/dapr --devel --versions | awk '/dapr\/dapr/ {print $3; exit}' )
|
||||
echo "DAPR_RUNTIME_VERSION=$RUNTIME_VERSION" >> $GITHUB_ENV
|
||||
echo "Found $RUNTIME_VERSION"
|
||||
shell: bash
|
||||
- name: Determine latest Dapr Cli version including Pre-releases
|
||||
run: |
|
||||
export CLI_VERSION=$(curl "https://api.github.com/repos/dapr/cli/releases?per_page=1&page=1" --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' | jq '.[0].tag_name'| tr -d '",v')
|
||||
echo "DAPR_CLI_VERSION=$CLI_VERSION" >> $GITHUB_ENV
|
||||
echo "Found $CLI_VERSION"
|
||||
shell: bash
|
||||
- name: Set up Dapr CLI - Mac/Linux
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: wget -q ${{ env.DAPR_INSTALL_URL }}/install.sh -O - | /bin/bash -s ${{ env.DAPR_CLI_VERSION }}
|
||||
- name: Set up Dapr CLI - Windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: powershell -Command "\$$script=iwr -useb ${{ env.DAPR_INSTALL_URL }}/install.ps1; \$$block=[ScriptBlock]::Create(\$$script); invoke-command -ScriptBlock \$$block -ArgumentList ${{ env.DAPR_CLI_VERSION }}"
|
||||
- name: Install Dapr
|
||||
run: |
|
||||
export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
|
||||
dapr init --runtime-version=${{ env.DAPR_RUNTIME_VERSION }}
|
||||
dapr --version
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install utilities dependencies
|
||||
run: |
|
||||
echo "PATH=$PATH:$HOME/.local/bin" >> $GITHUB_ENV
|
||||
pip3 install setuptools wheel
|
||||
pip3 install mechanical-markdown
|
||||
- name: Validate ${{ matrix.quickstart_language }} ${{ matrix.quickstart_variant }} Configuration API
|
||||
run: |
|
||||
pushd configuration/${{ matrix.quickstart_language }}/${{ matrix.quickstart_variant }}
|
||||
make validate
|
||||
popd
|
||||
- name: Linkcheck README.md
|
||||
run: |
|
||||
make validate
|
||||
|
|
@ -17,8 +17,7 @@ Pick a building block API (for example, pub-sub, state management) and rapidly t
|
|||
| [Bindings](./bindings/) | Work with external systems using input bindings to respond to events and output bindings to call operations|
|
||||
| [Secrets Management](./secrets_management/) | Securely fetch secrets |
|
||||
| Actors | Coming soon... |
|
||||
| Observability | Coming soon... |
|
||||
| Configuration | Coming soon... |
|
||||
| [Configuration](./configuration) | Get configuration items as key/value pairs or subscribe to changes whenever a configuration item changes |
|
||||
|
||||
### Tutorials
|
||||
Go deeper into a topic or scenario, oftentimes using building block APIs together to solve problems (for example, build a distributed calculator, build and deploy an app to Kubernetes).
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: configstore
|
||||
spec:
|
||||
type: configuration.redis
|
||||
version: v1
|
||||
metadata:
|
||||
- name: redisHost
|
||||
value: localhost:6379
|
||||
- name: redisPassword
|
||||
value: ""
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
# Dapr Configuration API
|
||||
|
||||
In this quickstart, you'll create a microservice which makes use of Dapr's Configuration API. Configuration items are key/value pairs containing configuration data such as app ids, partition keys, database names etc. The service gets configuration items from the configuration store and subscribes for configuration updates.
|
||||
|
||||
Visit [this](https://docs.dapr.io/developing-applications/building-blocks/configuration/) link for more information about Dapr and Configuration 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 service:
|
||||
|
||||
- Dotnet service `order-processor`
|
||||
|
||||
## Add configuration items to the config store
|
||||
|
||||
### Prerequisite
|
||||
|
||||
- Locally running redis container - a redis container named `dapr_redis` is automatically created when you run `dapr init`
|
||||
- Open a new terminal and set values for config items `orderId1` and `orderId2` by using the command below
|
||||
|
||||
<!-- STEP
|
||||
name: Add configuration items
|
||||
expected_stdout_lines:
|
||||
- 'OK'
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "101" orderId2 "102"
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Run order-processor
|
||||
|
||||
1. Open a new terminal and navigate to `order-processor` directory.
|
||||
2. Run the service app with Dapr.
|
||||
|
||||
<!-- STEP
|
||||
name: Run order-processor service
|
||||
expected_stdout_lines:
|
||||
- '== APP == Configuration for orderId1: {"orderId1":{"value":"101"}}'
|
||||
- '== APP == Configuration for orderId2: {"orderId2":{"value":"102"}}'
|
||||
- '== APP == App subscribed to config changes with subscription id:'
|
||||
- '== APP == App unsubscribed from config updates'
|
||||
- "Exited App successfully"
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
dapr run --app-id order-processor-http --components-path ../../../components/ --app-port 7001 -- dotnet run --project .
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## (Optional) Update value of config items
|
||||
|
||||
1. Keep the `order-processor` app running and open a separate terminal
|
||||
2. Change the values of `orderId1` and `orderId2` using the command below
|
||||
3. `order-processor` app gets the updated values of config items
|
||||
|
||||
<!-- STEP
|
||||
name: Update config items
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "103" orderId2 "104"
|
||||
```
|
||||
|
||||
<!--END_STEP -->
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
var baseURL = (Environment.GetEnvironmentVariable("BASE_URL") ?? "http://localhost") + ":"
|
||||
+ (Environment.GetEnvironmentVariable("DAPR_HTTP_PORT") ?? "3500");
|
||||
|
||||
const string DAPR_CONFIGURATION_STORE = "configstore";
|
||||
|
||||
var CONFIGURATION_ITEMS = new List<string> { "orderId1", "orderId2" };
|
||||
|
||||
var httpClient = new HttpClient();
|
||||
httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
|
||||
|
||||
foreach (var item in CONFIGURATION_ITEMS)
|
||||
{
|
||||
// Get config items from the config store
|
||||
try
|
||||
{
|
||||
var response = await httpClient.GetStringAsync($"{baseURL}/v1.0-alpha1/configuration/{DAPR_CONFIGURATION_STORE}?key={item.ToString()}");
|
||||
Console.WriteLine("Configuration for " + item + ": " + response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Could not get config item, err:" + ex.Message);
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async Task<string> subscribeToConfigUpdates()
|
||||
{
|
||||
// Add delay to allow app channel to be ready
|
||||
Thread.Sleep(3000);
|
||||
try
|
||||
{
|
||||
var subscription = await httpClient.GetStringAsync($"{baseURL}/v1.0-alpha1/configuration/{DAPR_CONFIGURATION_STORE}/subscribe");
|
||||
if (subscription.Contains("errorCode"))
|
||||
{
|
||||
Console.WriteLine("Error subscribing to config updates, err:" + subscription);
|
||||
Environment.Exit(1);
|
||||
return string.Empty;
|
||||
}
|
||||
dynamic data = JObject.Parse(subscription);
|
||||
Console.WriteLine("App subscribed to config changes with subscription id: " + data.id);
|
||||
return data.id;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error subscribing to config updates, err:" + ex.Message);
|
||||
Environment.Exit(1);
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
async Task readConfigurationChanges()
|
||||
{
|
||||
// Create POST endpoint to receive config updates
|
||||
app.MapPost("/configuration/configstore/{item}", async (HttpRequest request) =>
|
||||
{
|
||||
using var sr = new StreamReader(request.Body);
|
||||
var config = await sr.ReadToEndAsync();
|
||||
dynamic update = JObject.Parse(config);
|
||||
Console.WriteLine("Configuration update " + update.items.ToString(Formatting.None));
|
||||
});
|
||||
await app.StartAsync();
|
||||
}
|
||||
|
||||
await readConfigurationChanges();
|
||||
string subscriptionId = await subscribeToConfigUpdates();
|
||||
|
||||
// Unsubscribe to config updates and exit app after 20 seconds
|
||||
await Task.Delay(20000);
|
||||
try
|
||||
{
|
||||
string unsubscribe = await httpClient.GetStringAsync($"{baseURL}/v1.0-alpha1/configuration/{DAPR_CONFIGURATION_STORE}/{subscriptionId}/unsubscribe");
|
||||
if (unsubscribe.Contains("true"))
|
||||
{
|
||||
Console.WriteLine("App unsubscribed from config updates");
|
||||
Environment.Exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Error unsubscribing from config updates, err:" + unsubscribe);
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error unsubscribing from config updates, err:" + ex.Message);
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"CheckoutService": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": "true",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "http://localhost:7001",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
# Dapr Configuration API
|
||||
|
||||
In this quickstart, you'll create a microservice which makes use of Dapr's Configuration API. Configuration items are key/value pairs containing configuration data such as app ids, partition keys, database names etc. The service gets configuration items from the configuration store and subscribes for configuration updates.
|
||||
|
||||
Visit [this](https://docs.dapr.io/developing-applications/building-blocks/configuration/) link for more information about Dapr and Configuration API.
|
||||
|
||||
This quickstart includes one service:
|
||||
|
||||
- Dotnet service `order-processor`
|
||||
|
||||
## Add configuration items to the config store
|
||||
|
||||
### Prerequisite
|
||||
|
||||
- Locally running redis container - a redis container named `dapr_redis` is automatically created when you run `dapr init`
|
||||
- Open a new terminal and set values for config items `orderId1` and `orderId2` by using the command below
|
||||
|
||||
<!-- STEP
|
||||
name: Add configuration items
|
||||
expected_stdout_lines:
|
||||
- 'OK'
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "101" orderId2 "102"
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Run order-processor
|
||||
|
||||
1. Open a new terminal and navigate to `order-processor` directory.
|
||||
2. Run the service app with Dapr.
|
||||
|
||||
<!-- STEP
|
||||
name: Run order-processor service
|
||||
expected_stdout_lines:
|
||||
- '== APP == Configuration for orderId1: {"Value":"101","Version":"","Metadata":{}}'
|
||||
- '== APP == Configuration for orderId2: {"Value":"102","Version":"","Metadata":{}}'
|
||||
- '== APP == App subscribed to config changes with subscription id:'
|
||||
- '== APP == App unsubscribed from config changes'
|
||||
- "Exited App successfully"
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
dapr run --app-id order-processor-http --components-path ../../../components/ --app-port 7001 -- dotnet run --project .
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## (Optional) Update value of config items
|
||||
|
||||
1. Keep the `order-processor` app running and open a separate terminal
|
||||
2. Change the values of `orderId1` and `orderId2` using the command below
|
||||
3. `order-processor` app gets the updated values of config items
|
||||
|
||||
<!-- STEP
|
||||
name: Update config items
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "103" orderId2 "104"
|
||||
```
|
||||
|
||||
<!--END_STEP -->
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Client;
|
||||
|
||||
const string DAPR_CONFIGURATION_STORE = "configstore";
|
||||
var CONFIGURATION_ITEMS = new List<string> { "orderId1", "orderId2" };
|
||||
string subscriptionId = string.Empty;
|
||||
|
||||
var client = new DaprClientBuilder().Build();
|
||||
|
||||
// Get config from configuration store
|
||||
GetConfigurationResponse config = await client.GetConfiguration(DAPR_CONFIGURATION_STORE, CONFIGURATION_ITEMS);
|
||||
foreach (var item in config.Items)
|
||||
{
|
||||
var cfg = System.Text.Json.JsonSerializer.Serialize(item.Value);
|
||||
Console.WriteLine("Configuration for " + item.Key + ": " + cfg);
|
||||
}
|
||||
|
||||
// Exit the app after 20 seconds
|
||||
var shutdownTimer = new System.Timers.Timer();
|
||||
shutdownTimer.Interval = 20000;
|
||||
shutdownTimer.Elapsed += (o, e) => unsubscribe(subscriptionId);
|
||||
shutdownTimer.Start();
|
||||
|
||||
// Subscribe for configuration changes
|
||||
SubscribeConfigurationResponse subscribe = await client.SubscribeConfiguration(DAPR_CONFIGURATION_STORE, CONFIGURATION_ITEMS);
|
||||
|
||||
// Print configuration changes
|
||||
await foreach (var configItem in subscribe.Source)
|
||||
{
|
||||
// First invocation when app subscribes to config changes only returns subscription id
|
||||
if (configItem.Keys.Count == 0)
|
||||
{
|
||||
Console.WriteLine("App subscribed to config changes with subscription id: " + subscribe.Id);
|
||||
subscriptionId = subscribe.Id;
|
||||
continue;
|
||||
}
|
||||
var cfg = System.Text.Json.JsonSerializer.Serialize(configItem);
|
||||
Console.WriteLine("Configuration update " + cfg);
|
||||
}
|
||||
|
||||
|
||||
// Unsubscribe to config updates and exit the app
|
||||
void unsubscribe(string subscriptionId)
|
||||
{
|
||||
try
|
||||
{
|
||||
client.UnsubscribeConfiguration(DAPR_CONFIGURATION_STORE, subscriptionId);
|
||||
Console.WriteLine("App unsubscribed from config changes");
|
||||
Environment.Exit(0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error unsubscribing from config updates: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dapr.AspNetCore" Version="1.9.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
# Dapr Configuration API
|
||||
|
||||
In this quickstart, you'll create a microservice which makes use of Dapr's Configuration API. Configuration items are key/value pairs containing configuration data such as app ids, partition keys, database names etc. The service gets configuration items from the configuration store and subscribes for configuration updates.
|
||||
|
||||
Visit [this](https://docs.dapr.io/developing-applications/building-blocks/configuration/) link for more information about Dapr and Configuration 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 service:
|
||||
|
||||
- Go service `order-processor`
|
||||
|
||||
## Add configuration items to the config store
|
||||
|
||||
### Prerequisite
|
||||
|
||||
- Locally running redis container - a redis container named `dapr_redis` is automatically created when you run `dapr init`
|
||||
- Open a new terminal and set values for config items `orderId1` and `orderId2` by using the command below
|
||||
|
||||
<!-- STEP
|
||||
name: Add configuration items
|
||||
expected_stdout_lines:
|
||||
- 'OK'
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "101" orderId2 "102"
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Run order-processor
|
||||
|
||||
1. Navigate to `order-processor` directory.
|
||||
2. Run the service app with Dapr.
|
||||
|
||||
<!-- STEP
|
||||
name: Run order-processor service
|
||||
expected_stdout_lines:
|
||||
- '== APP == Configuration for orderId1: {"orderId1":{"value":"101"}}'
|
||||
- '== APP == Configuration for orderId2: {"orderId2":{"value":"102"}}'
|
||||
- '== APP == App subscribed to config changes with subscription id:'
|
||||
- '== APP == Shutting down HTTP server'
|
||||
- '== APP == App unsubscribed from config changes'
|
||||
- "Exited App successfully"
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
dapr run --app-id order-processor --app-port 6001 --components-path ../../../components -- go run .
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## (Optional) Update value of config items
|
||||
|
||||
1. Keep the `order-processor` app running and open a separate terminal
|
||||
2. Change the values of `orderId1` and `orderId2` using the command below
|
||||
3. `order-processor` app gets the updated values of config items
|
||||
|
||||
<!-- STEP
|
||||
name: Update config items
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "103" orderId2 "104"
|
||||
```
|
||||
|
||||
<!--END_STEP -->
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
var DAPR_HOST, DAPR_HTTP_PORT string
|
||||
var okHost, okPort bool
|
||||
var appPort = "6001"
|
||||
var DAPR_CONFIGURATION_STORE = "configstore"
|
||||
var CONFIGURATION_ITEMS = []string{"orderId1", "orderId2"}
|
||||
|
||||
func main() {
|
||||
if DAPR_HOST, okHost = os.LookupEnv("DAPR_HOST"); !okHost {
|
||||
DAPR_HOST = "http://localhost"
|
||||
}
|
||||
if DAPR_HTTP_PORT, okPort = os.LookupEnv("DAPR_HTTP_PORT"); !okPort {
|
||||
DAPR_HTTP_PORT = "3500"
|
||||
}
|
||||
if value, ok := os.LookupEnv("APP_PORT"); ok {
|
||||
appPort = value
|
||||
}
|
||||
|
||||
// Get config items from the config store
|
||||
for _, item := range CONFIGURATION_ITEMS {
|
||||
getResponse, err := http.Get(DAPR_HOST + ":" + DAPR_HTTP_PORT + "/v1.0-alpha1/configuration/" + DAPR_CONFIGURATION_STORE + "?key=" + item)
|
||||
if err != nil {
|
||||
fmt.Print("Could not get config item, err:" + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
result, _ := ioutil.ReadAll(getResponse.Body)
|
||||
fmt.Println("Configuration for "+item+":", string(result))
|
||||
}
|
||||
|
||||
var subscriptionId string
|
||||
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(2)
|
||||
// Start HTTP Server to receive config updates for 20 seconds and then shutdown and unsubscribe from config updates
|
||||
go func() {
|
||||
startServerToListen(&subscriptionId)
|
||||
wg.Done()
|
||||
}()
|
||||
// Subscribe for config updates
|
||||
go func() {
|
||||
subscribeToConfigUpdates(&subscriptionId)
|
||||
wg.Done()
|
||||
}()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func subscribeToConfigUpdates(subscriptionId *string) {
|
||||
// Add delay to allow app channel to be ready
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
subscription, err := http.Get(DAPR_HOST + ":" + DAPR_HTTP_PORT + "/v1.0-alpha1/configuration/" + DAPR_CONFIGURATION_STORE + "/subscribe")
|
||||
if err != nil {
|
||||
fmt.Println("Error subscribing to config updates, err:" + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
sub, err := ioutil.ReadAll(subscription.Body)
|
||||
if err != nil {
|
||||
fmt.Print("Unable to read subscription id, err: " + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
if !strings.Contains(string(sub), "errorCode") {
|
||||
var subid map[string]interface{}
|
||||
json.Unmarshal(sub, &subid)
|
||||
fmt.Println("App subscribed to config changes with subscription id:", subid["id"])
|
||||
*subscriptionId = subid["id"].(string)
|
||||
} else {
|
||||
fmt.Println("Error subscribing to config updates: ", string(sub))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func startServerToListen(subscriptionId *string) {
|
||||
r := mux.NewRouter()
|
||||
httpServer := http.Server{
|
||||
Addr: ":" + appPort,
|
||||
Handler: r,
|
||||
}
|
||||
r.HandleFunc("/configuration/configstore/{configItem}", configUpdateHandler).Methods("POST")
|
||||
|
||||
// Unsubscribe to config updates and shutdown http server after 20 seconds
|
||||
time.AfterFunc(20*time.Second, func() {
|
||||
unsubscribeFromConfigUpdates(*subscriptionId)
|
||||
fmt.Println("Shutting down HTTP server")
|
||||
err := httpServer.Shutdown(context.Background())
|
||||
if err != nil {
|
||||
fmt.Println("Error shutting down HTTP server, err:" + err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
// Start HTTP server
|
||||
if err := httpServer.ListenAndServe(); err != nil {
|
||||
log.Println("HTTP server error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func unsubscribeFromConfigUpdates(subscriptionId string) {
|
||||
unsubscribe, err := http.Get(DAPR_HOST + ":" + DAPR_HTTP_PORT + "/v1.0-alpha1/configuration/" + DAPR_CONFIGURATION_STORE + "/" + subscriptionId + "/unsubscribe")
|
||||
if err != nil {
|
||||
fmt.Println("Error unsubscribing from config updates, err:" + err.Error())
|
||||
}
|
||||
unsub, err := ioutil.ReadAll(unsubscribe.Body)
|
||||
if err != nil {
|
||||
fmt.Print("Unable to read unsubscribe response, err: " + err.Error())
|
||||
}
|
||||
if strings.Contains(string(unsub), "true") {
|
||||
fmt.Println("App unsubscribed from config changes")
|
||||
return
|
||||
} else {
|
||||
fmt.Println("Error unsubscribing from config updates: ", string(unsub))
|
||||
}
|
||||
}
|
||||
|
||||
func configUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
var notification map[string]interface{}
|
||||
json.Unmarshal(body, ¬ification)
|
||||
update, _ := json.Marshal(notification["items"])
|
||||
fmt.Println("Configuration update", string(update))
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
module dapr_example
|
||||
|
||||
go 1.18
|
||||
|
||||
require github.com/gorilla/mux v1.8.0
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
# Dapr Configuration API
|
||||
|
||||
In this quickstart, you'll create a microservice which makes use of Dapr's Configuration API. Configuration items are key/value pairs containing configuration data such as app ids, partition keys, database names etc. The service gets configuration items from the configuration store and subscribes for configuration updates.
|
||||
|
||||
Visit [this](https://docs.dapr.io/developing-applications/building-blocks/configuration/) link for more information about Dapr and Configuration API.
|
||||
|
||||
This quickstart includes one service:
|
||||
|
||||
- Go service `order-processor`
|
||||
|
||||
## Add configuration items to the config store
|
||||
|
||||
### Prerequisite
|
||||
|
||||
- Locally running redis container - a redis container named `dapr_redis` is automatically created when you run `dapr init`
|
||||
- Open a new terminal and set values for config items `orderId1` and `orderId2` by using the command below
|
||||
|
||||
<!-- STEP
|
||||
name: Add configuration items
|
||||
expected_stdout_lines:
|
||||
- 'OK'
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "101" orderId2 "102"
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Run order-processor
|
||||
|
||||
1. Open a new terminal and navigate to `order-processor` directory.
|
||||
2. Run the service app with Dapr.
|
||||
|
||||
<!-- STEP
|
||||
name: Run order-processor service
|
||||
expected_stdout_lines:
|
||||
- '== APP == Configuration for orderId1: {"Value":"101","Version":"","Metadata":null}'
|
||||
- '== APP == Configuration for orderId2: {"Value":"102","Version":"","Metadata":null}'
|
||||
- '== APP == App subscribed to config changes with subscription id:'
|
||||
- '== APP == App unsubscribed to config changes'
|
||||
- "Exited App successfully"
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
dapr run --app-id order-processor --app-port 6001 --components-path ../../../components -- go run .
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## (Optional) Update value of config items
|
||||
|
||||
1. Keep the `order-processor` app running and open a separate terminal
|
||||
2. Change the values of `orderId1` and `orderId2` using the command below
|
||||
3. `order-processor` app gets the updated values of config items
|
||||
|
||||
<!-- STEP
|
||||
name: Update config items
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "103" orderId2 "104"
|
||||
```
|
||||
|
||||
<!--END_STEP -->
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
dapr "github.com/dapr/go-sdk/client"
|
||||
)
|
||||
|
||||
var DAPR_CONFIGURATION_STORE = "configstore"
|
||||
var CONFIGURATION_ITEMS = []string{"orderId1", "orderId2"}
|
||||
|
||||
func main() {
|
||||
client, err := dapr.NewClient()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Get config items from config store
|
||||
for _, item := range CONFIGURATION_ITEMS {
|
||||
config, err := client.GetConfigurationItem(ctx, DAPR_CONFIGURATION_STORE, item)
|
||||
if err != nil {
|
||||
fmt.Printf("Could not get config item, err:" + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
c, _ := json.Marshal(config)
|
||||
fmt.Println("Configuration for " + item + ": " + string(c))
|
||||
}
|
||||
|
||||
var subscriptionId string
|
||||
|
||||
// Subscribe for config changes
|
||||
err = client.SubscribeConfigurationItems(ctx, DAPR_CONFIGURATION_STORE, CONFIGURATION_ITEMS, func(id string, config map[string]*dapr.ConfigurationItem) {
|
||||
// First invocation when app subscribes to config changes only returns subscription id
|
||||
if len(config) == 0 {
|
||||
fmt.Println("App subscribed to config changes with subscription id: " + id)
|
||||
subscriptionId = id
|
||||
return
|
||||
}
|
||||
// Print config changes
|
||||
c, _ := json.Marshal(config)
|
||||
fmt.Println("Configuration update " + string(c))
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println("Error subscribing to config updates, err:" + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Unsubscribe to config updates and exit app after 20 seconds
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = client.UnsubscribeConfigurationItems(context.Background(), DAPR_CONFIGURATION_STORE, subscriptionId)
|
||||
if err != nil {
|
||||
fmt.Println("Error unsubscribing to config updates, err:" + err.Error())
|
||||
} else {
|
||||
fmt.Println("App unsubscribed to config changes")
|
||||
}
|
||||
os.Exit(0)
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
module dapr_example
|
||||
|
||||
go 1.18
|
||||
|
||||
require github.com/dapr/go-sdk v1.6.0
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/stretchr/testify v1.8.0 // indirect
|
||||
golang.org/x/net v0.0.0-20220927171203-f486391704dc // indirect
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f // indirect
|
||||
google.golang.org/grpc v1.48.0 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/dapr/go-sdk v1.6.0 h1:jg5A2khSCHF8bGZsig5RWN/gD0jjitszc2V6Uq2pPdY=
|
||||
github.com/dapr/go-sdk v1.6.0/go.mod h1:KLQBltoD9K0w5hKTihdcyg9Epob9gypwL5dYcQzPro4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20220927171203-f486391704dc h1:FxpXZdoBqT8RjqTy6i1E8nXHhW21wK7ptQ/EPIGxzPQ=
|
||||
golang.org/x/net v0.0.0-20220927171203-f486391704dc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f h1:kYlCnpX4eB0QEnXm12j4DAX4yrjjhJmsyuWtSSZ+Buo=
|
||||
google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
|
||||
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
# Dapr Configuration API
|
||||
|
||||
In this quickstart, you'll create a microservice which makes use of Dapr's Configuration API. Configuration items are key/value pairs containing configuration data such as app ids, partition keys, database names etc. The service gets configuration items from the configuration store and subscribes for configuration updates.
|
||||
|
||||
Visit [this](https://docs.dapr.io/developing-applications/building-blocks/configuration/) link for more information about Dapr and Configuration 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 service:
|
||||
|
||||
- Java service `order-processor`
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Maven 3.x](https://maven.apache.org/install.html)
|
||||
- Java JDK 11 (or greater):
|
||||
- [Microsoft JDK 11](https://docs.microsoft.com/en-us/java/openjdk/download#openjdk-11)
|
||||
- [Oracle JDK 11](https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK11)
|
||||
- [OpenJDK 11](https://jdk.java.net/11/)
|
||||
- Locally running redis container - a redis container named `dapr_redis` is automatically created when you run `dapr init`
|
||||
|
||||
## Add configuration items to the config store
|
||||
|
||||
- Open a new terminal and set values for config items `orderId1` and `orderId2` by using the command below
|
||||
|
||||
<!-- STEP
|
||||
name: Add configuration items
|
||||
expected_stdout_lines:
|
||||
- 'OK'
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "101" orderId2 "102"
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Build the Java file
|
||||
|
||||
<!-- STEP
|
||||
name: Build Java file
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
mvn clean install
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Run order-processor
|
||||
|
||||
1. Navigate to `order-processor` directory.
|
||||
2. Run the service app with Dapr.
|
||||
|
||||
<!-- STEP
|
||||
name: Run order-processor service
|
||||
expected_stdout_lines:
|
||||
- '== APP == Configuration for orderId1:{"orderId1":{"value":"101"}}'
|
||||
- '== APP == Configuration for orderId2:{"orderId2":{"value":"102"}}'
|
||||
- '== APP == App subscribed to config changes with subscription id:'
|
||||
- '= APP == App unsubscribed from config changes'
|
||||
- '== APP == Shutting down spring app'
|
||||
- "Exited App successfully"
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
dapr run --app-id order-processor --app-port 6001 --components-path ../../../components -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## (Optional) Update value of config items
|
||||
|
||||
1. Keep the `order-processor` app running and open a separate terminal
|
||||
2. Change the values of `orderId1` and `orderId2` using the command below
|
||||
3. `order-processor` app gets the updated values of config items
|
||||
|
||||
<!-- STEP
|
||||
name: Update config items
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "103" orderId2 "104"
|
||||
```
|
||||
|
||||
<!--END_STEP -->
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.6.3</version>
|
||||
<relativePath/>
|
||||
<!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>com.service</groupId>
|
||||
<artifactId>OrderProcessingService</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>OrderProcessingService</name>
|
||||
<description>Demo for Dapr Configuration API</description>
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20220924</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>2.6.4</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
package com.service;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.json.*;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@SpringBootApplication
|
||||
public class OrderProcessingServiceApplication {
|
||||
private static final String DAPR_CONFIGURATION_STORE = "configstore";
|
||||
private static List<String> CONFIGURATION_ITEMS = List.of("orderId1",
|
||||
"orderId2");
|
||||
private static String DAPR_HOST = System.getenv().getOrDefault("DAPR_HOST",
|
||||
"http://localhost");
|
||||
private static String DAPR_HTTP_PORT = System.getenv().getOrDefault("DAPR_HTTP_PORT", "3500");
|
||||
private static String APP_PORT = System.getenv().getOrDefault("APP_PORT", "6001");
|
||||
private static final HttpClient httpClient = HttpClient.newBuilder()
|
||||
.version(HttpClient.Version.HTTP_2)
|
||||
.connectTimeout(Duration.ofSeconds(10))
|
||||
.build();
|
||||
|
||||
public static void main(String[] args) throws IOException, URISyntaxException, InterruptedException {
|
||||
URI baseUrl = new URI(DAPR_HOST + ":" + DAPR_HTTP_PORT);
|
||||
// Get config items from the config store
|
||||
try {
|
||||
for (String configurationItem : CONFIGURATION_ITEMS) {
|
||||
URI uri = baseUrl
|
||||
.resolve(
|
||||
"/v1.0-alpha1/configuration/" + DAPR_CONFIGURATION_STORE + "?key=" +
|
||||
configurationItem);
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.GET()
|
||||
.uri(uri)
|
||||
.build();
|
||||
HttpResponse<String> response = httpClient.send(request,
|
||||
HttpResponse.BodyHandlers.ofString());
|
||||
System.out.println("Configuration for " + configurationItem + ":" +
|
||||
response.body());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Could not get config item, err:" + e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// Create Spring Application to listen to configuration updates
|
||||
SpringApplication app = new SpringApplication(OrderProcessingServiceApplication.class);
|
||||
app.setDefaultProperties(Collections.singletonMap("server.port", APP_PORT));
|
||||
|
||||
// Start the application
|
||||
ConfigurableApplicationContext context = app.run(args);
|
||||
|
||||
// Add delay to allow app channel to be ready
|
||||
Thread.sleep(3000);
|
||||
|
||||
// Subscribe to Configuration Updates
|
||||
String subscriptionId = null;
|
||||
try {
|
||||
URI uri = baseUrl
|
||||
.resolve("/v1.0-alpha1/configuration/" + DAPR_CONFIGURATION_STORE + "/subscribe");
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.GET()
|
||||
.uri(uri)
|
||||
.build();
|
||||
HttpResponse<String> response = httpClient.send(request,
|
||||
HttpResponse.BodyHandlers.ofString());
|
||||
JSONObject subscription = new JSONObject(response.body());
|
||||
subscriptionId = subscription.getString("id");
|
||||
System.out.println("App subscribed to config changes with subscription id:" + subscriptionId);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error subscribing to config updates, err:" + e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// Receive config updates for 20 seconds, then unsubscribe from config updates and shutdown spring app
|
||||
Thread.sleep(20000);
|
||||
try {
|
||||
// unsubscribe from config updates
|
||||
URI uri = baseUrl
|
||||
.resolve("/v1.0-alpha1/configuration/" + DAPR_CONFIGURATION_STORE + "/" + subscriptionId
|
||||
+ "/unsubscribe");
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.GET()
|
||||
.uri(uri)
|
||||
.build();
|
||||
HttpResponse<String> response = httpClient.send(request,
|
||||
HttpResponse.BodyHandlers.ofString());
|
||||
if (response.body().contains("true")) {
|
||||
|
||||
System.out.println("App unsubscribed from config changes");
|
||||
} else {
|
||||
|
||||
System.out.println("Error unsubscribing from config updates: " + response.body());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error unsubscribing from config updates, err:" + e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// Shutdown spring app
|
||||
System.out.println("Shutting down spring app");
|
||||
SpringApplication.exit(context, () -> 0);
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.service.controller;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class OrderProcessingServiceController {
|
||||
/**
|
||||
* Read configuration updates from config store
|
||||
*
|
||||
* @param body Request body
|
||||
* @return ResponseEntity Returns ResponseEntity.ok()
|
||||
*/
|
||||
@PostMapping(path = "/configuration/configstore/{configItem}", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ResponseEntity<?> readUpdates(@RequestBody Map<String, Object> body) {
|
||||
System.out.println("Configuration update "+ body.get("items"));
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
# Dapr Configuration API
|
||||
|
||||
In this quickstart, you'll create a microservice which makes use of Dapr's Configuration API. Configuration items are key/value pairs containing configuration data such as app ids, partition keys, database names etc. The service gets configuration items from the configuration store and subscribes for configuration updates.
|
||||
|
||||
Visit [this](https://docs.dapr.io/developing-applications/building-blocks/configuration/) link for more information about Dapr and Configuration API.
|
||||
|
||||
This quickstart includes one service:
|
||||
|
||||
- Java service `order-processor`
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Maven 3.x](https://maven.apache.org/install.html)
|
||||
- Java JDK 11 (or greater):
|
||||
- [Microsoft JDK 11](https://docs.microsoft.com/en-us/java/openjdk/download#openjdk-11)
|
||||
- [Oracle JDK 11](https://www.oracle.com/technetwork/java/javase/downloads/index.html#JDK11)
|
||||
- [OpenJDK 11](https://jdk.java.net/11/)
|
||||
- Locally running redis container - a redis container named `dapr_redis` is automatically created when you run `dapr init`
|
||||
|
||||
## Add configuration items to the config store
|
||||
|
||||
- Open a new terminal and set values for config items `orderId1` and `orderId2` by using the command below
|
||||
|
||||
<!-- STEP
|
||||
name: Add configuration items
|
||||
expected_stdout_lines:
|
||||
- 'OK'
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "101" orderId2 "102"
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Build the Java file
|
||||
|
||||
<!-- STEP
|
||||
name: Build Java file
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
mvn clean install
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Run order-processor
|
||||
|
||||
1. Navigate to `order-processor` directory.
|
||||
2. Run the service app with Dapr.
|
||||
|
||||
<!-- STEP
|
||||
name: Run order-processor service
|
||||
expected_stdout_lines:
|
||||
- "== APP == Configuration for orderId1: {'value':'101'}"
|
||||
- "== APP == Configuration for orderId2: {'value':'102'}"
|
||||
- '== APP == App subscribed to config changes with subscription id:'
|
||||
- '== APP == App unsubscribed to config changes'
|
||||
- "Exited App successfully"
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
dapr run --app-id order-processor --components-path ../../../components -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## (Optional) Update value of config items
|
||||
|
||||
1. Keep the `order-processor` app running and open a separate terminal
|
||||
2. Change the values of `orderId1` and `orderId2` using the command below
|
||||
3. `order-processor` app gets the updated values of config items
|
||||
|
||||
<!-- STEP
|
||||
name: Update config items
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "103" orderId2 "104"
|
||||
```
|
||||
|
||||
<!--END_STEP -->
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.service</groupId>
|
||||
<artifactId>OrderProcessingService</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>OrderProcessingService</name>
|
||||
<description>Demo for Dapr Configuration API</description>
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<slf4jVersion>1.6.1</slf4jVersion>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.dapr</groupId>
|
||||
<artifactId>dapr-sdk</artifactId>
|
||||
<version>1.7.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<version>3.4.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>2.6.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<mainClass>
|
||||
com.service.OrderProcessingServiceApplication
|
||||
</mainClass>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.service;
|
||||
|
||||
import io.dapr.client.DaprClientBuilder;
|
||||
import io.dapr.client.DaprPreviewClient;
|
||||
import io.dapr.client.domain.ConfigurationItem;
|
||||
import io.dapr.client.domain.SubscribeConfigurationResponse;
|
||||
import io.dapr.client.domain.UnsubscribeConfigurationResponse;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class OrderProcessingServiceApplication {
|
||||
private static final String DAPR_CONFIGURATON_STORE = "configstore";
|
||||
private static List<String> CONFIGURATION_ITEMS = List.of("orderId1", "orderId2");
|
||||
private static String subscriptionId = null;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Get config items from the config store
|
||||
try (DaprPreviewClient client = (new DaprClientBuilder()).buildPreviewClient()) {
|
||||
for (String configurationItem : CONFIGURATION_ITEMS) {
|
||||
ConfigurationItem item = client.getConfiguration(DAPR_CONFIGURATON_STORE, configurationItem).block();
|
||||
System.out.println("Configuration for " + configurationItem + ": {'value':'" + item.getValue() + "'}");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Could not get config item, err:" + e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
try (DaprPreviewClient client = (new DaprClientBuilder()).buildPreviewClient()) {
|
||||
// Subscribe for config changes
|
||||
Flux<SubscribeConfigurationResponse> subscription = client.subscribeConfiguration(DAPR_CONFIGURATON_STORE,
|
||||
CONFIGURATION_ITEMS.toArray(String[]::new));
|
||||
|
||||
// Read config changes for 20 seconds
|
||||
subscription.subscribe((response) -> {
|
||||
// First ever response contains the subscription id
|
||||
if (response.getItems() == null || response.getItems().isEmpty()) {
|
||||
subscriptionId = response.getSubscriptionId();
|
||||
System.out.println("App subscribed to config changes with subscription id: " + subscriptionId);
|
||||
} else {
|
||||
response.getItems().forEach((k, v) -> {
|
||||
System.out.println("Configuration update for " + k + ": {'value':'" + v.getValue() + "'}");
|
||||
});
|
||||
}
|
||||
});
|
||||
Thread.sleep(20000);
|
||||
// Unsubscribe from config changes
|
||||
UnsubscribeConfigurationResponse unsubscribe = client
|
||||
.unsubscribeConfiguration(subscriptionId, DAPR_CONFIGURATON_STORE).block();
|
||||
if (unsubscribe.getIsUnsubscribed()) {
|
||||
System.out.println("App unsubscribed to config changes");
|
||||
} else {
|
||||
System.out.println("Error unsubscribing to config updates, err:" + unsubscribe.getMessage());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error reading config updates, err:" + e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
##lint files
|
||||
*.cjs
|
||||
|
||||
##node modules
|
||||
node_modules
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
# Dapr Configuration API
|
||||
|
||||
In this quickstart, you'll create a microservice which makes use of Dapr's Configuration API. Configuration items are key/value pairs containing configuration data such as app ids, partition keys, database names etc. The service gets configuration items from the configuration store and subscribes for configuration updates.
|
||||
|
||||
Visit [this](https://docs.dapr.io/developing-applications/building-blocks/configuration/) link for more information about Dapr and Configuration 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 service:
|
||||
|
||||
- Node service `order-processor`
|
||||
|
||||
## Add configuration items to the config store
|
||||
|
||||
### Prerequisite
|
||||
|
||||
- Locally running redis container - a redis container named `dapr_redis` is automatically created when you run `dapr init`
|
||||
- Open a new terminal and set values for config items `orderId1` and `orderId2` by using the command below
|
||||
|
||||
<!-- STEP
|
||||
name: Add configuration items
|
||||
expected_stdout_lines:
|
||||
- 'OK'
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "101" orderId2 "102"
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Run order-processor
|
||||
|
||||
1. Navigate to folder and install dependencies:
|
||||
|
||||
<!-- STEP
|
||||
name: Install Node dependencies
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
npm install
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
1. Run the Node app with Dapr:
|
||||
|
||||
<!-- STEP
|
||||
name: Run order-processor service
|
||||
expected_stdout_lines:
|
||||
- "== APP == Configuration for orderId1: { orderId1: { value: '101' } }"
|
||||
- "== APP == Configuration for orderId2: { orderId2: { value: '102' } }"
|
||||
- '== APP == App subscribed to config changes with subscription id:'
|
||||
- '== APP == App unsubscribed from config changes'
|
||||
- "Exited App successfully"
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
dapr run --app-id order-processor --components-path ../../../components/ --app-port 6001 -- node index.js
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## (Optional) Update value of config items
|
||||
|
||||
1. Keep the `order-processor` app running and open a separate terminal
|
||||
2. Change the values of `orderId1` and `orderId2` using the command below
|
||||
3. `order-processor` app gets the updated values of config items
|
||||
|
||||
<!-- STEP
|
||||
name: Update config items
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "103" orderId2 "104"
|
||||
```
|
||||
|
||||
<!--END_STEP -->
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
import axios from "axios";
|
||||
import express, { json } from "express";
|
||||
|
||||
const DAPR_HOST = process.env.DAPR_HOST ?? "localhost";
|
||||
|
||||
let DAPR_PORT = process.env.DAPR_HTTP_PORT ?? 3500;
|
||||
let APP_PORT = process.env.APP_PORT ?? 6001;
|
||||
|
||||
const DAPR_CONFIGURATION_STORE = "configstore";
|
||||
const BASE_URL = `http://${DAPR_HOST}:${DAPR_PORT}/v1.0-alpha1/configuration/${DAPR_CONFIGURATION_STORE}`;
|
||||
const CONFIGURATION_ITEMS = ["orderId1", "orderId2"];
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
async function main() {
|
||||
// Get config items from the config store
|
||||
CONFIGURATION_ITEMS.forEach((item) => {
|
||||
axios
|
||||
.get(`${BASE_URL}?key=${item}`)
|
||||
.then((response) => {
|
||||
console.log("Configuration for " + item + ":", response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("Could not get config item, err:" + error);
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
|
||||
// Start server to receive config updates
|
||||
readConfigurationChanges();
|
||||
// Subscribe to config updates
|
||||
var subscriptionId = await subscribeToConfigUpdates();
|
||||
|
||||
// Unsubscribe to config updates and exit app after 20 seconds
|
||||
setTimeout(async () => {
|
||||
await unsubscribeToConfigUpdates(subscriptionId);
|
||||
process.exit(0);
|
||||
}, 20000);
|
||||
}
|
||||
|
||||
async function subscribeToConfigUpdates() {
|
||||
// Add delay to allow app channel to be ready
|
||||
await sleep(3000);
|
||||
// Subscribe to config updates
|
||||
try {
|
||||
const { data: response } = await axios.get(`${BASE_URL}/subscribe`);
|
||||
console.log("App subscribed to config changes with subscription id: ", response.id);
|
||||
return response.id;
|
||||
} catch (error) {
|
||||
console.log("Could not subscribe to config updates, err:" + error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function unsubscribeToConfigUpdates(subscriptionId) {
|
||||
try {
|
||||
const { data: response } = await axios.get(`${BASE_URL}/${subscriptionId}/unsubscribe`);
|
||||
if (JSON.stringify(response).includes("true")) {
|
||||
console.log("App unsubscribed from config changes");
|
||||
} else {
|
||||
console.log("Error unsubscribing to config updates, err:" + response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error unsubscribing to config updates, err:" + error);
|
||||
}
|
||||
}
|
||||
|
||||
async function readConfigurationChanges() {
|
||||
// Create POST endpoint to receive config updates
|
||||
app.post("/configuration/configstore/*", (req, res) => {
|
||||
console.log("Configuration update", JSON.stringify(req.body.items));
|
||||
res.sendStatus(200);
|
||||
});
|
||||
app.listen(APP_PORT, () => console.log("App listening for config updates"));
|
||||
}
|
||||
|
||||
async function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
main().catch((e) => console.error(e));
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "order-processor",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node ."
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^0.25.0",
|
||||
"express": "^4.18.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.8.0",
|
||||
"eslint-plugin-react": "^7.28.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
##lint files
|
||||
*.cjs
|
||||
|
||||
##node modules
|
||||
node_modules
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
# Dapr Configuration API
|
||||
|
||||
In this quickstart, you'll create a microservice which makes use of Dapr's Configuration API. Configuration items are key/value pairs containing configuration data such as app ids, partition keys, database names etc. The service gets configuration items from the configuration store and subscribes for configuration updates.
|
||||
|
||||
Visit [this](https://docs.dapr.io/developing-applications/building-blocks/configuration/) link for more information about Dapr and Configuration API.
|
||||
|
||||
This quickstart includes one service:
|
||||
|
||||
- Node service `order-processor`
|
||||
|
||||
## Add configuration items to the config store
|
||||
|
||||
### Prerequisite
|
||||
|
||||
- Locally running redis container - a redis container named `dapr_redis` is automatically created when you run `dapr init`
|
||||
- Open a new terminal and set values for config items `orderId1` and `orderId2` by using the command below
|
||||
|
||||
<!-- STEP
|
||||
name: Add configuration items
|
||||
expected_stdout_lines:
|
||||
- 'OK'
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "101" orderId2 "102"
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Run order-processor
|
||||
|
||||
1. Navigate to folder and install dependencies:
|
||||
|
||||
<!-- STEP
|
||||
name: Install Node dependencies
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
npm install
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
1. Run the Node app with Dapr:
|
||||
|
||||
<!-- STEP
|
||||
name: Run order-processor service
|
||||
expected_stdout_lines:
|
||||
- '== APP == Configuration for orderId1: {"key":"orderId1","value":'
|
||||
- '== APP == Configuration for orderId2: {"key":"orderId2","value":'
|
||||
- '== APP == App unsubscribed to config changes'
|
||||
- "Exited App successfully"
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
dapr run --app-id order-processor --components-path ../../../components/ --app-protocol grpc --dapr-grpc-port 3500 -- node index.js
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## (Optional) Update value of config items
|
||||
|
||||
1. Keep the `order-processor` app running and open a separate terminal
|
||||
2. Change the values of `orderId1` and `orderId2` using the command below
|
||||
3. `order-processor` app gets the updated values of config items
|
||||
|
||||
<!-- STEP
|
||||
name: Update config items
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "103" orderId2 "104"
|
||||
```
|
||||
|
||||
<!--END_STEP -->
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
import { CommunicationProtocolEnum, DaprClient } from "@dapr/dapr";
|
||||
|
||||
// JS SDK does not support Configuration API over HTTP protocol yet
|
||||
const protocol = CommunicationProtocolEnum.GRPC;
|
||||
const host = process.env.DAPR_HOST ?? "localhost";
|
||||
const port = process.env.DAPR_GRPC_PORT ?? 3500;
|
||||
|
||||
const DAPR_CONFIGURATION_STORE = "configstore";
|
||||
const CONFIGURATION_ITEMS = ["orderId1", "orderId2"];
|
||||
|
||||
async function main() {
|
||||
const client = new DaprClient(host, port, protocol);
|
||||
// Get config items from the config store
|
||||
try {
|
||||
const config = await client.configuration.get(DAPR_CONFIGURATION_STORE, CONFIGURATION_ITEMS);
|
||||
Object.keys(config.items).forEach((key) => {
|
||||
console.log("Configuration for " + key + ":", JSON.stringify(config.items[key]));
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Could not get config item, err:" + error);
|
||||
process.exit(1);
|
||||
}
|
||||
// Subscribe to config updates
|
||||
try {
|
||||
const stream = await client.configuration.subscribeWithKeys(
|
||||
DAPR_CONFIGURATION_STORE,
|
||||
CONFIGURATION_ITEMS,
|
||||
(config) => {
|
||||
console.log("Configuration update", JSON.stringify(config.items));
|
||||
}
|
||||
);
|
||||
// Unsubscribe to config updates and exit app after 20 seconds
|
||||
setTimeout(() => {
|
||||
stream.stop();
|
||||
console.log("App unsubscribed to config changes");
|
||||
process.exit(0);
|
||||
}, 20000);
|
||||
} catch (error) {
|
||||
console.log("Error subscribing to config updates, err:" + error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((e) => console.error(e));
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "order-processor",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node ."
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@dapr/dapr": "^2.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.8.0",
|
||||
"eslint-plugin-react": "^7.28.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
# Dapr Configuration API
|
||||
|
||||
In this quickstart, you'll create a microservice which makes use of Dapr's Configuration API. Configuration items are key/value pairs containing configuration data such as app ids, partition keys, database names etc. The service gets configuration items from the configuration store and subscribes for configuration updates.
|
||||
|
||||
Visit [this](https://docs.dapr.io/developing-applications/building-blocks/configuration/) link for more information about Dapr and Configuration 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 service:
|
||||
|
||||
- Python service `order-processor`
|
||||
|
||||
## Add configuration items to the config store
|
||||
|
||||
### Prerequisite
|
||||
|
||||
- Locally running redis container - a redis container named `dapr_redis` is automatically created when you run `dapr init`
|
||||
- Open a new terminal and set values for config items `orderId1` and `orderId2` by using the command below
|
||||
|
||||
<!-- STEP
|
||||
name: Add configuration items
|
||||
expected_stdout_lines:
|
||||
- 'OK'
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "101" orderId2 "102"
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
## Run Python service with Dapr
|
||||
|
||||
1. Open a new terminal window and navigate to `order-processor` directory:
|
||||
|
||||
<!-- STEP
|
||||
name: Install python dependencies
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
2. Run the Python service app with Dapr:
|
||||
|
||||
<!-- STEP
|
||||
name: Run order-processor service
|
||||
expected_stdout_lines:
|
||||
- "== APP == INFO:root:Configuration for orderId1: {'orderId1': {'value': '101'}}"
|
||||
- "== APP == INFO:root:Configuration for orderId2: {'orderId2': {'value': '102'}}"
|
||||
- "== APP == INFO:root:App subscribed to config changes with subscription id:"
|
||||
- 'App unsubscribed from config changes'
|
||||
- "Exited App successfully"
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
dapr run --app-id order-processor --components-path ../../../components/ --app-port 6001 -- python3 app.py
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## (Optional) Update value of config items
|
||||
|
||||
1. Keep the `order-processor` app running and open a separate terminal
|
||||
2. Change the values of `orderId1` and `orderId2` using the command below
|
||||
3. `order-processor` app gets the updated values of config items
|
||||
|
||||
<!-- STEP
|
||||
name: Update config items
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "103" orderId2 "104"
|
||||
```
|
||||
|
||||
<!--END_STEP -->
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
import threading
|
||||
import time
|
||||
import logging
|
||||
import requests
|
||||
import os
|
||||
from flask import Flask, request
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
app = Flask(__name__)
|
||||
APP_PORT = os.getenv('APP_PORT', '6001')
|
||||
BASE_URL = os.getenv('BASE_URL', 'http://localhost') + ':' + os.getenv(
|
||||
'DAPR_HTTP_PORT', '3500')
|
||||
DAPR_CONFIGURATION_STORE = 'configstore'
|
||||
CONFIGURATION_ITEMS = ['orderId1', 'orderId2']
|
||||
|
||||
# Get config items from the config store
|
||||
for item in CONFIGURATION_ITEMS:
|
||||
config = requests.get(
|
||||
url='%s/v1.0-alpha1/configuration/%s?key=%s' % (BASE_URL, DAPR_CONFIGURATION_STORE, item)
|
||||
)
|
||||
if config.status_code == 200:
|
||||
logging.info('Configuration for ' +item+ ": " + str(config.json()))
|
||||
else:
|
||||
logging.info('Could not get config item, err: ' + str(config.json()))
|
||||
|
||||
def subscribe_config_updates():
|
||||
# Add delay to allow app channel to be ready
|
||||
time.sleep(3)
|
||||
subscription = requests.get(
|
||||
url = '%s/v1.0-alpha1/configuration/%s/subscribe' % (BASE_URL, DAPR_CONFIGURATION_STORE)
|
||||
)
|
||||
if subscription.status_code == 200 and 'errCode' not in str(subscription.json()) :
|
||||
logging.info('App subscribed to config changes with subscription id: ' + str(subscription.json()['id']))
|
||||
return subscription.json()['id']
|
||||
else:
|
||||
logging.info('Error subscribing to config updates: ' + str(subscription.json()))
|
||||
exit(1)
|
||||
|
||||
# Create POST endpoint to receive config updates
|
||||
@app.route('/configuration/configstore/<configItem>', methods=['POST'])
|
||||
def config_subscriber(configItem):
|
||||
print('Configuration update ' + str(request.json['items']), flush=True)
|
||||
return '' , 200
|
||||
|
||||
# Start the flask app
|
||||
threading.Thread(target=lambda: app.run(port=APP_PORT, debug=False, use_reloader=False), daemon=True).start()
|
||||
# Subscribe to config updates
|
||||
subscription_id = subscribe_config_updates()
|
||||
|
||||
# Unsubscribe to config updates and exit app after 20 seconds
|
||||
time.sleep(20)
|
||||
unsubscribe = requests.get(
|
||||
url = '%s/v1.0-alpha1/configuration/%s/%s/unsubscribe' % (BASE_URL, DAPR_CONFIGURATION_STORE, subscription_id)
|
||||
)
|
||||
if unsubscribe.status_code == 200 and 'True' in str(unsubscribe.json()):
|
||||
logging.info('App unsubscribed from config changes')
|
||||
else:
|
||||
logging.info('Error unsubscribing from config updates: ' + str(unsubscribe.json()))
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
requests
|
||||
flask
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
# Dapr Configuration API
|
||||
|
||||
In this quickstart, you'll create a microservice which makes use of Dapr's Configuration API. Configuration items are key/value pairs containing configuration data such as app ids, partition keys, database names etc. The service gets configuration items from the configuration store and subscribes for configuration updates.
|
||||
|
||||
Visit [this](https://docs.dapr.io/developing-applications/building-blocks/configuration/) link for more information about Dapr and Configuration API.
|
||||
|
||||
This quickstart includes one service:
|
||||
|
||||
- Python service `order-processor`
|
||||
|
||||
## Add configuration items to the config store
|
||||
|
||||
### Prerequisite
|
||||
|
||||
- Locally running redis container - a redis container named `dapr_redis` is automatically created when you run `dapr init`
|
||||
- Open a new terminal and set values for config items `orderId1` and `orderId2` by using the command below
|
||||
|
||||
<!-- STEP
|
||||
name: Add configuration items
|
||||
expected_stdout_lines:
|
||||
- 'OK'
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "101" orderId2 "102"
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## Run Python service with Dapr
|
||||
|
||||
1. Open a new terminal window and navigate to `order-processor` directory:
|
||||
|
||||
<!-- STEP
|
||||
name: Install python dependencies
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
2. Run the Python service app with Dapr:
|
||||
|
||||
<!-- STEP
|
||||
name: Run order-processor service
|
||||
expected_stdout_lines:
|
||||
- '== APP == Configuration for orderId1 : value: "101"'
|
||||
- '== APP == Configuration for orderId2 : value: "102"'
|
||||
- '== APP == App unsubscribed from config changes'
|
||||
- "Exited App successfully"
|
||||
expected_stderr_lines:
|
||||
output_match_mode: substring
|
||||
match_order: none
|
||||
-->
|
||||
|
||||
```bash
|
||||
cd ./order-processor
|
||||
dapr run --app-id order-processor --components-path ../../../components/ --app-port 6001 -- python3 app.py
|
||||
```
|
||||
|
||||
<!-- END_STEP -->
|
||||
|
||||
## (Optional) Update value of config items
|
||||
|
||||
1. Keep the `order-processor` app running and open a separate terminal
|
||||
2. Change the values of `orderId1` and `orderId2` using the command below
|
||||
3. `order-processor` app gets the updated values of config items
|
||||
|
||||
<!-- STEP
|
||||
name: Update config items
|
||||
-->
|
||||
|
||||
```bash
|
||||
docker exec dapr_redis redis-cli MSET orderId1 "103" orderId2 "104"
|
||||
```
|
||||
|
||||
<!--END_STEP -->
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import asyncio
|
||||
import json
|
||||
import threading
|
||||
import time
|
||||
import logging
|
||||
from dapr.clients import DaprClient
|
||||
from dapr.clients.grpc._response import ConfigurationWatcher
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
||||
configuration: ConfigurationWatcher = ConfigurationWatcher()
|
||||
|
||||
DAPR_CONFIGURATION_STORE = 'configstore'
|
||||
CONFIGURATION_ITEMS = ['orderId1', 'orderId2']
|
||||
|
||||
with DaprClient() as client:
|
||||
# Get config items from the config store
|
||||
for config_item in CONFIGURATION_ITEMS:
|
||||
config = client.get_configuration(store_name=DAPR_CONFIGURATION_STORE, keys=[config_item], config_metadata={})
|
||||
print(f"Configuration for {config_item} : {config.items[config_item]}", flush=True)
|
||||
|
||||
async def subscribe_config():
|
||||
with DaprClient() as client:
|
||||
# Subscribe for configuration changes
|
||||
configuration = await client.subscribe_configuration(DAPR_CONFIGURATION_STORE, CONFIGURATION_ITEMS)
|
||||
# Exit app after 20 seconds
|
||||
exitTime = time.time() + 20
|
||||
while time.time() < exitTime:
|
||||
if configuration is not None:
|
||||
items = configuration.get_items()
|
||||
for key in items:
|
||||
print("Configuration update {'"+key+"' : {'value': '"+ items[key].value +"'}}", flush=True)
|
||||
time.sleep(1)
|
||||
# Unsubscribe from configuration updates
|
||||
unsubscribed = True
|
||||
for config_item in CONFIGURATION_ITEMS:
|
||||
unsub_item = client.unsubscribe_configuration(DAPR_CONFIGURATION_STORE, config_item)
|
||||
if unsub_item is False:
|
||||
unsubscribed = False
|
||||
if unsubscribed == True:
|
||||
print("App unsubscribed from config changes", flush=True)
|
||||
else:
|
||||
print("Error unsubscribing from config updates", flush=True)
|
||||
|
||||
asyncio.run(subscribe_config())
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
dapr
|
||||
Loading…
Reference in New Issue