diff --git a/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md b/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md index 6e99d4a04..4f0cb2127 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md +++ b/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md @@ -10,9 +10,9 @@ This article describe how to deploy services each with an unique application ID, ## Example: -The below code examples loosely describe an application that processes orders. In the examples, there are two services - an order processing service and a checkout service. Both services have Dapr sidecars and the order processing service uses Dapr to invoke the checkout method in the checkout service. +The below code examples loosely describes an application that processes orders. In the examples, there are two services - an order processing service and a checkout service. Both services have Dapr sidecars and the order processing service uses Dapr to invoke the checkout method in the checkout service. -Diagram showing service invocation of example service +Diagram showing service invocation of example service ## Step 1: Choose an ID for your service @@ -184,18 +184,26 @@ Below are code examples that leverage Dapr SDKs for service invocation. {{% codetab %}} ```csharp -//headers - +//dependencies using Dapr.Client; -using System.Net.Http; //code - -CancellationTokenSource source = new CancellationTokenSource(); -CancellationToken cancellationToken = source.Token; -using var client = new DaprClientBuilder().Build(); -var result = client.CreateInvokeMethodRequest(HttpMethod.Get, "checkout", "checkout/" + orderId, cancellationToken); -await client.InvokeMethodAsync(result); +namespace EventService +{ + class Program + { + static async Task Main(string[] args) + { + int orderId = 100; + CancellationTokenSource source = new CancellationTokenSource(); + CancellationToken cancellationToken = source.Token; + //Using Dapr SDK to invoke a method + using var client = new DaprClientBuilder().Build(); + var result = client.CreateInvokeMethodRequest(HttpMethod.Get, "checkout", "checkout/" + orderId, cancellationToken); + await client.InvokeMethodAsync(result); + } + } +} ``` {{% /codetab %}} @@ -204,22 +212,27 @@ await client.InvokeMethodAsync(result); {{% codetab %}} ```java -//headers - +//dependencies import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; import io.dapr.client.domain.HttpExtension; //code - -DaprClient daprClient = new DaprClientBuilder().build(); -var result = daprClient.invokeMethod( - "checkout", - "checkout/" + orderId, - null, - HttpExtension.GET, - String.class -); +@SpringBootApplication +public class OrderProcessingServiceApplication { + public static void main(String[] args) throws InterruptedException { + int orderId = 100; + //Using Dapr SDK to invoke a method + DaprClient client = new DaprClientBuilder().build(); + var result = client.invokeMethod( + "checkout", + "checkout/" + orderId, + null, + HttpExtension.GET, + String.class + ); + } +} ``` {{% /codetab %}} @@ -227,19 +240,19 @@ var result = daprClient.invokeMethod( {{% codetab %}} ```python -//headers - +#dependencies from dapr.clients import DaprClient -//code - -with DaprClient() as daprClient: - result = daprClient.invoke_method( - "checkout", - f"checkout/{orderId}", - data=b'', - http_verb="GET" - ) +#code +orderId = 100 +#Using Dapr SDK to invoke a method +with DaprClient() as client: + result = client.invoke_method( + "checkout", + f"checkout/{orderId}", + data=b'', + http_verb="GET" + ) ``` {{% /codetab %}} @@ -247,21 +260,25 @@ with DaprClient() as daprClient: {{% codetab %}} ```go -//headers +//dependencies import ( - dapr "github.com/dapr/go-sdk/client" + "strconv" + dapr "github.com/dapr/go-sdk/client" + ) //code - -client, err := dapr.NewClient() -if err != nil { - panic(err) +func main() { + orderId := 100 + //Using Dapr SDK to invoke a method + client, err := dapr.NewClient() + if err != nil { + panic(err) + } + defer client.Close() + ctx := context.Background() + result, err := client.InvokeMethod(ctx, "checkout", "checkout/" + strconv.Itoa(orderId), "get") } -defer client.Close() -ctx := context.Background() - -result, err := client.InvokeMethod(ctx, "checkout", "checkout/" + strconv.Itoa(orderId), "get") ``` {{% /codetab %}} @@ -269,15 +286,20 @@ result, err := client.InvokeMethod(ctx, "checkout", "checkout/" + strconv.Itoa(o {{% codetab %}} ```javascript -//headers - +//dependencies import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; //code +const daprHost = "127.0.0.1"; -const daprHost = "127.0.0.1"; -const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); -const result = await client.invoker.invoke('checkout' , "checkout/" + orderId , HttpMethod.GET); +var main = function() { + var orderId = 100; + //Using Dapr SDK to invoke a method + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + const result = await client.invoker.invoke('checkout' , "checkout/" + orderId , HttpMethod.GET); +} + +main(); ``` {{% /codetab %}} @@ -341,4 +363,4 @@ For more information on tracing and logs see the [observability]({{< ref observa ## Related Links * [Service invocation overview]({{< ref service-invocation-overview.md >}}) -* [Service invocation API specification]({{< ref service_invocation_api.md >}}) \ No newline at end of file +* [Service invocation API specification]({{< ref service_invocation_api.md >}}) diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md index d00c1ea61..1684b7ddb 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md @@ -19,6 +19,12 @@ In this guide we'll start of with the basics: Using the key/value state API to a - [Dapr CLI]({{< ref install-dapr-cli.md >}}) - Initialized [Dapr environment]({{< ref install-dapr-selfhost.md >}}) +## Example: + +The below code examples loosely describe an application that processes orders. In the examples, there is an order processing service which has a Dapr sidecar. The order processing service uses Dapr to store state in a Redis state store. + +Diagram showing state management of example service + ## Step 1: Setup a state store A state store component represents a resource that Dapr uses to communicate with a database. @@ -60,136 +66,255 @@ See the instructions [here]({{< ref "setup-state-store" >}}) on how to setup dif ## Step 2: Save and retrieve a single state -The following example shows how to a single key/value pair using the Dapr state building block. +The following example shows how to save and retrieve a single key/value pair using the Dapr state building block. {{% alert title="Note" color="warning" %}} It is important to set an app-id, as the state keys are prefixed with this value. If you don't set it one is generated for you at runtime, and the next time you run the command a new one will be generated and you will no longer be able to access previously saved state. {{% /alert %}} -{{< tabs "HTTP API (Bash)" "HTTP API (PowerShell)" "Python SDK" "PHP SDK">}} +Below are code examples that leverage Dapr SDKs for saving and retrieving a single state. -{{% codetab %}} -Begin by launching a Dapr sidecar: - -```bash -dapr run --app-id myapp --dapr-http-port 3500 -``` - -Then in a separate terminal save a key/value pair into your statestore: -```bash -curl -X POST -H "Content-Type: application/json" -d '[{ "key": "key1", "value": "value1"}]' http://localhost:3500/v1.0/state/statestore -``` - -Now get the state you just saved: -```bash -curl http://localhost:3500/v1.0/state/statestore/key1 -``` - -You can also restart your sidecar and try retrieving state again to see that state persists separate from the app. -{{% /codetab %}} +{{< tabs Dotnet Java Python Go Javascript "HTTP API (Bash)" "HTTP API (PowerShell)">}} {{% codetab %}} -Begin by launching a Dapr sidecar: +```csharp + +//dependencies +using Dapr.Client; + +//code +namespace EventService +{ + class Program + { + static async Task Main(string[] args) + { + string DAPR_STORE_NAME = "statestore"; + + int orderId = 100; + //Using Dapr SDK to save and get state + using var client = new DaprClientBuilder().Build(); + await client.SaveStateAsync(DAPR_STORE_NAME, "order_1", orderId.ToString()); + await client.SaveStateAsync(DAPR_STORE_NAME, "order_2", orderId.ToString()); + var result = await client.GetStateAsync(DAPR_STORE_NAME, orderId.ToString()); + } + } +} + +``` + +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash -dapr --app-id myapp --port 3500 run +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 dotnet run ``` -Then in a separate terminal save a key/value pair into your statestore: -```powershell -Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '[{"key": "key1", "value": "value1"}]' -Uri 'http://localhost:3500/v1.0/state/statestore' -``` - -Now get the state you just saved: -```powershell -Invoke-RestMethod -Uri 'http://localhost:3500/v1.0/state/statestore/key1' -``` - -You can also restart your sidecar and try retrieving state again to see that state persists separate from the app. - {{% /codetab %}} + {{% codetab %}} -Save the following to a file named `pythonState.py`: +```java + +//dependencies +import io.dapr.client.DaprClient; +import io.dapr.client.DaprClientBuilder; +import io.dapr.client.domain.State; +import reactor.core.publisher.Mono; + +//code +@SpringBootApplication +public class OrderProcessingServiceApplication { + + private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); + + public static void main(String[] args) throws InterruptedException { + String STATE_STORE_NAME = "statestore"; + + int orderId = 100; + //Using Dapr SDK to save and get state + DaprClient client = new DaprClientBuilder().build(); + client.saveState(STATE_STORE_NAME, "order_1", Integer.toString(orderId)).block(); + client.saveState(STATE_STORE_NAME, "order_2", Integer.toString(orderId)).block(); + Mono> result = client.getState(STATE_STORE_NAME, "order_1", String.class); + } +} + +``` + +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 mvn spring-boot:run +``` + +{{% /codetab %}} + + +{{% codetab %}} ```python + +#dependencies from dapr.clients import DaprClient -with DaprClient() as d: - d.save_state(store_name="statestore", key="myFirstKey", value="myFirstValue" ) - print("State has been stored") +#code +logging.basicConfig(level = logging.INFO) + +DAPR_STORE_NAME = "statestore" - data = d.get_state(store_name="statestore", key="myFirstKey").data - print(f"Got value: {data}") +orderId = 100 +#Using Dapr SDK to save and get state +with DaprClient() as client: + client.save_state(DAPR_STORE_NAME, "order_1", str(orderId)) + result = client.get_state(DAPR_STORE_NAME, "order_1") + logging.info('Result after get: ' + str(result)) ``` -Once saved run the following command to launch a Dapr sidecar and run the Python application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash -dapr --app-id myapp run python pythonState.py -``` - -You should get an output similar to the following, which will show both the Dapr and app logs: - -```md -== DAPR == time="2021-01-06T21:34:33.7970377-08:00" level=info msg="starting Dapr Runtime -- version 0.11.3 -- commit a1a8e11" app_id=Braidbald-Boot scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:34:33.8040378-08:00" level=info msg="standalone mode configured" app_id=Braidbald-Boot scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:34:33.8040378-08:00" level=info msg="app id: Braidbald-Boot" app_id=Braidbald-Boot scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:34:33.9750400-08:00" level=info msg="component loaded. name: statestore, type: state.redis" app_id=Braidbald-Boot scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:34:33.9760387-08:00" level=info msg="API gRPC server is running on port 51656" app_id=Braidbald-Boot scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:34:33.9770372-08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 172.9994ms" app_id=Braidbald-Boot scope=dapr. - -Checking if Dapr sidecar is listening on GRPC port 51656 -Dapr sidecar is up and running. -Updating metadata for app command: python pythonState.py -You are up and running! Both Dapr and your app logs will appear here. - -== APP == State has been stored -== APP == Got value: b'myFirstValue' +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 -- python3 OrderProcessingService.py ``` {{% /codetab %}} + {{% codetab %}} -Save the following in `state-example.php`: +```go -```php -run(function(\Dapr\State\StateManager $stateManager, \Psr\Log\LoggerInterface $logger) { - $stateManager->save_state(store_name: 'statestore', item: new \Dapr\State\StateItem( - key: 'myFirstKey', - value: 'myFirstValue' - )); - $logger->alert('State has been stored'); +) + +//code +func main() { + + STATE_STORE_NAME := "statestore" + + orderId := 100 + + //Using Dapr SDK to save and get state + client, err := dapr.NewClient() + if err != nil { + panic(err) + } + defer client.Close() + ctx := context.Background() + + if err := client.SaveState(ctx, STATE_STORE_NAME, "order_1", []byte(strconv.Itoa(orderId))); err != nil { + panic(err) + } + + result, err := client.GetState(ctx, STATE_STORE_NAME, "order_1") + if err != nil { + panic(err) + } +} - $data = $stateManager->load_state(store_name: 'statestore', key: 'myFirstKey')->value; - $logger->alert("Got value: {data}", ['data' => $data]); -}); ``` -Once saved run the following command to launch a Dapr sidecar and run the PHP application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash -dapr --app-id myapp run -- php state-example.php +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 go run OrderProcessingService.go ``` -You should get an output similar to the following, which will show both the Dapr and app logs: +{{% /codetab %}} -```md -✅ You're up and running! Both Dapr and your app logs will appear here. -== APP == [2021-02-12T16:30:11.078777+01:00] APP.ALERT: State has been stored [] [] +{{% codetab %}} + +```javascript + +//dependencies +import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; + +//code +const daprHost = "127.0.0.1"; + +var main = function() { + const STATE_STORE_NAME = "statestore"; + + var orderId = 100; + //Using Dapr SDK to save and get state + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + await client.state.save(STATE_STORE_NAME, [ + { + key: "order_1", + value: orderId.toString() + }, + { + key: "order_2", + value: orderId.toString() + } + ]); + var result = await client.state.get(STATE_STORE_NAME, "order_1"); +} + +main(); -== APP == [2021-02-12T16:30:11.082620+01:00] APP.ALERT: Got value: myFirstValue {"data":"myFirstValue"} [] ``` +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 npm start +``` + +{{% /codetab %}} + + +{{% codetab %}} +Begin by launching a Dapr sidecar: + +```bash +dapr run --app-id orderprocessing --dapr-http-port 3601 +``` + +Then in a separate terminal save a key/value pair into your statestore: +```bash +curl -X POST -H "Content-Type: application/json" -d '[{ "key": "order_1", "value": "250"}]' http://localhost:3601/v1.0/state/statestore +``` + +Now get the state you just saved: +```bash +curl http://localhost:3601/v1.0/state/statestore/order_1 +``` + +Restart your sidecar and try retrieving state again to observe that state persists separately from the app. +{{% /codetab %}} + +{{% codetab %}} + +Begin by launching a Dapr sidecar: + +```bash +dapr --app-id orderprocessing --port 3601 run +``` + +Then in a separate terminal save a key/value pair into your statestore: +```powershell +Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '[{"key": "order_1", "value": "250"}]' -Uri 'http://localhost:3601/v1.0/state/statestore' +``` + +Now get the state you just saved: +```powershell +Invoke-RestMethod -Uri 'http://localhost:3601/v1.0/state/statestore/order_1' +``` + +Restart your sidecar and try retrieving state again to observe that state persists separately from the app. + {{% /codetab %}} {{< /tabs >}} @@ -197,243 +322,327 @@ You should get an output similar to the following, which will show both the Dapr ## Step 3: Delete state -The following example shows how to delete an item by using a key with the state management API: +Below are code examples that leverage Dapr SDKs for deleting the state. -{{< tabs "HTTP API (Bash)" "HTTP API (PowerShell)" "Python SDK" "PHP SDK">}} +{{< tabs Dotnet Java Python Go Javascript "HTTP API (Bash)" "HTTP API (PowerShell)">}} {{% codetab %}} -With the same dapr instance running from above run: + +```csharp + +//dependencies +using Dapr.Client; + +//code +namespace EventService +{ + class Program + { + static async Task Main(string[] args) + { + string DAPR_STORE_NAME = "statestore"; + //Using Dapr SDK to delete the state + using var client = new DaprClientBuilder().Build(); + await client.DeleteStateAsync(DAPR_STORE_NAME, "order_1", cancellationToken: cancellationToken); + } + } +} + +``` + +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: + ```bash -curl -X DELETE 'http://localhost:3500/v1.0/state/statestore/key1' +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 dotnet run ``` -Try getting state again and note that no value is returned. + {{% /codetab %}} -{{% codetab %}} -With the same dapr instance running from above run: -```powershell -Invoke-RestMethod -Method Delete -Uri 'http://localhost:3500/v1.0/state/statestore/key1' -``` -Try getting state again and note that no value is returned. -{{% /codetab %}} {{% codetab %}} -Update `pythonState.py` with: +```java + +//dependencies +import io.dapr.client.DaprClient; +import io.dapr.client.DaprClientBuilder; + +//code +@SpringBootApplication +public class OrderProcessingServiceApplication { + public static void main(String[] args) throws InterruptedException{ + String STATE_STORE_NAME = "statestore"; + + //Using Dapr SDK to delete the state + DaprClient client = new DaprClientBuilder().build(); + String storedEtag = client.getState(STATE_STORE_NAME, "order_1", String.class).block().getEtag(); + client.deleteState(STATE_STORE_NAME, "order_1", storedEtag, null).block(); + } +} + +``` + +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 mvn spring-boot:run +``` + +{{% /codetab %}} + + +{{% codetab %}} ```python -from dapr.clients import DaprClient -with DaprClient() as d: - d.save_state(store_name="statestore", key="key1", value="value1" ) - print("State has been stored") +#dependencies +from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType - data = d.get_state(store_name="statestore", key="key1").data - print(f"Got value: {data}") +#code +logging.basicConfig(level = logging.INFO) + +DAPR_STORE_NAME = "statestore" - d.delete_state(store_name="statestore", key="key1") +#Using Dapr SDK to delete the state +with DaprClient() as client: + client.delete_state(store_name=DAPR_STORE_NAME, key="order_1") - data = d.get_state(store_name="statestore", key="key1").data - print(f"Got value after delete: {data}") ``` -Now run your program with: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash -dapr --app-id myapp run python pythonState.py +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 -- python3 OrderProcessingService.py ``` -You should see an output similar to the following: - -```md -Starting Dapr with id Yakchocolate-Lord. HTTP Port: 59457. gRPC Port: 59458 - -== DAPR == time="2021-01-06T22:55:36.5570696-08:00" level=info msg="starting Dapr Runtime -- version 0.11.3 -- commit a1a8e11" app_id=Yakchocolate-Lord scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T22:55:36.5690367-08:00" level=info msg="standalone mode configured" app_id=Yakchocolate-Lord scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T22:55:36.7220140-08:00" level=info msg="component loaded. name: statestore, type: state.redis" app_id=Yakchocolate-Lord scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T22:55:36.7230148-08:00" level=info msg="API gRPC server is running on port 59458" app_id=Yakchocolate-Lord scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T22:55:36.7240207-08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 154.984ms" app_id=Yakchocolate-Lord scope=dapr.runtime type=log ver=0.11.3 - -Checking if Dapr sidecar is listening on GRPC port 59458 -Dapr sidecar is up and running. -Updating metadata for app command: python pythonState.py -You're up and running! Both Dapr and your app logs will appear here. - -== APP == State has been stored -== APP == Got value: b'value1' -== APP == Got value after delete: b'' -``` {{% /codetab %}} + {{% codetab %}} -Update `state-example.php` with the following contents: +```go -```php -run(function(\Dapr\State\StateManager $stateManager, \Psr\Log\LoggerInterface $logger) { - $stateManager->save_state(store_name: 'statestore', item: new \Dapr\State\StateItem( - key: 'myFirstKey', - value: 'myFirstValue' - )); - $logger->alert('State has been stored'); +) - $data = $stateManager->load_state(store_name: 'statestore', key: 'myFirstKey')->value; - $logger->alert("Got value: {data}", ['data' => $data]); +//code +func main() { + + STATE_STORE_NAME := "statestore" + + //Using Dapr SDK to delete the state + client, err := dapr.NewClient() + if err != nil { + panic(err) + } + defer client.Close() + ctx := context.Background() + + if err := client.DeleteState(ctx, STATE_STORE_NAME, "order_1"); err != nil { + panic(err) + } +} - $stateManager->delete_keys(store_name: 'statestore', keys: ['myFirstKey']); - $data = $stateManager->load_state(store_name: 'statestore', key: 'myFirstKey')->value; - $logger->alert("Got value after delete: {data}", ['data' => $data]); -}); ``` -Now run it with: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash -dapr --app-id myapp run -- php state-example.php +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 go run OrderProcessingService.go ``` -You should see something similar the following output: +{{% /codetab %}} -```md -✅ You're up and running! Both Dapr and your app logs will appear here. -== APP == [2021-02-12T16:38:00.839201+01:00] APP.ALERT: State has been stored [] [] +{{% codetab %}} -== APP == [2021-02-12T16:38:00.841997+01:00] APP.ALERT: Got value: myFirstValue {"data":"myFirstValue"} [] +```javascript + +//dependencies +import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; + +//code +const daprHost = "127.0.0.1"; + +var main = function() { + const STATE_STORE_NAME = "statestore"; + + //Using Dapr SDK to save and get state + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + await client.state.delete(STATE_STORE_NAME, "order_1"); +} + +main(); -== APP == [2021-02-12T16:38:00.845721+01:00] APP.ALERT: Got value after delete: {"data":null} [] ``` +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 npm start +``` + +{{% /codetab %}} + +{{% codetab %}} +With the same Dapr instance running from above run: +```bash +curl -X DELETE 'http://localhost:3601/v1.0/state/statestore/order_1' +``` +Try getting state again and note that no value is returned. +{{% /codetab %}} + +{{% codetab %}} +With the same Dapr instance running from above run: +```powershell +Invoke-RestMethod -Method Delete -Uri 'http://localhost:3601/v1.0/state/statestore/order_1' +``` +Try getting state again and note that no value is returned. {{% /codetab %}} {{< /tabs >}} ## Step 4: Save and retrieve multiple states -Dapr also allows you to save and retrieve multiple states in the same call. +Below are code examples that leverage Dapr SDKs for saving and retrieving multiple states. -{{< tabs "HTTP API (Bash)" "HTTP API (PowerShell)" "Python SDK" "PHP SDK">}} +{{< tabs Java Python Javascript "HTTP API (Bash)" "HTTP API (PowerShell)">}} {{% codetab %}} -With the same dapr instance running from above save two key/value pairs into your statestore: + +```java + +//dependencies +import io.dapr.client.DaprClient; +import io.dapr.client.DaprClientBuilder; +import io.dapr.client.domain.State; + +//code +@SpringBootApplication +public class OrderProcessingServiceApplication { + + private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); + + public static void main(String[] args) throws InterruptedException{ + String STATE_STORE_NAME = "statestore"; + + int orderId = 100; + //Using Dapr SDK to retrieve multiple states + DaprClient client = new DaprClientBuilder().build(); + Mono>> resultBulk = client.getBulkState(STATE_STORE_NAME, + Arrays.asList("order_1", "order_2"), String.class); + } +} + +``` + +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: + ```bash -curl -X POST -H "Content-Type: application/json" -d '[{ "key": "key1", "value": "value1"}, { "key": "key2", "value": "value2"}]' http://localhost:3500/v1.0/state/statestore -``` - -Now get the states you just saved: -```bash -curl -X POST -H "Content-Type: application/json" -d '{"keys":["key1", "key2"]}' http://localhost:3500/v1.0/state/statestore/bulk -``` -{{% /codetab %}} - -{{% codetab %}} -With the same dapr instance running from above save two key/value pairs into your statestore: -```powershell -Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '[{ "key": "key1", "value": "value1"}, { "key": "key2", "value": "value2"}]' -Uri 'http://localhost:3500/v1.0/state/statestore' -``` - -Now get the states you just saved: -```powershell -Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"keys":["key1", "key2"]}' -Uri 'http://localhost:3500/v1.0/state/statestore/bulk' +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 mvn spring-boot:run ``` {{% /codetab %}} + {{% codetab %}} -The `StateItem` object can be used to store multiple Dapr states with the `save_states` and `get_states` methods. - -Update your `pythonState.py` file with the following code: - ```python + +#dependencies from dapr.clients import DaprClient from dapr.clients.grpc._state import StateItem -with DaprClient() as d: - s1 = StateItem(key="key1", value="value1") - s2 = StateItem(key="key2", value="value2") +#code +logging.basicConfig(level = logging.INFO) + +DAPR_STORE_NAME = "statestore" - d.save_bulk_state(store_name="statestore", states=[s1,s2]) - print("States have been stored") +orderId = 100 +#Using Dapr SDK to save and retrieve multiple states +with DaprClient() as client: + client.save_bulk_state(store_name=DAPR_STORE_NAME, states=[StateItem(key="order_2", value=str(orderId))]) + result = client.get_bulk_state(store_name=DAPR_STORE_NAME, keys=["order_1", "order_2"], states_metadata={"metakey": "metavalue"}).items + logging.info('Result after get bulk: ' + str(result)) - items = d.get_bulk_state(store_name="statestore", keys=["key1", "key2"]).items - print(f"Got items: {[i.data for i in items]}") ``` -Now run your program with: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash -dapr --app-id myapp run python pythonState.py +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 -- python3 OrderProcessingService.py ``` -You should see an output similar to the following: +{{% /codetab %}} -```md -== DAPR == time="2021-01-06T21:54:56.7262358-08:00" level=info msg="starting Dapr Runtime -- version 0.11.3 -- commit a1a8e11" app_id=Musesequoia-Sprite scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:54:56.7401933-08:00" level=info msg="standalone mode configured" app_id=Musesequoia-Sprite scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:54:56.8754240-08:00" level=info msg="Initialized name resolution to standalone" app_id=Musesequoia-Sprite scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:54:56.8844248-08:00" level=info msg="component loaded. name: statestore, type: state.redis" app_id=Musesequoia-Sprite scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:54:56.8854273-08:00" level=info msg="API gRPC server is running on port 60614" app_id=Musesequoia-Sprite scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:54:56.8854273-08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 145.234ms" app_id=Musesequoia-Sprite scope=dapr.runtime type=log ver=0.11.3 -Checking if Dapr sidecar is listening on GRPC port 60614 -Dapr sidecar is up and running. -Updating metadata for app command: python pythonState.py -You're up and running! Both Dapr and your app logs will appear here. -== APP == States have been stored -== APP == Got items: [b'value1', b'value2'] +{{% codetab %}} + +```javascript + +//dependencies +import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; + +//code +const daprHost = "127.0.0.1"; + +var main = function() { + const STATE_STORE_NAME = "statestore"; + + var orderId = 100; + //Using Dapr SDK to save and retrieve multiple states + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + await client.state.save(STATE_STORE_NAME, [ + { + key: "order_1", + value: orderId.toString() + }, + { + key: "order_2", + value: orderId.toString() + } + ]); + result = await client.state.getBulk(STATE_STORE_NAME, ["order_1", "order_2"]); +} + +main(); + +``` + +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 npm start ``` {{% /codetab %}} {{% codetab %}} - -To batch load and save state with PHP, just create a "Plain Ole' PHP Object" (POPO) and annotate it with -the StateStore annotation. - -Update the `state-example.php` file: - -```php -run(function(\Dapr\State\StateManager $stateManager, \Psr\Log\LoggerInterface $logger) { - $obj = new MyState(); - $stateManager->save_object(item: $obj); - $logger->alert('States have been stored'); - - $stateManager->load_object(into: $obj); - $logger->alert("Got value: {data}", ['data' => $obj]); -}); -``` - -Run the app: - +With the same Dapr instance running from above save two key/value pairs into your statestore: ```bash -dapr --app-id myapp run -- php state-example.php +curl -X POST -H "Content-Type: application/json" -d '[{ "key": "order_1", "value": "250"}, { "key": "order_2", "value": "550"}]' http://localhost:3601/v1.0/state/statestore ``` -And see the following output: +Now get the states you just saved: +```bash +curl -X POST -H "Content-Type: application/json" -d '{"keys":["order_1", "order_2"]}' http://localhost:3601/v1.0/state/statestore/bulk +``` +{{% /codetab %}} -```md -✅ You're up and running! Both Dapr and your app logs will appear here. +{{% codetab %}} +With the same Dapr instance running from above save two key/value pairs into your statestore: +```powershell +Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '[{ "key": "order_1", "value": "250"}, { "key": "order_2", "value": "550"}]' -Uri 'http://localhost:3601/v1.0/state/statestore' +``` -== APP == [2021-02-12T16:55:02.913801+01:00] APP.ALERT: States have been stored [] [] - -== APP == [2021-02-12T16:55:02.917850+01:00] APP.ALERT: Got value: [object MyState] {"data":{"MyState":{"key1":"value1","key2":"value2"}}} [] +Now get the states you just saved: +```powershell +Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"keys":["order_1", "order_2"]}' -Uri 'http://localhost:3601/v1.0/state/statestore/bulk' ``` {{% /codetab %}} @@ -446,138 +655,202 @@ And see the following output: State transactions require a state store that supports multi-item transactions. Visit the [supported state stores page]({{< ref supported-state-stores >}}) page for a full list. Note that the default Redis container created in a self-hosted environment supports them. {{% /alert %}} -{{< tabs "HTTP API (Bash)" "HTTP API (PowerShell)" "Python SDK" "PHP SDK">}} +Below are code examples that leverage Dapr SDKs for performing state transactions. + +{{< tabs Dotnet Java Python Javascript "HTTP API (Bash)" "HTTP API (PowerShell)">}} {{% codetab %}} -With the same dapr instance running from above perform two state transactions: + +```csharp + +//dependencies +using Dapr.Client; + +//code +namespace EventService +{ + class Program + { + static async Task Main(string[] args) + { + string DAPR_STORE_NAME = "statestore"; + + int orderId = 100; + //Using Dapr SDK to perform the state transactions + using var client = new DaprClientBuilder().Build(); + var requests = new List() + { + new StateTransactionRequest("order_3", JsonSerializer.SerializeToUtf8Bytes(orderId.ToString()), StateOperationType.Upsert), + new StateTransactionRequest("order_2", null, StateOperationType.Delete) + }; + CancellationTokenSource source = new CancellationTokenSource(); + CancellationToken cancellationToken = source.Token; + await client.ExecuteStateTransactionAsync(DAPR_STORE_NAME, requests, cancellationToken: cancellationToken); + } + } +} + +``` + +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: + ```bash -curl -X POST -H "Content-Type: application/json" -d '{"operations": [{"operation":"upsert", "request": {"key": "key1", "value": "newValue1"}}, {"operation":"delete", "request": {"key": "key2"}}]}' http://localhost:3500/v1.0/state/statestore/transaction -``` - -Now see the results of your state transactions: -```bash -curl -X POST -H "Content-Type: application/json" -d '{"keys":["key1", "key2"]}' http://localhost:3500/v1.0/state/statestore/bulk -``` -{{% /codetab %}} - -{{% codetab %}} -With the same dapr instance running from above save two key/value pairs into your statestore: -```powershell -Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"operations": [{"operation":"upsert", "request": {"key": "key1", "value": "newValue1"}}, {"operation":"delete", "request": {"key": "key2"}}]}' -Uri 'http://localhost:3500/v1.0/state/statestore' -``` - -Now see the results of your state transactions: -```powershell -Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"keys":["key1", "key2"]}' -Uri 'http://localhost:3500/v1.0/state/statestore/bulk' +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 dotnet run ``` {{% /codetab %}} + {{% codetab %}} -The `TransactionalStateOperation` can perform a state transaction if your state stores need to be transactional. +```java -Update your `pythonState.py` file with the following code: +//dependencies +import io.dapr.client.DaprClient; +import io.dapr.client.DaprClientBuilder; +import io.dapr.client.domain.State; +import io.dapr.client.domain.TransactionalStateOperation; + + +//code +@SpringBootApplication +public class OrderProcessingServiceApplication { + + private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); + + public static void main(String[] args) throws InterruptedException{ + String STATE_STORE_NAME = "statestore"; + + int orderId = 100; + //Using Dapr SDK to perform the state transactions + DaprClient client = new DaprClientBuilder().build(); + List> operationList = new ArrayList<>(); + operationList.add(new TransactionalStateOperation<>(TransactionalStateOperation.OperationType.UPSERT, + new State<>("order_3", Integer.toString(orderId), ""))); + operationList.add(new TransactionalStateOperation<>(TransactionalStateOperation.OperationType.DELETE, + new State<>("order_2"))); + client.executeStateTransaction(STATE_STORE_NAME, operationList).block(); + } +} + +``` + +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 mvn spring-boot:run +``` + +{{% /codetab %}} + + +{{% codetab %}} ```python + +#dependencies from dapr.clients import DaprClient from dapr.clients.grpc._state import StateItem from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType -with DaprClient() as d: - s1 = StateItem(key="key1", value="value1") - s2 = StateItem(key="key2", value="value2") +#code +logging.basicConfig(level = logging.INFO) + +DAPR_STORE_NAME = "statestore" - d.save_bulk_state(store_name="statestore", states=[s1,s2]) - print("States have been stored") +orderId = 100 +#Using Dapr SDK to perform the state transactions +with DaprClient() as client: + client.execute_state_transaction(store_name=DAPR_STORE_NAME, operations=[ + TransactionalStateOperation( + operation_type=TransactionOperationType.upsert, + key="order_3", + data=str(orderId)), + TransactionalStateOperation(key="order_3", data=str(orderId)), + TransactionalStateOperation( + operation_type=TransactionOperationType.delete, + key="order_2", + data=str(orderId)), + TransactionalStateOperation(key="order_2", data=str(orderId)) + ]) - d.execute_state_transaction( - store_name="statestore", - operations=[ - TransactionalStateOperation(key="key1", data="newValue1", operation_type=TransactionOperationType.upsert), - TransactionalStateOperation(key="key2", data="value2", operation_type=TransactionOperationType.delete) - ] - ) - print("State transactions have been completed") - - items = d.get_bulk_state(store_name="statestore", keys=["key1", "key2"]).items - print(f"Got items: {[i.data for i in items]}") ``` -Now run your program with: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash -dapr run python pythonState.py +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 -- python3 OrderProcessingService.py ``` -You should see an output similar to the following: +{{% /codetab %}} -```md -Starting Dapr with id Singerchecker-Player. HTTP Port: 59533. gRPC Port: 59534 -== DAPR == time="2021-01-06T22:18:14.1246721-08:00" level=info msg="starting Dapr Runtime -- version 0.11.3 -- commit a1a8e11" app_id=Singerchecker-Player scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T22:18:14.1346254-08:00" level=info msg="standalone mode configured" app_id=Singerchecker-Player scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T22:18:14.2747063-08:00" level=info msg="component loaded. name: statestore, type: state.redis" app_id=Singerchecker-Player scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T22:18:14.2757062-08:00" level=info msg="API gRPC server is running on port 59534" app_id=Singerchecker-Player scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T22:18:14.2767059-08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 142.0805ms" app_id=Singerchecker-Player scope=dapr.runtime type=log ver=0.11.3 -Checking if Dapr sidecar is listening on GRPC port 59534 -Dapr sidecar is up and running. -Updating metadata for app command: python pythonState.py -You're up and running! Both Dapr and your app logs will appear here. +{{% codetab %}} -== APP == State transactions have been completed -== APP == Got items: [b'value1', b''] +```javascript + +//dependencies +import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; + +//code +const daprHost = "127.0.0.1"; + +var main = function() { + const STATE_STORE_NAME = "statestore"; + + var orderId = 100; + //Using Dapr SDK to save and retrieve multiple states + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + await client.state.transaction(STATE_STORE_NAME, [ + { + operation: "upsert", + request: { + key: "order_3", + value: orderId.toString() + } + }, + { + operation: "delete", + request: { + key: "order_2" + } + } + ]); +} + +main(); + +``` + +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 npm start ``` {{% /codetab %}} {{% codetab %}} - -Transactional state is supported by extending `TransactionalState` base object which hooks into your -object via setters and getters to provide a transaction. Before you created your own transactional object, -but now you'll ask the Dependency Injection framework to build one for you. - -Modify the `state-example.php` file again: - -```php -run(function(MyState $obj, \Psr\Log\LoggerInterface $logger, \Dapr\State\StateManager $stateManager) { - $obj->begin(); - $obj->key1 = 'hello world'; - $obj->key2 = 'value3'; - $obj->commit(); - $logger->alert('Transaction committed!'); - - // begin a new transaction which reloads from the store - $obj->begin(); - $logger->alert("Got value: {key1}, {key2}", ['key1' => $obj->key1, 'key2' => $obj->key2]); -}); -``` - -Run the application: - +With the same Dapr instance running from above perform two state transactions: ```bash -dapr --app-id myapp run -- php state-example.php +curl -X POST -H "Content-Type: application/json" -d '{"operations": [{"operation":"upsert", "request": {"key": "order_1", "value": "250"}}, {"operation":"delete", "request": {"key": "order_2"}}]}' http://localhost:3601/v1.0/state/statestore/transaction ``` -Observe the following output: +Now see the results of your state transactions: +```bash +curl -X POST -H "Content-Type: application/json" -d '{"keys":["order_1", "order_2"]}' http://localhost:3601/v1.0/state/statestore/bulk +``` +{{% /codetab %}} -```md -✅ You're up and running! Both Dapr and your app logs will appear here. +{{% codetab %}} +With the same Dapr instance running from above save two key/value pairs into your statestore: +```powershell +Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"operations": [{"operation":"upsert", "request": {"key": "order_1", "value": "250"}}, {"operation":"delete", "request": {"key": "order_2"}}]}' -Uri 'http://localhost:3601/v1.0/state/statestore' +``` -== APP == [2021-02-12T17:10:06.837110+01:00] APP.ALERT: Transaction committed! [] [] - -== APP == [2021-02-12T17:10:06.840857+01:00] APP.ALERT: Got value: hello world, value3 {"key1":"hello world","key2":"value3"} [] +Now see the results of your state transactions: +```powershell +Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"keys":["order_1", "order_2"]}' -Uri 'http://localhost:3601/v1.0/state/statestore/bulk' ``` {{% /codetab %}} @@ -588,4 +861,4 @@ Observe the following output: - Read the full [State API reference]({{< ref state_api.md >}}) - Try one of the [Dapr SDKs]({{< ref sdks >}}) -- Build a [stateful service]({{< ref howto-stateful-service.md >}}) +- Build a [stateful service]({{< ref howto-stateful-service.md >}}) \ No newline at end of file diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/state-store-ttl.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/state-store-ttl.md index a87587b15..b1e349f3a 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/state-store-ttl.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/state-store-ttl.md @@ -31,12 +31,38 @@ Please refer to the TTL column in the tables at [state store components]({{< ref State TTL can be set in the metadata as part of the state store set request: -{{< tabs "HTTP API (Bash)" "HTTP API (PowerShell)" "Python SDK" "PHP SDK">}} +{{< tabs Python "HTTP API (Bash)" "HTTP API (PowerShell)">}} + +{{% codetab %}} + +```python +#dependencies + +from dapr.clients import DaprClient + +#code + +DAPR_STORE_NAME = "statestore" + +with DaprClient() as client: + client.save_state(DAPR_STORE_NAME, "order_1", str(orderId), metadata=( + ('ttlInSeconds', '120') + )) + +``` + +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 -- python3 OrderProcessingService.py +``` + +{{% /codetab %}} {{% codetab %}} ```bash -curl -X POST -H "Content-Type: application/json" -d '[{ "key": "key1", "value": "value1", "metadata": { "ttlInSeconds": "120" } }]' http://localhost:3500/v1.0/state/statestore +curl -X POST -H "Content-Type: application/json" -d '[{ "key": "order_1", "value": "250", "metadata": { "ttlInSeconds": "120" } }]' http://localhost:3601/v1.0/state/statestore ``` {{% /codetab %}} @@ -44,48 +70,7 @@ curl -X POST -H "Content-Type: application/json" -d '[{ "key": "key1", "value": {{% codetab %}} ```powershell -Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '[{"key": "key1", "value": "value1", "metadata": {"ttlInSeconds": "120"}}]' -Uri 'http://localhost:3500/v1.0/state/statestore' -``` - -{{% /codetab %}} - -{{% codetab %}} - -```python -from dapr.clients import DaprClient - -with DaprClient() as d: - d.save_state( - store_name="statestore", - key="myFirstKey", - value="myFirstValue", - metadata=( - ('ttlInSeconds', '120') - ) - ) - print("State has been stored") - -``` - -{{% /codetab %}} - -{{% codetab %}} - -Save the following in `state-example.php`: - -```php -run(function(\Dapr\State\StateManager $stateManager, \Psr\Log\LoggerInterface $logger) { - $stateManager->save_state(store_name: 'statestore', item: new \Dapr\State\StateItem( - key: 'myFirstKey', - value: 'myFirstValue', - metadata: ['ttlInSeconds' => '120'] - )); - $logger->alert('State has been stored'); -}); +Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '[{"key": "order_1", "value": "250", "metadata": {"ttlInSeconds": "120"}}]' -Uri 'http://localhost:3601/v1.0/state/statestore' ``` {{% /codetab %}} @@ -98,4 +83,4 @@ See [this guide]({{< ref state_api.md >}}) for a reference on the state API. - Learn [how to use key value pairs to persist a state]({{< ref howto-get-save-state.md >}}) - List of [state store components]({{< ref supported-state-stores >}}) -- Read the [API reference]({{< ref state_api.md >}}) +- Read the [API reference]({{< ref state_api.md >}}) \ No newline at end of file diff --git a/daprdocs/static/images/service_invocation_eg.png b/daprdocs/static/images/building-block-service-invocation-example.png similarity index 100% rename from daprdocs/static/images/service_invocation_eg.png rename to daprdocs/static/images/building-block-service-invocation-example.png diff --git a/daprdocs/static/images/building-block-state-management-example.png b/daprdocs/static/images/building-block-state-management-example.png new file mode 100644 index 000000000..87b3b9b32 Binary files /dev/null and b/daprdocs/static/images/building-block-state-management-example.png differ