mirror of https://github.com/dapr/quickstarts.git
				
				
				
			Merge pull request #743 from sarsharma/configuration_quickstart
Add quickstart for Configuration API
This commit is contained in:
		
						commit
						b0b9a5aa36
					
				|  | @ -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| | | [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 | | | [Secrets Management](./secrets_management/) | Securely fetch secrets | | ||||||
| | Actors | Coming soon... | | | Actors | Coming soon... | | ||||||
| | Observability | Coming soon... | | | [Configuration](./configuration) | Get configuration items as key/value pairs or subscribe to changes whenever a configuration item changes | | ||||||
| | Configuration | Coming soon... | |  | ||||||
| 
 | 
 | ||||||
| ### Tutorials | ### 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). | 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