From 5239de8bee4690ea56fb9dccee6b482c78507ab1 Mon Sep 17 00:00:00 2001 From: "Alessandro (Ale) Segala" <43508+ItalyPaleAle@users.noreply.github.com> Date: Fri, 26 May 2023 20:07:19 -0700 Subject: [PATCH 1/7] Clarify that MySQL components work with MariaDB too (#3463) Signed-off-by: ItalyPaleAle <43508+ItalyPaleAle@users.noreply.github.com> --- .../components-reference/supported-bindings/mysql.md | 8 +++++--- .../supported-state-stores/setup-mysql.md | 6 ++++-- daprdocs/data/components/bindings/generic.yaml | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-bindings/mysql.md b/daprdocs/content/en/reference/components-reference/supported-bindings/mysql.md index c36ece9ec..103303ba0 100644 --- a/daprdocs/content/en/reference/components-reference/supported-bindings/mysql.md +++ b/daprdocs/content/en/reference/components-reference/supported-bindings/mysql.md @@ -1,7 +1,7 @@ --- type: docs -title: "MySQL binding spec" -linkTitle: "MySQL" +title: "MySQL & MariaDB binding spec" +linkTitle: "MySQL & MariaDB" description: "Detailed documentation on the MySQL binding component" aliases: - "/operations/components/setup-bindings/supported-bindings/mysql/" @@ -9,7 +9,9 @@ aliases: ## Component format -To setup MySQL binding create a component of type `bindings.mysql`. See [this guide]({{< ref "howto-bindings.md#1-create-a-binding" >}}) on how to create and apply a binding configuration. +The MySQL binding allows connecting to both MySQL and MariaDB databases. In this document, we refer to "MySQL" to indicate both databases. + +To setup a MySQL binding create a component of type `bindings.mysql`. See [this guide]({{< ref "howto-bindings.md#1-create-a-binding" >}}) on how to create and apply a binding configuration. The MySQL binding uses [Go-MySQL-Driver](https://github.com/go-sql-driver/mysql) internally. diff --git a/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-mysql.md b/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-mysql.md index 908157aec..62c3a430f 100644 --- a/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-mysql.md +++ b/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-mysql.md @@ -1,7 +1,7 @@ --- type: docs -title: "MySQL" -linkTitle: "MySQL" +title: "MySQL & MariaDB" +linkTitle: "MySQL & MariaDB" description: Detailed information on the MySQL state store component aliases: - "/operations/components/setup-state-store/supported-state-stores/setup-mysql/" @@ -9,6 +9,8 @@ aliases: ## Component format +The MySQL state store components allows connecting to both MySQL and MariaDB databases. In this document, we refer to "MySQL" to indicate both databases. + To setup MySQL state store create a component of type `state.mysql`. See [this guide]({{< ref "howto-get-save-state.md#step-1-setup-a-state-store" >}}) on how to create and apply a state store configuration. diff --git a/daprdocs/data/components/bindings/generic.yaml b/daprdocs/data/components/bindings/generic.yaml index f6ce33e6e..35ec6d437 100644 --- a/daprdocs/data/components/bindings/generic.yaml +++ b/daprdocs/data/components/bindings/generic.yaml @@ -62,7 +62,7 @@ features: input: true output: true -- component: MySQL +- component: MySQL & MariaDB link: mysql state: Alpha version: v1 From 46f9f4d06dc2574c4b14b6cab1ed0b597aeeee38 Mon Sep 17 00:00:00 2001 From: Josh van Leeuwen Date: Sat, 27 May 2023 04:57:53 +0100 Subject: [PATCH 2/7] Actor State TTL Preview Feature (#3453) * Update documentation to reflect that the Actor State TTL feature is now in preview, behind a feature gate. Signed-off-by: joshvanl * Update actor state curl example Signed-off-by: joshvanl * Clear up language about Actor state TTL behavior Signed-off-by: joshvanl * Remove Actor State TTL section from actors overview page, since the feature is in preview Signed-off-by: joshvanl * Update daprdocs/content/en/reference/api/actors_api.md Signed-off-by: Mark Fussell * Update daprdocs/content/en/reference/api/actors_api.md Signed-off-by: Mark Fussell * Update actors_api.md Changing layout Signed-off-by: Mark Fussell --------- Signed-off-by: joshvanl Signed-off-by: Mark Fussell Co-authored-by: Mark Fussell --- .../building-blocks/actors/actors-overview.md | 9 --------- .../support/support-preview-features.md | 1 + daprdocs/content/en/reference/api/actors_api.md | 15 +++++++++++---- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/actors/actors-overview.md b/daprdocs/content/en/developing-applications/building-blocks/actors/actors-overview.md index 36676f36e..ada5c602a 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/actors/actors-overview.md +++ b/daprdocs/content/en/developing-applications/building-blocks/actors/actors-overview.md @@ -90,15 +90,6 @@ The Dapr actor runtime provides a simple turn-based access model for accessing a Transactional state stores can be used to store actor state. To specify which state store to use for actors, specify value of property `actorStateStore` as `true` in the state store component's metadata section. Actors state is stored with a specific scheme in transactional state stores, allowing for consistent querying. Only a single state store component can be used as the state store for all actors. Read the [state API reference]({{< ref state_api.md >}}) and the [actors API reference]({{< ref actors_api.md >}}) to learn more about state stores for actors. -#### Time to Live (TTL) on state -You should always set the TTL metadata field (`ttlInSeconds`), or the equivalent API call in your chosen SDK when saving actor state to ensure that state eventually removed. Read [actors overview]({{< ref actors-overview.md >}}) for more information. - -#### Demo - -Watch this video for a demo on [Actor State TTL](https://youtu.be/kVpQYkGemRc?t=20) from the Dapr Community Call #80: - - - ### Actor timers and reminders Actors can schedule periodic work on themselves by registering either timers or reminders. diff --git a/daprdocs/content/en/operations/support/support-preview-features.md b/daprdocs/content/en/operations/support/support-preview-features.md index 92522d496..085b8644a 100644 --- a/daprdocs/content/en/operations/support/support-preview-features.md +++ b/daprdocs/content/en/operations/support/support-preview-features.md @@ -21,6 +21,7 @@ For CLI there is no explicit opt-in, just the version that this was first made a | **Multi-App Run** | Configure multiple Dapr applications from a single configuration file and run from a single command | `dapr run -f` | [Multi-App Run]({{< ref multi-app-dapr-run.md >}}) | v1.10 | | **Workflows** | Author workflows as code to automate and orchestrate tasks within your application, like messaging, state management, and failure handling | N/A | [Workflows concept]({{< ref "components-concept#workflows" >}})| v1.10 | | **Service invocation for non-Dapr endpoints** | Allow the invocation of non-Dapr endpoints by Dapr using the [Service invocation API]({{< ref service_invocation_api.md >}}). Read ["How-To: Invoke Non-Dapr Endpoints using HTTP"]({{< ref howto-invoke-non-dapr-endpoints.md >}}) for more information. | N/A | [Service invocation API]({{< ref service_invocation_api.md >}}) | v1.11 | +| **Actor State TTL** | Allow actors to save records to state stores with Time To Live (TTL) set to automatically clean up old data. In its current implementation, actor state with TTL may not be reflected correctly by clients, read [Actor State Transactions]({{< ref actors_api.md >}}) for more information. | `ActorStateTTL` | [Actor State Transactions]({{< ref actors_api.md >}}) | v1.11 | ### Streaming for HTTP service invocation diff --git a/daprdocs/content/en/reference/api/actors_api.md b/daprdocs/content/en/reference/api/actors_api.md index 180bea672..5e4bbd579 100644 --- a/daprdocs/content/en/reference/api/actors_api.md +++ b/daprdocs/content/en/reference/api/actors_api.md @@ -75,10 +75,15 @@ Persists the change to the state for an actor as a multi-item transaction. ***Note that this operation is dependant on a using state store component that supports multi-item transactions.*** -When putting state, _always_ set the `ttlInSeconds` field in the -metadata for each value, unless there is a state clean up process out of band of -Dapr. Omitting this field will result in the underlying Actor state store to -grow indefinitely. +#### TTL + +With the [`ActorStateTTL` feature enabled]]({{< ref +"support-preview-features.md" >}}), actor clients can set the `ttlInSeconds` +field in the transaction metadata to have the state expire after that many +seconds. If the `ttlInSeconds` field is not set, the state will not expire. + +Keep in mind when building actor applications with this feature enabled; +Currently, all actor SDKs will preserve the actor state in their local cache even after the state has expired. This means that the actor state will not be removed from the local cache if the TTL has expired until the actor is restarted or deactivated. This behaviour will be changed in a future release. See the Dapr Community Call 80 recording for more details on actor state TTL. @@ -109,6 +114,8 @@ Parameter | Description #### Examples +> Note, the following example uses the `ttlInSeconds` field, which requires the [`ActorStateTTL` feature enabled]]({{< ref "support-preview-features.md" >}}). + ```shell curl -X POST http://localhost:3500/v1.0/actors/stormtrooper/50/state \ -H "Content-Type: application/json" \ From 5696e3ccdf62594ef1db081e78444b560e4a300f Mon Sep 17 00:00:00 2001 From: Shivam Kumar Date: Mon, 29 May 2023 21:55:19 +0530 Subject: [PATCH 3/7] Update configuration api (#3410) * updating alpha1 endpoint to stable Signed-off-by: Shivam Kumar * lowercaing table name in postgres Signed-off-by: Shivam Kumar * updating redis metadata Signed-off-by: Shivam Kumar * updating redis and postgres to stable Signed-off-by: Shivam Kumar * updating configuration manage: wip Signed-off-by: Shivam Kumar * Apply suggestions from code review Co-authored-by: Mark Fussell Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> Signed-off-by: Shivam Kumar * adding demo link Signed-off-by: Shivam Kumar * updating sdk codes Signed-off-by: Shivam Kumar * adding java sdk subscribe Signed-off-by: Shivam Kumar * adding javascript sdk subscribe Signed-off-by: Shivam Kumar * adding sdk unsubscribe Signed-off-by: Shivam Kumar * adding quotes Signed-off-by: Shivam Kumar * Update howto-manage-configuration.md Updating a small grammar change --------- Signed-off-by: Shivam Kumar Co-authored-by: Mark Fussell Co-authored-by: Hannah Hunter <94493363+hhunter-ms@users.noreply.github.com> --- .../en/concepts/building-blocks-concept.md | 2 +- .../configuration-api-overview.md | 5 + .../howto-manage-configuration.md | 621 ++++++++++++++---- .../en/reference/api/configuration_api.md | 20 +- .../azure-appconfig-configuration-store.md | 2 +- .../postgres-configuration-store.md | 4 +- .../redis-configuration-store.md | 42 +- 7 files changed, 527 insertions(+), 169 deletions(-) diff --git a/daprdocs/content/en/concepts/building-blocks-concept.md b/daprdocs/content/en/concepts/building-blocks-concept.md index 0bdeaed19..1346b7bfc 100644 --- a/daprdocs/content/en/concepts/building-blocks-concept.md +++ b/daprdocs/content/en/concepts/building-blocks-concept.md @@ -27,6 +27,6 @@ The following are the building blocks provided by Dapr: | [**Actors**]({{< ref "actors-overview.md" >}}) | `/v1.0/actors` | An actor is an isolated, independent unit of compute and state with single-threaded execution. Dapr provides an actor implementation based on the virtual actor pattern which provides a single-threaded programming model and where actors are garbage collected when not in use. | [**Observability**]({{< ref "observability-concept.md" >}}) | `N/A` | Dapr system components and runtime emit metrics, logs, and traces to debug, operate and monitor Dapr system services, components and user applications. | [**Secrets**]({{< ref "secrets-overview.md" >}}) | `/v1.0/secrets` | Dapr provides a secrets building block API and integrates with secret stores such as public cloud stores, local stores and Kubernetes to store the secrets. Services can call the secrets API to retrieve secrets, for example to get a connection string to a database. -| [**Configuration**]({{< ref "configuration-api-overview.md" >}}) | `/v1.0-alpha1/configuration` | The Configuration API enables you to retrieve and subscribe to application configuration items for supported configuration stores. This enables an application to retrieve specific configuration information, for example, at start up or when configuration changes are made in the store. +| [**Configuration**]({{< ref "configuration-api-overview.md" >}}) | `/v1.0/configuration` | The Configuration API enables you to retrieve and subscribe to application configuration items for supported configuration stores. This enables an application to retrieve specific configuration information, for example, at start up or when configuration changes are made in the store. | [**Distributed lock**]({{< ref "distributed-lock-api-overview.md" >}}) | `/v1.0-alpha1/lock` | The distributed lock API enables you to take a lock on a resource so that multiple instances of an application can access the resource without conflicts and provide consistency guarantees. | [**Workflows**]({{< ref "workflow-overview.md" >}}) | `/v1.0-alpha1/workflow` | The Workflow API enables you to define long running, persistent processes or data flows that span multiple microservices using Dapr workflows or workflow components. The Workflow API can be combined with other Dapr API building blocks. For example, a workflow can call another service with service invocation or retrieve secrets, providing flexibility and portability. \ No newline at end of file diff --git a/daprdocs/content/en/developing-applications/building-blocks/configuration/configuration-api-overview.md b/daprdocs/content/en/developing-applications/building-blocks/configuration/configuration-api-overview.md index 2e95f6b49..3bda0d141 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/configuration/configuration-api-overview.md +++ b/daprdocs/content/en/developing-applications/building-blocks/configuration/configuration-api-overview.md @@ -40,6 +40,11 @@ Want to put the Dapr configuration API to the test? Walk through the following q Want to skip the quickstarts? Not a problem. You can try out the configuration building block directly in your application to read and manage configuration data. After [Dapr is installed]({{< ref "getting-started/_index.md" >}}), you can begin using the configuration API starting with [the configuration how-to guide]({{< ref howto-manage-configuration.md >}}). +## Watch the demo + +Watch [this demo of using the Dapr Configuration building block](https://youtu.be/tNq-n1XQuLA?t=496) + + ## Next steps Follow these guides on: diff --git a/daprdocs/content/en/developing-applications/building-blocks/configuration/howto-manage-configuration.md b/daprdocs/content/en/developing-applications/building-blocks/configuration/howto-manage-configuration.md index b05722312..d09e5337c 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/configuration/howto-manage-configuration.md +++ b/daprdocs/content/en/developing-applications/building-blocks/configuration/howto-manage-configuration.md @@ -67,9 +67,11 @@ spec: ``` ## Retrieve Configuration Items -### Get configuration items using Dapr SDKs +### Get configuration items -{{< tabs ".NET" Java Python>}} +The following example shows how to get a saved configuration item using the Dapr Configuration API. + +{{< tabs ".NET" Java Python Go Javascript "HTTP API (BASH)" "HTTP API (Powershell)">}} {{% codetab %}} @@ -87,7 +89,6 @@ namespace ConfigurationApi { private static readonly string CONFIG_STORE_NAME = "configstore"; - [Obsolete] public static async Task Main(string[] args) { using var client = new DaprClientBuilder().Build(); @@ -105,7 +106,7 @@ namespace ConfigurationApi ```java //dependencies import io.dapr.client.DaprClientBuilder; -import io.dapr.client.DaprPreviewClient; +import io.dapr.client.DaprClient; import io.dapr.client.domain.ConfigurationItem; import io.dapr.client.domain.GetConfigurationRequest; import io.dapr.client.domain.SubscribeConfigurationRequest; @@ -116,7 +117,7 @@ import reactor.core.publisher.Mono; private static final String CONFIG_STORE_NAME = "configstore"; public static void main(String[] args) throws Exception { - try (DaprPreviewClient client = (new DaprClientBuilder()).buildPreviewClient()) { + try (DaprClient client = (new DaprClientBuilder()).build()) { List keys = new ArrayList<>(); keys.add("orderId1"); keys.add("orderId2"); @@ -150,79 +151,31 @@ with DaprClient() as d: {{% /codetab %}} -{{< /tabs >}} - -### Get configuration items using gRPC API - -Using your [favorite language](https://grpc.io/docs/languages/), create a Dapr gRPC client from the [Dapr proto](https://github.com/dapr/dapr/blob/master/dapr/proto/runtime/v1/dapr.proto). The following examples show Java, C#, Python and Javascript clients. - -{{< tabs Java Dotnet Python Javascript >}} - {{% codetab %}} -```java +```go +package main -Dapr.ServiceBlockingStub stub = Dapr.newBlockingStub(channel); -stub.GetConfigurationAlpha1(new GetConfigurationRequest{ StoreName = "redisconfigstore", Keys = new String[]{"myconfig"} }); -``` +import ( + "context" + "fmt" -{{% /codetab %}} + dapr "github.com/dapr/go-sdk/client" +) -{{% codetab %}} - -```csharp - -var call = client.GetConfigurationAlpha1(new GetConfigurationRequest { StoreName = "redisconfigstore", Keys = new String[]{"myconfig"} }); -``` - -{{% /codetab %}} - -{{% codetab %}} - -```python -response = stub.GetConfigurationAlpha1(request={ StoreName: 'redisconfigstore', Keys = ['myconfig'] }) -``` - -{{% /codetab %}} - -{{% codetab %}} - -```javascript -client.GetConfigurationAlpha1({ StoreName: 'redisconfigstore', Keys = ['myconfig'] }) -``` - -{{% /codetab %}} - -{{< /tabs >}} - -### Watch configuration items using Dapr SDKs - -{{< tabs "Dotnet Extension" "Dotnet Client">}} -{{% codetab %}} - -```csharp -[Obsolete("Configuration API is an Alpha API. Obsolete will be removed when the API is no longer Alpha")] -public static void Main(string[] args) -{ - CreateHostBuilder(args).Build().Run(); -} - -public static IHostBuilder CreateHostBuilder(string[] args) -{ - var client = new DaprClientBuilder().Build(); - return Host.CreateDefaultBuilder(args) - .ConfigureAppConfiguration(config => - { - // Get the initial value from the configuration component. - config.AddDaprConfigurationStore("redisconfig", new List() { "withdrawVersion" }, client, TimeSpan.FromSeconds(20)); - - // Watch the keys in the configuration component and update it in local configurations. - config.AddStreamingDaprConfigurationStore("redisconfig", new List() { "withdrawVersion", "source" }, client, TimeSpan.FromSeconds(20)); - }) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); +func main() { + ctx := context.Background() + client, err := dapr.NewClient() + if err != nil { + panic(err) + } + items, err := client.GetConfigurationItems(ctx, "configstore", ["orderId1","orderId2"]) + if err != nil { + panic(err) + } + for key, item := range items { + fmt.Printf("get config: key = %s value = %s version = %s",key,(*item).Value, (*item).Version) + } } ``` @@ -230,106 +183,502 @@ public static IHostBuilder CreateHostBuilder(string[] args) {{% codetab %}} +```js +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); + } +} + +main().catch((e) => console.error(e)); +``` + +{{% /codetab %}} + +{{% codetab %}} + +Launch a dapr sidecar: + +```bash +dapr run --app-id orderprocessing --dapr-http-port 3601 +``` + +In a separate terminal, get the configuration item saved earlier: + +```bash +curl http://localhost:3601/v1.0/configuration/configstore?key=orderId1 +``` + +{{% /codetab %}} + +{{% codetab %}} + +Launch a Dapr sidecar: + +```bash +dapr run --app-id orderprocessing --dapr-http-port 3601 +``` + +In a separate terminal, get the configuration item saved earlier: + +```powershell +Invoke-RestMethod -Uri 'http://localhost:3601/v1.0/configuration/configstore?key=orderId1' +``` + +{{% /codetab %}} + +{{< /tabs >}} + + +### Subscribe to configuration item updates + +Below are code examples that leverage SDKs to subscribe to keys `[orderId1, orderId2]` using `configstore` store component. + +{{< tabs ".NET" "ASP.NET Core" Java Python Go Javascript>}} + +{{% codetab %}} + ```csharp -public IDictionary Data { get; set; } = new Dictionary(); -public string Id { get; set; } = string.Empty; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Dapr.Client; -public async Task WatchConfiguration(DaprClient daprClient, string store, IReadOnlyList keys, Dictionary metadata, CancellationToken token = default) +const string DAPR_CONFIGURATION_STORE = "configstore"; +var CONFIGURATION_KEYS = new List { "orderId1", "orderId2" }; +var client = new DaprClientBuilder().Build(); + +// Subscribe for configuration changes +SubscribeConfigurationResponse subscribe = await client.SubscribeConfiguration(DAPR_CONFIGURATION_STORE, CONFIGURATION_ITEMS); + +// Print configuration changes +await foreach (var items in subscribe.Source) { - // Initialize the gRPC Stream that will provide configuration updates. - var subscribeConfigurationResponse = await daprClient.SubscribeConfiguration(store, keys, metadata, token); + // First invocation when app subscribes to config changes only returns subscription id + if (items.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(items); + Console.WriteLine("Configuration update " + cfg); +} +``` - // The response contains a data source which is an IAsyncEnumerable, so it can be iterated through via an awaited foreach. - await foreach (var items in subscribeConfigurationResponse.Source.WithCancellation(token)) +Navigate to the directory containing the above code, then run the following command to launch both a Dapr sidecar and the subscriber application: + +```bash +dapr run --app-id orderprocessing -- dotnet run +``` + +{{% /codetab %}} + +{{% codetab %}} + +```csharp +using System; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Dapr.Client; +using Dapr.Extensions.Configuration; +using System.Collections.Generic; +using System.Threading; + +namespace ConfigurationApi +{ + public class Program { - // Each iteration from the stream can contain all the keys that were queried for, so it must be individually iterated through. - var data = new Dictionary(Data); - foreach (var item in items) + public static void Main(string[] args) { - // The Id in the response is used to unsubscribe. - Id = subscribeConfigurationResponse.Id; - data[item.Key] = item.Value; + Console.WriteLine("Starting application."); + CreateHostBuilder(args).Build().Run(); + Console.WriteLine("Closing application."); + } + + /// + /// Creates WebHost Builder. + /// + /// Arguments. + /// Returns IHostbuilder. + public static IHostBuilder CreateHostBuilder(string[] args) + { + var client = new DaprClientBuilder().Build(); + return Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration(config => + { + // Get the initial value and continue to watch it for changes. + config.AddDaprConfigurationStore("configstore", new List() { "orderId1","orderId2" }, client, TimeSpan.FromSeconds(20)); + config.AddStreamingDaprConfigurationStore("configstore", new List() { "orderId1","orderId2" }, client, TimeSpan.FromSeconds(20)); + + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); } - Data = data; } } ``` +Navigate to the directory containing the above code, then run the following command to launch both a Dapr sidecar and the subscriber application: + +```bash +dapr run --app-id orderprocessing -- dotnet run +``` + {{% /codetab %}} -{{< /tabs >}} -### Watch configuration items using gRPC API +{{% codetab %}} -Create a Dapr gRPC client from the [Dapr proto](https://github.com/dapr/dapr/blob/master/dapr/proto/runtime/v1/dapr.proto) using your [preferred language](https://grpc.io/docs/languages/). Use the `SubscribeConfigurationAlpha1` proto method on your client stub to start subscribing to events. The method accepts the following request object: +```java +import io.dapr.client.DaprClientBuilder; +import io.dapr.client.DaprClient; +import io.dapr.client.domain.ConfigurationItem; +import io.dapr.client.domain.GetConfigurationRequest; +import io.dapr.client.domain.SubscribeConfigurationRequest; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; -```proto -message SubscribeConfigurationRequest { - // The name of configuration store. - string store_name = 1; +//code +private static final String CONFIG_STORE_NAME = "configstore"; +private static String subscriptionId = null; - // Optional. The key of the configuration item to fetch. - // If set, only query for the specified configuration items. - // Empty list means fetch all. - repeated string keys = 2; +public static void main(String[] args) throws Exception { + try (DaprClient client = (new DaprClientBuilder()).build()) { + // Subscribe for config changes + List keys = new ArrayList<>(); + keys.add("orderId1"); + keys.add("orderId2"); + Flux subscription = client.subscribeConfiguration(DAPR_CONFIGURATON_STORE,keys); - // The metadata which will be sent to configuration store components. - map metadata = 3; + // 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); + } } ``` -Using this method, you can subscribe to changes in specific keys for a given configuration store. gRPC streaming varies widely based on language - see the [gRPC examples here](https://grpc.io/docs/languages/) for usage. +Navigate to the directory containing the above code, then run the following command to launch both a Dapr sidecar and the subscriber application: -Below are the examples in sdks: +```bash +dapr run --app-id orderprocessing -- -- mvn spring-boot:run -{{< tabs Python>}} +{{% /codetab %}} + +{{% codetab %}} + +```python +#dependencies +from dapr.clients import DaprClient +#code + +def handler(id: str, resp: ConfigurationResponse): + for key in resp.items: + print(f"Subscribed item received key={key} value={resp.items[key].value} " + f"version={resp.items[key].version} " + f"metadata={resp.items[key].metadata}", flush=True) + +def executeConfiguration(): + with DaprClient() as d: + storeName = 'configurationstore' + keys = ['orderId1', 'orderId2'] + id = d.subscribe_configuration(store_name=storeName, keys=keys, + handler=handler, config_metadata={}) + print("Subscription ID is", id, flush=True) + sleep(20) + +executeConfiguration() +``` + +Navigate to the directory containing the above code, then run the following command to launch both a Dapr sidecar and the subscriber application: + +```bash +dapr run --app-id orderprocessing -- python3 OrderProcessingService.py +``` + +{{% /codetab %}} + +{{% codetab %}} + +```go +package main + +import ( + "context" + "fmt" + "time" + + dapr "github.com/dapr/go-sdk/client" +) + +func main() { + ctx := context.Background() + client, err := dapr.NewClient() + if err != nil { + panic(err) + } + subscribeID, err := client.SubscribeConfigurationItems(ctx, "configstore", []string{"orderId1", "orderId2"}, func(id string, items map[string]*dapr.ConfigurationItem) { + for k, v := range items { + fmt.Printf("get updated config key = %s, value = %s version = %s \n", k, v.Value, v.Version) + } + }) + if err != nil { + panic(err) + } + time.Sleep(20*time.Second) +} +``` + +Navigate to the directory containing the above code, then run the following command to launch both a Dapr sidecar and the subscriber application: + +```bash +dapr run --app-id orderprocessing -- go run main.go +``` + +{{% /codetab %}} + +{{% codetab %}} + +```js +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); + // 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)); +``` + +Navigate to the directory containing the above code, then run the following command to launch both a Dapr sidecar and the subscriber application: + +```bash +dapr run --app-id orderprocessing --app-protocol grpc --dapr-grpc-port 3500 -- node index.js +``` + +{{% /codetab %}} + +{{< /tabs >}} + + +### Unsubscribe from configuration item updates + +After you've subscribed to watch configuration items, you will receive updates for all of the subscribed keys. To stop receiving updates, you need to explicitly call the unsubscribe API. + +Following are the code examples showing how you can unsubscribe to configuration updates using unsubscribe API. + +{{< tabs ".NET" Java Python Go Javascript "HTTP API (BASH)" "HTTP API (Powershell)">}} + +{{% codetab %}} +```csharp +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Dapr.Client; + +const string DAPR_CONFIGURATION_STORE = "configstore"; +var client = new DaprClientBuilder().Build(); + +// Unsubscribe to config updates and exit the app +async Task unsubscribe(string subscriptionId) +{ + try + { + await 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); + } +} +``` +{{% /codetab %}} + +{{% codetab %}} +```java +import io.dapr.client.DaprClientBuilder; +import io.dapr.client.DaprClient; +import io.dapr.client.domain.ConfigurationItem; +import io.dapr.client.domain.GetConfigurationRequest; +import io.dapr.client.domain.SubscribeConfigurationRequest; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +//code +private static final String CONFIG_STORE_NAME = "configstore"; +private static String subscriptionId = null; + +public static void main(String[] args) throws Exception { + try (DaprClient client = (new DaprClientBuilder()).build()) { + // 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 unsubscribing to config updates," + e.getMessage()); + System.exit(1); + } +} +``` +{{% /codetab %}} {{% codetab %}} ```python -#dependencies import asyncio +import time +import logging from dapr.clients import DaprClient -#code -async def executeConfiguration(): - with DaprClient() as d: - CONFIG_STORE_NAME = 'configstore' - key = 'orderId' - # Subscribe to configuration by key. - configuration = await d.subscribe_configuration(store_name=CONFIG_STORE_NAME, keys=[key], config_metadata={}) - if configuration != None: - items = configuration.get_items() - for item in items: - print(f"Subscribe key={item.key} value={item.value} version={item.version}", flush=True) - else: - print("Nothing yet") -asyncio.run(executeConfiguration()) -``` +subscriptionID = "" -```bash -dapr run --app-id orderprocessing --resources-path components/ -- python3 OrderProcessingService.py +with DaprClient() as d: + isSuccess = d.unsubscribe_configuration(store_name='configstore', id=subscriptionID) + print(f"Unsubscribed successfully? {isSuccess}", flush=True) ``` - {{% /codetab %}} -{{< /tabs >}} +{{% codetab %}} +```go +package main -#### Stop watching configuration items +import ( + "context" + "encoding/json" + "fmt" + "log" + "os" + "time" -After you've subscribed to watch configuration items, the gRPC-server stream starts. Since this stream thread does not close itself, you have to explicitly call the `UnSubscribeConfigurationRequest` API to unsubscribe. This method accepts the following request object: + dapr "github.com/dapr/go-sdk/client" +) -```proto -// UnSubscribeConfigurationRequest is the message to stop watching the key-value configuration. -message UnSubscribeConfigurationRequest { - // The name of configuration store. - string store_name = 1; - // Optional. The keys of the configuration item to stop watching. - // Store_name and keys should match previous SubscribeConfigurationRequest's keys and store_name. - // Once invoked, the subscription that is watching update for the key-value event is stopped - repeated string keys = 2; +var DAPR_CONFIGURATION_STORE = "configstore" +var subscriptionID = "" + +func main() { + client, err := dapr.NewClient() + if err != nil { + log.Panic(err) + } + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + if err := client.UnsubscribeConfigurationItems(ctx, DAPR_CONFIGURATION_STORE , subscriptionID); err != nil { + panic(err) + } } ``` +{{% /codetab %}} -Using this unsubscribe method, you can stop watching configuration update events. Dapr locates the subscription stream based on the `store_name` and any optional keys supplied and closes it. +{{% codetab %}} +```js +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); + + try { + const stream = await client.configuration.subscribeWithKeys( + DAPR_CONFIGURATION_STORE, + CONFIGURATION_ITEMS, + (config) => { + console.log("Configuration update", JSON.stringify(config.items)); + } + ); + setTimeout(() => { + // Unsubscribe to config updates + 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)); +``` +{{% /codetab %}} + +{{% codetab %}} +```bash +curl 'http://localhost:/v1.0/configuration/configstore//unsubscribe' +``` +{{% /codetab %}} + +{{% codetab %}} +```powershell +Invoke-RestMethod -Uri 'http://localhost:/v1.0/configuration/configstore//unsubscribe' +``` +{{% /codetab %}} ## Next steps -* Read [configuration API overview]({{< ref configuration-api-overview.md >}}) \ No newline at end of file +* Read [configuration API overview]({{< ref configuration-api-overview.md >}}) diff --git a/daprdocs/content/en/reference/api/configuration_api.md b/daprdocs/content/en/reference/api/configuration_api.md index 6f611f3c8..a1d878472 100644 --- a/daprdocs/content/en/reference/api/configuration_api.md +++ b/daprdocs/content/en/reference/api/configuration_api.md @@ -13,7 +13,7 @@ This endpoint lets you get configuration from a store. ### HTTP Request ``` -GET http://localhost:/v1.0-alpha1/configuration/ +GET http://localhost:/v1.0/configuration/ ``` #### URL Parameters @@ -29,13 +29,13 @@ If no query parameters are provided, all configuration items are returned. To specify the keys of the configuration items to get, use one or more `key` query parameters. For example: ``` -GET http://localhost:/v1.0-alpha1/configuration/mystore?key=config1&key=config2 +GET http://localhost:/v1.0/configuration/mystore?key=config1&key=config2 ``` To retrieve all configuration items: ``` -GET http://localhost:/v1.0-alpha1/configuration/mystore +GET http://localhost:/v1.0/configuration/mystore ``` #### Request Body @@ -59,7 +59,7 @@ JSON-encoded value of key/value pairs for each configuration item. ### Example ```shell -curl -X GET 'http://localhost:3500/v1.0-alpha1/configuration/mystore?key=myConfigKey' +curl -X GET 'http://localhost:3500/v1.0/configuration/mystore?key=myConfigKey' ``` > The above command returns the following JSON: @@ -75,7 +75,7 @@ This endpoint lets you subscribe to configuration changes. Notifications happen ### HTTP Request ``` -GET http://localhost:/v1.0-alpha1/configuration//subscribe +GET http://localhost:/v1.0/configuration//subscribe ``` #### URL Parameters @@ -91,13 +91,13 @@ If no query parameters are provided, all configuration items are subscribed to. To specify the keys of the configuration items to subscribe to, use one or more `key` query parameters. For example: ``` -GET http://localhost:/v1.0-alpha1/configuration/mystore/subscribe?key=config1&key=config2 +GET http://localhost:/v1.0/configuration/mystore/subscribe?key=config1&key=config2 ``` To subscribe to all changes: ``` -GET http://localhost:/v1.0-alpha1/configuration/mystore/subscribe +GET http://localhost:/v1.0/configuration/mystore/subscribe ``` #### Request Body @@ -121,7 +121,7 @@ JSON-encoded value ### Example ```shell -curl -X GET 'http://localhost:3500/v1.0-alpha1/configuration/mystore/subscribe?key=myConfigKey' +curl -X GET 'http://localhost:3500/v1.0/configuration/mystore/subscribe?key=myConfigKey' ``` > The above command returns the following JSON: @@ -141,7 +141,7 @@ This endpoint lets you unsubscribe to configuration changes. ### HTTP Request ``` -GET http://localhost:/v1.0-alpha1/configuration///unsubscribe +GET http://localhost:/v1.0/configuration///unsubscribe ``` #### URL Parameters @@ -181,7 +181,7 @@ Code | Description ### Example ```shell -curl -X GET 'http://localhost:3500/v1.0-alpha1/configuration/mystore/bf3aa454-312d-403c-af95-6dec65058fa2/unsubscribe' +curl -X GET 'http://localhost:3500/v1.0/configuration/mystore/bf3aa454-312d-403c-af95-6dec65058fa2/unsubscribe' ``` ## Optional application (user code) routes diff --git a/daprdocs/content/en/reference/components-reference/supported-configuration-stores/azure-appconfig-configuration-store.md b/daprdocs/content/en/reference/components-reference/supported-configuration-stores/azure-appconfig-configuration-store.md index 490b75d61..4d2eeaea5 100644 --- a/daprdocs/content/en/reference/components-reference/supported-configuration-stores/azure-appconfig-configuration-store.md +++ b/daprdocs/content/en/reference/components-reference/supported-configuration-stores/azure-appconfig-configuration-store.md @@ -100,7 +100,7 @@ The Azure App Configuration store component supports the following optional `lab The label can be populated using query parameters in the request URL: ```bash -GET curl http://localhost:/v1.0-alpha1/configuration/?key=&metadata.label=