diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index aa50e1dc1..a6b77f550 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,7 +1,7 @@ Thank you for helping make the Dapr documentation better! **Please follow this checklist before submitting:** - +- [ ] Commits are signed with Developer Certificate of Origin (DCO - [learn more](https://docs.dapr.io/contributing/contributing-overview/#developer-certificate-of-origin-signing-your-work)) - [ ] [Read the contribution guide](https://docs.dapr.io/contributing/contributing-docs/) - [ ] Commands include options for Linux, MacOS, and Windows within codetabs - [ ] New file and folder names are globally unique diff --git a/daprdocs/content/en/concepts/service-mesh.md b/daprdocs/content/en/concepts/service-mesh.md index 1c81221f7..96613a319 100644 --- a/daprdocs/content/en/concepts/service-mesh.md +++ b/daprdocs/content/en/concepts/service-mesh.md @@ -7,7 +7,7 @@ description: > How Dapr compares to and works with service meshes --- -Dapr uses a sidecar architecture, running as a separate process alongside the application and includes features such as service invocation, network security, and distributed tracing. This often raises the question: how does Dapr compare to service mesh solutions such as [Linkerd](https://linkerd.io/), [Istio](https://istio.io/) and [Open Service Mesh](https://openservicemesh.io/) amoung others? +Dapr uses a sidecar architecture, running as a separate process alongside the application and includes features such as service invocation, network security, and distributed tracing. This often raises the question: how does Dapr compare to service mesh solutions such as [Linkerd](https://linkerd.io/), [Istio](https://istio.io/) and [Open Service Mesh](https://openservicemesh.io/) among others? ## How Dapr and service meshes compare While Dapr and service meshes do offer some overlapping capabilities, **Dapr is not a service mesh**, where a service mesh is defined as a *networking* service mesh. Unlike a service mesh which is focused on networking concerns, Dapr is focused on providing building blocks that make it easier for developers to build applications as microservices. Dapr is developer-centric, versus service meshes which are infrastructure-centric. diff --git a/daprdocs/content/en/contributing/contributing-overview.md b/daprdocs/content/en/contributing/contributing-overview.md index 6a8a404b3..69bb68c2f 100644 --- a/daprdocs/content/en/contributing/contributing-overview.md +++ b/daprdocs/content/en/contributing/contributing-overview.md @@ -43,6 +43,7 @@ Before you submit an issue, make sure you've checked the following: - Many changes to the Dapr runtime may require changes to the API. In that case, the best place to discuss the potential feature is the main [Dapr repo](https://github.com/dapr/dapr). - Other examples could include bindings, state stores or entirely new components. + ## Pull Requests All contributions come through pull requests. To submit a proposed change, follow this workflow: @@ -53,18 +54,53 @@ All contributions come through pull requests. To submit a proposed change, follo 1. Create your change - Code changes require tests 1. Update relevant documentation for the change -1. Commit and open a PR +1. Commit with [DCO sign-off]({{< ref "contributing-overview.md#developer-certificate-of-origin-signing-your-work" >}}) and open a PR 1. Wait for the CI process to finish and make sure all checks are green 1. A maintainer of the project will be assigned, and you can expect a review within a few days + #### Use work-in-progress PRs for early feedback A good way to communicate before investing too much time is to create a "Work-in-progress" PR and share it with your reviewers. The standard way of doing this is to add a "[WIP]" prefix in your PR's title and assign the **do-not-merge** label. This will let people looking at your PR know that it is not well baked yet. -### Use of Third-party code +## Use of Third-party code - Third-party code must include licenses. +## Developer Certificate of Origin: Signing your work +#### Every commit needs to be signed + +The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the [DCO](https://developercertificate.org/), reformatted for readability: +``` +By making a contribution to this project, I certify that: + (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or + (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or + (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. + (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. +``` +Contributors sign-off that they adhere to these requirements by adding a `Signed-off-by` line to commit messages. + +``` +This is my commit message +Signed-off-by: Random J Developer +``` +Git even has a `-s` command line option to append this automatically to your commit message: +``` +$ git commit -s -m 'This is my commit message' +``` + +Each Pull Request is checked whether or not commits in a Pull Request do contain a valid Signed-off-by line. + +#### I didn't sign my commit, now what?! + +No worries - You can easily replay your changes, sign them and force push them! + +``` +git checkout +git commit --amend --no-edit --signoff +git push --force-with-lease +``` + ## Code of Conduct Please see the [Dapr community code of conduct](https://github.com/dapr/community/blob/master/CODE-OF-CONDUCT.md). diff --git a/daprdocs/content/en/developing-applications/building-blocks/bindings/howto-bindings.md b/daprdocs/content/en/developing-applications/building-blocks/bindings/howto-bindings.md index ea45ababf..ce9c1d87a 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/bindings/howto-bindings.md +++ b/daprdocs/content/en/developing-applications/building-blocks/bindings/howto-bindings.md @@ -9,11 +9,11 @@ weight: 300 Output bindings enable you to invoke external resources without taking dependencies on special SDK or libraries. For a complete sample showing output bindings, visit this [link](https://github.com/dapr/quickstarts/tree/master/bindings). -Watch this [video](https://www.youtube.com/watch?v=ysklxm81MTs&feature=youtu.be&t=1960) on how to use bi-directional output bindings. +## Example: -
- -
+The below code example loosely describes an application that processes orders. In the example, there is an order processing service which has a Dapr sidecar. The order processing service uses Dapr to invoke external resources, in this case a Kafka, via an output binding. + +Diagram showing bindings of example service ## 1. Create a binding @@ -21,7 +21,7 @@ An output binding represents a resource that Dapr uses to invoke and send messag For the purpose of this guide, you'll use a Kafka binding. You can find a list of the different binding specs [here]({{< ref setup-bindings >}}). -Create a new binding component with the name of `myevent`. +Create a new binding component with the name of `checkout`. Inside the `metadata` section, configure Kafka related properties such as the topic to publish the message to and the broker. @@ -36,16 +36,24 @@ Create the following YAML file, named `binding.yaml`, and save this to a `compon apiVersion: dapr.io/v1alpha1 kind: Component metadata: - name: myevent - namespace: default + name: checkout spec: type: bindings.kafka version: v1 metadata: + # Kafka broker connection setting - name: brokers value: localhost:9092 + # consumer configuration: topic and consumer group + - name: topics + value: sample + - name: consumerGroup + value: group1 + # publisher configuration: topic - name: publishTopic - value: topic1 + value: sample + - name: authRequired + value: "false" ``` {{% /codetab %}} @@ -59,40 +67,273 @@ To deploy this into a Kubernetes cluster, fill in the `metadata` connection deta apiVersion: dapr.io/v1alpha1 kind: Component metadata: - name: myevent - namespace: default + name: checkout spec: type: bindings.kafka version: v1 metadata: + # Kafka broker connection setting - name: brokers value: localhost:9092 + # consumer configuration: topic and consumer group + - name: topics + value: sample + - name: consumerGroup + value: group1 + # publisher configuration: topic - name: publishTopic - value: topic1 + value: sample + - name: authRequired + value: "false" ``` {{% /codetab %}} {{< /tabs >}} -## 2. Send an event +## 2. Send an event (Output binding) + +Below are code examples that leverage Dapr SDKs to interact with an output binding. + +{{< tabs Dotnet Java Python Go Javascript>}} + +{{% codetab %}} + +```csharp +//dependencies +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Dapr.Client; +using Microsoft.AspNetCore.Mvc; +using System.Threading; + +//code +namespace EventService +{ + class Program + { + static async Task Main(string[] args) + { + string BINDING_NAME = "checkout"; + string BINDING_OPERATION = "create"; + while(true) { + System.Threading.Thread.Sleep(5000); + Random random = new Random(); + int orderId = random.Next(1,1000); + using var client = new DaprClientBuilder().Build(); + //Using Dapr SDK to invoke output binding + await client.InvokeBindingAsync(BINDING_NAME, BINDING_OPERATION, orderId); + Console.WriteLine("Sending message: " + orderId); + } + } + } +} + +``` + +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 --app-ssl dotnet run +``` + +{{% /codetab %}} + +{{% codetab %}} + +```java +//dependencies +import io.dapr.client.DaprClient; +import io.dapr.client.DaprClientBuilder; +import io.dapr.client.domain.HttpExtension; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +//code +@SpringBootApplication +public class OrderProcessingServiceApplication { + + private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); + + public static void main(String[] args) throws InterruptedException{ + String BINDING_NAME = "checkout"; + String BINDING_OPERATION = "create"; + while(true) { + TimeUnit.MILLISECONDS.sleep(5000); + Random random = new Random(); + int orderId = random.nextInt(1000-1) + 1; + DaprClient client = new DaprClientBuilder().build(); + //Using Dapr SDK to invoke output binding + client.invokeBinding(BINDING_NAME, BINDING_OPERATION, orderId).block(); + log.info("Sending message: " + orderId); + } + } +} + +``` + +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 +import random +from time import sleep +import requests +import logging +import json +from dapr.clients import DaprClient + +#code +logging.basicConfig(level = logging.INFO) +BINDING_NAME = 'checkout' +BINDING_OPERATION = 'create' +while True: + sleep(random.randrange(50, 5000) / 1000) + orderId = random.randint(1, 1000) + with DaprClient() as client: + #Using Dapr SDK to invoke output binding + resp = client.invoke_binding(BINDING_NAME, BINDING_OPERATION, json.dumps(orderId)) + logging.basicConfig(level = logging.INFO) + logging.info('Sending message: ' + str(orderId)) + +``` + +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 --app-protocol grpc python3 OrderProcessingService.py +``` + +{{% /codetab %}} + +{{% codetab %}} + +```go +//dependencies +import ( + "context" + "log" + "math/rand" + "time" + "strconv" + dapr "github.com/dapr/go-sdk/client" + +) + +//code +func main() { + BINDING_NAME := "checkout"; + BINDING_OPERATION := "create"; + for i := 0; i < 10; i++ { + time.Sleep(5000) + orderId := rand.Intn(1000-1) + 1 + client, err := dapr.NewClient() + if err != nil { + panic(err) + } + defer client.Close() + ctx := context.Background() + //Using Dapr SDK to invoke output binding + in := &dapr.InvokeBindingRequest{ Name: BINDING_NAME, Operation: BINDING_OPERATION , Data: []byte(strconv.Itoa(orderId))} + err = client.InvokeOutputBinding(ctx, in) + log.Println("Sending message: " + strconv.Itoa(orderId)) + } +} + +``` + +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 go run OrderProcessingService.go +``` + +{{% /codetab %}} + +{{% codetab %}} + +```javascript +//dependencies + +import { DaprServer, DaprClient, CommunicationProtocolEnum } from 'dapr-client'; + +//code +const daprHost = "127.0.0.1"; + +var main = function() { + for(var i=0;i<10;i++) { + sleep(5000); + var orderId = Math.floor(Math.random() * (1000 - 1) + 1); + start(orderId).catch((e) => { + console.error(e); + process.exit(1); + }); + } +} + +async function start(orderId) { + const BINDING_NAME = "checkout"; + const BINDING_OPERATION = "create"; + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + //Using Dapr SDK to invoke output binding + const result = await client.binding.send(BINDING_NAME, BINDING_OPERATION, { orderId: orderId }); + console.log("Sending message: " + orderId); +} + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +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 %}} + +{{< /tabs >}} All that's left now is to invoke the output bindings endpoint on a running Dapr instance. -You can do so using HTTP: +You can also invoke the output bindings endpoint using HTTP: ```bash -curl -X POST -H 'Content-Type: application/json' http://localhost:3500/v1.0/bindings/myevent -d '{ "data": { "message": "Hi!" }, "operation": "create" }' +curl -X POST -H 'Content-Type: application/json' http://localhost:3601/v1.0/bindings/checkout -d '{ "data": { "orderId": "100" }, "operation": "create" }' ``` -As seen above, you invoked the `/binding` endpoint with the name of the binding to invoke, in our case its `myevent`. +As seen above, you invoked the `/binding` endpoint with the name of the binding to invoke, in our case its `checkout`. The payload goes inside the mandatory `data` field, and can be any JSON serializable value. You'll also notice that there's an `operation` field that tells the binding what you need it to do. You can check [here]({{< ref supported-bindings >}}) which operations are supported for every output binding. +Watch this [video](https://www.youtube.com/watch?v=ysklxm81MTs&feature=youtu.be&t=1960) on how to use bi-directional output bindings. + +
+ +
+ ## References - [Binding API]({{< ref bindings_api.md >}}) - [Binding components]({{< ref bindings >}}) -- [Binding detailed specifications]({{< ref supported-bindings >}}) \ No newline at end of file +- [Binding detailed specifications]({{< ref supported-bindings >}}) diff --git a/daprdocs/content/en/developing-applications/building-blocks/bindings/howto-triggers.md b/daprdocs/content/en/developing-applications/building-blocks/bindings/howto-triggers.md index c0afc13a8..ad3da9c6a 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/bindings/howto-triggers.md +++ b/daprdocs/content/en/developing-applications/building-blocks/bindings/howto-triggers.md @@ -1,7 +1,7 @@ --- type: docs title: "How-To: Trigger your application with input bindings" -linkTitle: "How-To: Triggers" +linkTitle: "How-To: Input bindings" description: "Use Dapr input bindings to trigger event driven applications" weight: 200 --- @@ -18,78 +18,270 @@ Dapr bindings allow you to: For more info on bindings, read [this overview]({{}}). -For a quickstart sample showing bindings, visit this [link](https://github.com/dapr/quickstarts/tree/master/bindings). +## Example: + +The below code example loosely describes an application that processes orders. In the example, there is an order processing service which has a Dapr sidecar. The checkout service uses Dapr to trigger the application via an input binding. + +Diagram showing bindings of example service ## 1. Create a binding -An input binding represents an event resource that Dapr uses to read events from and push to your application. +An input binding represents a resource that Dapr uses to read events from and push to your application. -For the purpose of this HowTo, we'll use a Kafka binding. You can find a list of the different binding specs [here]({{< ref supported-bindings >}}). +For the purpose of this guide, you'll use a Kafka binding. You can find a list of supported binding components [here]({{< ref setup-bindings >}}). -Create the following YAML file, named binding.yaml, and save this to a `components` sub-folder in your application directory. +Create a new binding component with the name of `checkout`. + +Inside the `metadata` section, configure Kafka related properties, such as the topic to publish the message to and the broker. + +{{< tabs "Self-Hosted (CLI)" Kubernetes >}} + +{{% codetab %}} + +Create the following YAML file, named `binding.yaml`, and save this to a `components` sub-folder in your application directory. (Use the `--components-path` flag with `dapr run` to point to your custom components dir) -*Note: When running in Kubernetes, apply this file to your cluster using `kubectl apply -f binding.yaml`* - ```yaml apiVersion: dapr.io/v1alpha1 kind: Component metadata: - name: myevent - namespace: default + name: checkout spec: type: bindings.kafka version: v1 metadata: - - name: topics - value: topic1 + # Kafka broker connection setting - name: brokers value: localhost:9092 + # consumer configuration: topic and consumer group + - name: topics + value: sample - name: consumerGroup value: group1 + # publisher configuration: topic + - name: publishTopic + value: sample + - name: authRequired + value: "false" ``` -Here, you create a new binding component with the name of `myevent`. +{{% /codetab %}} -Inside the `metadata` section, configure the Kafka related properties such as the topics to listen on, the brokers and more. +{{% codetab %}} -## 2. Listen for incoming events +To deploy this into a Kubernetes cluster, fill in the `metadata` connection details of your [desired binding component]({{< ref setup-bindings >}}) in the yaml below (in this case kafka), save as `binding.yaml`, and run `kubectl apply -f binding.yaml`. -Now configure your application to receive incoming events. If using HTTP, you need to listen on a `POST` endpoint with the name of the binding as specified in `metadata.name` in the file. In this example, this is `myevent`. -*The following example shows how you would listen for the event in Node.js, but this is applicable to any programming language* +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: checkout +spec: + type: bindings.kafka + version: v1 + metadata: + # Kafka broker connection setting + - name: brokers + value: localhost:9092 + # consumer configuration: topic and consumer group + - name: topics + value: sample + - name: consumerGroup + value: group1 + # publisher configuration: topic + - name: publishTopic + value: sample + - name: authRequired + value: "false" +``` + +{{% /codetab %}} + +{{< /tabs >}} + +## 2. Listen for incoming events (input binding) + +Now configure your application to receive incoming events. If using HTTP, you need to listen on a `POST` endpoint with the name of the binding as specified in `metadata.name` in the file. + +Below are code examples that leverage Dapr SDKs to demonstrate an output binding. + +{{< tabs Dotnet Java Python Go Javascript>}} + +{{% codetab %}} + +```csharp +//dependencies +using System.Collections.Generic; +using System.Threading.Tasks; +using System; +using Microsoft.AspNetCore.Mvc; + +//code +namespace CheckoutService.controller +{ + [ApiController] + public class CheckoutServiceController : Controller + { + [HttpPost("/checkout")] + public ActionResult getCheckout([FromBody] int orderId) + { + Console.WriteLine("Received Message: " + orderId); + return "CID" + orderId; + } + } +} + +``` + +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 checkout --app-port 6002 --dapr-http-port 3602 --dapr-grpc-port 60002 --app-ssl dotnet run +``` + +{{% /codetab %}} + +{{% codetab %}} + +```java +//dependencies +import org.springframework.web.bind.annotation.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; + +//code +@RestController +@RequestMapping("/") +public class CheckoutServiceController { + private static final Logger log = LoggerFactory.getLogger(CheckoutServiceController.class); + @PostMapping(path = "/checkout") + public Mono getCheckout(@RequestBody(required = false) byte[] body) { + return Mono.fromRunnable(() -> + log.info("Received Message: " + new String(body))); + } +} + +``` + +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 checkout --app-port 6002 --dapr-http-port 3602 --dapr-grpc-port 60002 mvn spring-boot:run +``` + +{{% /codetab %}} + +{{% codetab %}} + +```python +#dependencies +import logging +from dapr.ext.grpc import App, BindingRequest + +#code +app = App() + +@app.binding('checkout') +def getCheckout(request: BindingRequest): + logging.basicConfig(level = logging.INFO) + logging.info('Received Message : ' + request.text()) + +app.run(6002) + +``` + +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 checkout --app-port 6002 --dapr-http-port 3602 --app-protocol grpc -- python3 CheckoutService.py +``` + +{{% /codetab %}} + +{{% codetab %}} + +```go +//dependencies +import ( + "encoding/json" + "log" + "net/http" + "github.com/gorilla/mux" +) + +//code +func getCheckout(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + var orderId int + err := json.NewDecoder(r.Body).Decode(&orderId) + log.Println("Received Message: ", orderId) + if err != nil { + log.Printf("error parsing checkout input binding payload: %s", err) + w.WriteHeader(http.StatusOK) + return + } +} + +func main() { + r := mux.NewRouter() + r.HandleFunc("/checkout", getCheckout).Methods("POST", "OPTIONS") + http.ListenAndServe(":6002", r) +} + +``` + +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 checkout --app-port 6002 --dapr-http-port 3602 --dapr-grpc-port 60002 go run CheckoutService.go +``` + +{{% /codetab %}} + +{{% codetab %}} ```javascript -const express = require('express') -const bodyParser = require('body-parser') -const app = express() -app.use(bodyParser.json()) +//dependencies +import { DaprServer, CommunicationProtocolEnum } from 'dapr-client'; -const port = 3000 +//code +const daprHost = "127.0.0.1"; +const serverHost = "127.0.0.1"; +const serverPort = "6002"; +const daprPort = "3602"; -app.post('/myevent', (req, res) => { - console.log(req.body) - res.status(200).send() -}) +start().catch((e) => { + console.error(e); + process.exit(1); +}); + +async function start() { + const server = new DaprServer(serverHost, serverPort, daprHost, daprPort, CommunicationProtocolEnum.HTTP); + await server.binding.receive('checkout', async (orderId) => console.log(`Received Message: ${JSON.stringify(orderId)}`)); + await server.startServer(); +} -app.listen(port, () => console.log(`Kafka consumer app listening on port ${port}!`)) ``` +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 checkout --app-port 6002 --dapr-http-port 3602 --dapr-grpc-port 60002 dotnet npm start +``` + +{{% /codetab %}} + +{{< /tabs >}} + ### ACK-ing an event In order to tell Dapr that you successfully processed an event in your application, return a `200 OK` response from your HTTP handler. -```javascript -res.status(200).send() -``` - ### Rejecting an event -In order to tell Dapr that the event wasn't processed correctly in your application and schedule it for redelivery, return any response different from `200 OK`. For example, a `500 Error`. - -```javascript -res.status(500).send() -``` +In order to tell Dapr that the event was not processed correctly in your application and schedule it for redelivery, return any response other than `200 OK`. For example, a `500 Error`. ### Specifying a custom route @@ -108,7 +300,6 @@ spec: ### Event delivery Guarantees Event delivery guarantees are controlled by the binding implementation. Depending on the binding implementation, the event delivery can be exactly once or at least once. - ## References * [Bindings building block]({{< ref bindings >}}) diff --git a/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-publish-subscribe.md b/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-publish-subscribe.md index ca2c4289d..5ace3cdb7 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-publish-subscribe.md +++ b/daprdocs/content/en/developing-applications/building-blocks/pubsub/howto-publish-subscribe.md @@ -20,33 +20,48 @@ When publishing a message, it's important to specify the content type of the dat Unless specified, Dapr will assume `text/plain`. When using Dapr's HTTP API, the content type can be set in a `Content-Type` header. gRPC clients and SDKs have a dedicated content type parameter. -## Step 1: Setup the Pub/Sub component -The following example creates applications to publish and subscribe to a topic called `deathStarStatus`. +## Example: - -

+The below code example loosely describes an application that processes orders. In the example, there are two services - an order processing service and a checkout service. Both services have Dapr sidecars. The order processing service uses Dapr to publish a message to RabbitMQ and the checkout service subscribes to the topic in the message queue. + +Diagram showing state management of example service + +## Step 1: Setup the Pub/Sub component +The following example creates applications to publish and subscribe to a topic called `orders`. The first step is to setup the Pub/Sub component: {{< tabs "Self-Hosted (CLI)" Kubernetes >}} {{% codetab %}} -Redis Streams is installed by default on a local machine when running `dapr init`. +The pubsub.yaml is created by default on your local machine when running `dapr init`. Verify by opening your components file under `%UserProfile%\.dapr\components\pubsub.yaml` on Windows or `~/.dapr/components/pubsub.yaml` on Linux/MacOS. + +In this example, RabbitMQ is used for publish and subscribe. Replace `pubsub.yaml` file contents with the below contents. -Verify by opening your components file under `%UserProfile%\.dapr\components\pubsub.yaml` on Windows or `~/.dapr/components/pubsub.yaml` on Linux/MacOS: ```yaml apiVersion: dapr.io/v1alpha1 kind: Component metadata: - name: pubsub + name: order_pub_sub spec: - type: pubsub.redis + type: pubsub.rabbitmq version: v1 metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword - value: "" + - name: host + value: "amqp://localhost:5672" + - name: durable + value: "false" + - name: deletedWhenUnused + value: "false" + - name: autoAck + value: "false" + - name: reconnectWait + value: "0" + - name: concurrency + value: parallel +scopes: + - orderprocessing + - checkout ``` You can override this file with another Redis instance or another [pubsub component]({{< ref setup-pubsub >}}) by creating a `components` directory containing the file and using the flag `--components-path` with the `dapr run` CLI command. @@ -59,16 +74,27 @@ To deploy this into a Kubernetes cluster, fill in the `metadata` connection deta apiVersion: dapr.io/v1alpha1 kind: Component metadata: - name: pubsub + name: order_pub_sub namespace: default spec: - type: pubsub.redis + type: pubsub.rabbitmq version: v1 metadata: - - name: redisHost - value: localhost:6379 - - name: redisPassword - value: "" + - name: host + value: "amqp://localhost:5672" + - name: durable + value: "false" + - name: deletedWhenUnused + value: "false" + - name: autoAck + value: "false" + - name: reconnectWait + value: "0" + - name: concurrency + value: parallel +scopes: + - orderprocessing + - checkout ``` {{% /codetab %}} @@ -95,41 +121,72 @@ You can subscribe to a topic using the following Custom Resources Definition (CR apiVersion: dapr.io/v1alpha1 kind: Subscription metadata: - name: myevent-subscription + name: order_pub_sub spec: - topic: deathStarStatus - route: /dsstatus - pubsubname: pubsub + topic: orders + route: /checkout + pubsubname: order_pub_sub scopes: -- app1 -- app2 +- orderprocessing +- checkout ``` -The example above shows an event subscription to topic `deathStarStatus`, for the pubsub component `pubsub`. -- The `route` field tells Dapr to send all topic messages to the `/dsstatus` endpoint in the app. -- The `scopes` field enables this subscription for apps with IDs `app1` and `app2`. +The example above shows an event subscription to topic `orders`, for the pubsub component `order_pub_sub`. +- The `route` field tells Dapr to send all topic messages to the `/checkout` endpoint in the app. +- The `scopes` field enables this subscription for apps with IDs `orderprocessing` and `checkout`. Set the component with: -{{< tabs "Self-Hosted (CLI)" Kubernetes>}} -{{% codetab %}} Place the CRD in your `./components` directory. When Dapr starts up, it loads subscriptions along with components. Note: By default, Dapr loads components from `$HOME/.dapr/components` on MacOS/Linux and `%USERPROFILE%\.dapr\components` on Windows. You can also override the default directory by pointing the Dapr CLI to a components path: +{{< tabs Dotnet Java Python Go Javascript Kubernetes>}} + +{{% codetab %}} + ```bash -dapr run --app-id myapp --components-path ./myComponents -- python3 app1.py +dapr run --app-id myapp --components-path ./myComponents -- dotnet run ``` -*Note: If you place the subscription in a custom components path, make sure the Pub/Sub component is present also.* +{{% /codetab %}} + +{{% codetab %}} + +```bash +dapr run --app-id myapp --components-path ./myComponents -- mvn spring-boot:run +``` + +{{% /codetab %}} + +{{% codetab %}} + +```bash +dapr run --app-id myapp --components-path ./myComponents -- python3 app.py +``` + +{{% /codetab %}} + +{{% codetab %}} + +```bash +dapr run --app-id myapp --components-path ./myComponents -- go run app.go +``` + +{{% /codetab %}} + +{{% codetab %}} + +```bash +dapr run --app-id myapp --components-path ./myComponents -- npm start +``` {{% /codetab %}} {{% codetab %}} In Kubernetes, save the CRD to a file and apply it to the cluster: - ```bash kubectl apply -f subscription.yaml ``` @@ -137,251 +194,233 @@ kubectl apply -f subscription.yaml {{< /tabs >}} -#### Example +Below are code examples that leverage Dapr SDKs to subscribe to a topic. -{{< tabs Python Node PHP>}} - -{{% codetab %}} -Create a file named `app1.py` and paste in the following: -```python -import flask -from flask import request, jsonify -from flask_cors import CORS -import json -import sys - -app = flask.Flask(__name__) -CORS(app) - -@app.route('/dsstatus', methods=['POST']) -def ds_subscriber(): - print(request.json, flush=True) - return json.dumps({'success':True}), 200, {'ContentType':'application/json'} - -app.run() -``` -After creating `app1.py` ensure flask and flask_cors are installed: - -```bash -pip install flask -pip install flask_cors -``` - -Then run: - -```bash -dapr --app-id app1 --app-port 5000 run python app1.py -``` -{{% /codetab %}} - -{{% codetab %}} -After setting up the subscription above, download this javascript (Node > 4.16) into a `app2.js` file: - -```javascript -const express = require('express') -const bodyParser = require('body-parser') -const app = express() -app.use(bodyParser.json({ type: 'application/*+json' })); - -const port = 3000 - -app.post('/dsstatus', (req, res) => { - console.log(req.body); - res.sendStatus(200); -}); - -app.listen(port, () => console.log(`consumer app listening on port ${port}!`)) -``` -Run this app with: - -```bash -dapr --app-id app2 --app-port 3000 run node app2.js -``` -{{% /codetab %}} +{{< tabs Dotnet Java Python Go Javascript>}} {{% codetab %}} -Create a file named `app1.php` and paste in the following: +```csharp +//dependencies +using System.Collections.Generic; +using System.Threading.Tasks; +using System; +using Microsoft.AspNetCore.Mvc; +using Dapr; +using Dapr.Client; -```php -post('/dsstatus', function( - #[\Dapr\Attributes\FromBody] - \Dapr\PubSub\CloudEvent $cloudEvent, - \Psr\Log\LoggerInterface $logger - ) { - $logger->alert('Received event: {event}', ['event' => $cloudEvent]); - return ['status' => 'SUCCESS']; - } -); -$app->start(); -``` - -After creating `app1.php`, and with the [SDK installed](https://docs.dapr.io/developing-applications/sdks/php/), -go ahead and start the app: - -```bash -dapr --app-id app1 --app-port 3000 run -- php -S 0.0.0.0:3000 app1.php -``` - -{{% /codetab %}} - -{{< /tabs >}} - -### Programmatic subscriptions - -To subscribe to topics, start a web server in the programming language of your choice and listen on the following `GET` endpoint: `/dapr/subscribe`. -The Dapr instance calls into your app at startup and expect a JSON response for the topic subscriptions with: -- `pubsubname`: Which pub/sub component Dapr should use. -- `topic`: Which topic to subscribe to. -- `route`: Which endpoint for Dapr to call on when a message comes to that topic. - -#### Example - -{{< tabs Python Node PHP>}} - -{{% codetab %}} -```python -import flask -from flask import request, jsonify -from flask_cors import CORS -import json -import sys - -app = flask.Flask(__name__) -CORS(app) - -@app.route('/dapr/subscribe', methods=['GET']) -def subscribe(): - subscriptions = [{'pubsubname': 'pubsub', - 'topic': 'deathStarStatus', - 'route': 'dsstatus'}] - return jsonify(subscriptions) - -@app.route('/dsstatus', methods=['POST']) -def ds_subscriber(): - print(request.json, flush=True) - return json.dumps({'success':True}), 200, {'ContentType':'application/json'} -app.run() -``` -After creating `app1.py` ensure flask and flask_cors are installed: - -```bash -pip install flask -pip install flask_cors -``` - -Then run: - -```bash -dapr --app-id app1 --app-port 5000 run python app1.py -``` -{{% /codetab %}} - -{{% codetab %}} -```javascript -const express = require('express') -const bodyParser = require('body-parser') -const app = express() -app.use(bodyParser.json({ type: 'application/*+json' })); - -const port = 3000 - -app.get('/dapr/subscribe', (req, res) => { - res.json([ +//code +namespace CheckoutService.controller +{ + [ApiController] + public class CheckoutServiceController : Controller + { + //Subscribe to a topic + [Topic("order_pub_sub", "orders")] + [HttpPost("checkout")] + public void getCheckout([FromBody] int orderId) { - pubsubname: "pubsub", - topic: "deathStarStatus", - route: "dsstatus" + Console.WriteLine("Subscriber received : " + orderId); } - ]); -}) - -app.post('/dsstatus', (req, res) => { - console.log(req.body); - res.sendStatus(200); -}); - -app.listen(port, () => console.log(`consumer app listening on port ${port}!`)) + } +} ``` -Run this app 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 app2 --app-port 3000 run node app2.js +dapr run --app-id checkout --app-port 6002 --dapr-http-port 3602 --dapr-grpc-port 60002 --app-ssl dotnet run ``` + {{% /codetab %}} {{% codetab %}} -Update `app1.php` with the following: +```java +//dependencies +import io.dapr.Topic; +import io.dapr.client.domain.CloudEvent; +import org.springframework.web.bind.annotation.*; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; -```php - $builder->addDefinitions(['dapr.subscriptions' => [ - new \Dapr\PubSub\Subscription(pubsubname: 'pubsub', topic: 'deathStarStatus', route: '/dsstatus'), -]])); -$app->post('/dsstatus', function( - #[\Dapr\Attributes\FromBody] - \Dapr\PubSub\CloudEvent $cloudEvent, - \Psr\Log\LoggerInterface $logger - ) { - $logger->alert('Received event: {event}', ['event' => $cloudEvent]); - return ['status' => 'SUCCESS']; + private static final Logger log = LoggerFactory.getLogger(CheckoutServiceController.class); + //Subscribe to a topic + @Topic(name = "orders", pubsubName = "order_pub_sub") + @PostMapping(path = "/checkout") + public Mono getCheckout(@RequestBody(required = false) CloudEvent cloudEvent) { + return Mono.fromRunnable(() -> { + try { + log.info("Subscriber received: " + cloudEvent.getData()); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); } -); -$app->start(); +} ``` -Run this app 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 app1 --app-port 3000 run -- php -S 0.0.0.0:3000 app1.php +dapr run --app-id checkout --app-port 6002 --dapr-http-port 3602 --dapr-grpc-port 60002 mvn spring-boot:run +``` + +{{% /codetab %}} + +{{% codetab %}} + +```python +#dependencies +from cloudevents.sdk.event import v1 +from dapr.ext.grpc import App +import logging +import json + +#code +app = App() +logging.basicConfig(level = logging.INFO) +#Subscribe to a topic +@app.subscribe(pubsub_name='order_pub_sub', topic='orders') +def mytopic(event: v1.Event) -> None: + data = json.loads(event.Data()) + logging.info('Subscriber received: ' + str(data)) + +app.run(6002) +``` + +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 checkout --app-port 6002 --dapr-http-port 3602 --app-protocol grpc -- python3 CheckoutService.py +``` + +{{% /codetab %}} + +{{% codetab %}} + +```go +//dependencies +import ( + "log" + "net/http" + "context" + + "github.com/dapr/go-sdk/service/common" + daprd "github.com/dapr/go-sdk/service/http" +) + +//code +var sub = &common.Subscription{ + PubsubName: "order_pub_sub", + Topic: "orders", + Route: "/checkout", +} + +func main() { + s := daprd.NewService(":6002") + //Subscribe to a topic + if err := s.AddTopicEventHandler(sub, eventHandler); err != nil { + log.Fatalf("error adding topic subscription: %v", err) + } + if err := s.Start(); err != nil && err != http.ErrServerClosed { + log.Fatalf("error listenning: %v", err) + } +} + +func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { + log.Printf("Subscriber received: %s", e.Data) + return false, nil +} +``` + +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 checkout --app-port 6002 --dapr-http-port 3602 --dapr-grpc-port 60002 go run CheckoutService.go +``` + +{{% /codetab %}} + +{{% codetab %}} + +```javascript +//dependencies +import { DaprServer, CommunicationProtocolEnum } from 'dapr-client'; + +//code +const daprHost = "127.0.0.1"; +const serverHost = "127.0.0.1"; +const serverPort = "6002"; + +start().catch((e) => { + console.error(e); + process.exit(1); +}); + +async function start(orderId) { + const server = new DaprServer( + serverHost, + serverPort, + daprHost, + process.env.DAPR_HTTP_PORT, + CommunicationProtocolEnum.HTTP + ); + //Subscribe to a topic + await server.pubsub.subscribe("order_pub_sub", "orders", async (orderId) => { + console.log(`Subscriber received: ${JSON.stringify(orderId)}`) + }); + await server.startServer(); +} +``` + +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 checkout --app-port 6002 --dapr-http-port 3602 --dapr-grpc-port 60002 npm start ``` {{% /codetab %}} {{< /tabs >}} -The `/dsstatus` endpoint matches the `route` defined in the subscriptions and this is where Dapr will send all topic messages to. +The `/checkout` endpoint matches the `route` defined in the subscriptions and this is where Dapr will send all topic messages to. ## Step 3: Publish a topic -To publish a topic you need to run an instance of a Dapr sidecar to use the pubsub Redis component. You can use the default Redis component installed into your local environment. - -Start an instance of Dapr with an app-id called `testpubsub`: +Start an instance of Dapr with an app-id called `orderprocessing`: ```bash -dapr run --app-id testpubsub --dapr-http-port 3500 +dapr run --app-id orderprocessing --dapr-http-port 3601 ``` {{< tabs "Dapr CLI" "HTTP API (Bash)" "HTTP API (PowerShell)">}} {{% codetab %}} -Then publish a message to the `deathStarStatus` topic: +Then publish a message to the `orders` topic: ```bash -dapr publish --publish-app-id testpubsub --pubsub pubsub --topic deathStarStatus --data '{"status": "completed"}' +dapr publish --publish-app-id orderprocessing --pubsub order_pub_sub --topic orders --data '{"orderId": "100"}' ``` {{% /codetab %}} {{% codetab %}} -Then publish a message to the `deathStarStatus` topic: +Then publish a message to the `orders` topic: ```bash -curl -X POST http://localhost:3500/v1.0/publish/pubsub/deathStarStatus -H "Content-Type: application/json" -d '{"status": "completed"}' +curl -X POST http://localhost:3601/v1.0/publish/order_pub_sub/orders -H "Content-Type: application/json" -d '{"orderId": "100"}' ``` {{% /codetab %}} {{% codetab %}} -Then publish a message to the `deathStarStatus` topic: +Then publish a message to the `orders` topic: ```powershell -Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"status": "completed"}' -Uri 'http://localhost:3500/v1.0/publish/pubsub/deathStarStatus' +Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"orderId": "100"}' -Uri 'http://localhost:3601/v1.0/publish/order_pub_sub/orders' ``` {{% /codetab %}} @@ -389,96 +428,240 @@ Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"status": Dapr automatically wraps the user payload in a Cloud Events v1.0 compliant envelope, using `Content-Type` header value for `datacontenttype` attribute. -## Step 4: ACK-ing a message +Below are code examples that leverage Dapr SDKs to publish a topic. -In order to tell Dapr that a message was processed successfully, return a `200 OK` response. If Dapr receives any other return status code than `200`, or if your app crashes, Dapr will attempt to redeliver the message following At-Least-Once semantics. - -#### Example - -{{< tabs Python Node>}} - -{{% codetab %}} -```python -@app.route('/dsstatus', methods=['POST']) -def ds_subscriber(): - print(request.json, flush=True) - return json.dumps({'success':True}), 200, {'ContentType':'application/json'} -``` -{{% /codetab %}} - -{{% codetab %}} -```javascript -app.post('/dsstatus', (req, res) => { - res.sendStatus(200); -}); -``` -{{% /codetab %}} - -{{< /tabs >}} - -{{% alert title="Note on message redelivery" color="primary" %}} -Some pubsub components (e.g. Redis) will redeliver a message if a response is not sent back within a specified time window. Make sure to configure metadata such as `processingTimeout` to customize this behavior. For more information refer to the respective [component references]({{< ref supported-pubsub >}}). -{{% /alert %}} - -## (Optional) Step 5: Publishing a topic with code - -{{< tabs Node PHP>}} - -{{% codetab %}} -If you prefer publishing a topic using code, here is an example. - -```javascript -const express = require('express'); -const path = require('path'); -const request = require('request'); -const bodyParser = require('body-parser'); - -const app = express(); -app.use(bodyParser.json()); - -const daprPort = process.env.DAPR_HTTP_PORT || 3500; -const daprUrl = `http://localhost:${daprPort}/v1.0`; -const port = 8080; -const pubsubName = 'pubsub'; - -app.post('/publish', (req, res) => { - console.log("Publishing: ", req.body); - const publishUrl = `${daprUrl}/publish/${pubsubName}/deathStarStatus`; - request( { uri: publishUrl, method: 'POST', json: req.body } ); - res.sendStatus(200); -}); - -app.listen(process.env.PORT || port, () => console.log(`Listening on port ${port}!`)); -``` -{{% /codetab %}} +{{< tabs Dotnet Java Python Go Javascript>}} {{% codetab %}} -If you prefer publishing a topic using code, here is an example. +```csharp +//dependencies +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Dapr.Client; +using Microsoft.AspNetCore.Mvc; +using System.Threading; -```php -run(function(\DI\FactoryInterface $factory, \Psr\Log\LoggerInterface $logger) { - $publisher = $factory->make(\Dapr\PubSub\Publish::class, ['pubsub' => 'pubsub']); - $publisher->topic('deathStarStatus')->publish('operational'); - $logger->alert('published!'); -}); +//code +namespace EventService +{ + class Program + { + static async Task Main(string[] args) + { + string PUBSUB_NAME = "order_pub_sub"; + string TOPIC_NAME = "orders"; + while(true) { + System.Threading.Thread.Sleep(5000); + Random random = new Random(); + int orderId = random.Next(1,1000); + CancellationTokenSource source = new CancellationTokenSource(); + CancellationToken cancellationToken = source.Token; + using var client = new DaprClientBuilder().Build(); + //Using Dapr SDK to publish a topic + await client.PublishEventAsync(PUBSUB_NAME, TOPIC_NAME, orderId, cancellationToken); + Console.WriteLine("Published data: " + orderId); + } + } + } +} ``` -You can save this to `app2.php` and while `app1` is running in another terminal, execute: +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 app2 run -- php app2.php +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 --app-ssl dotnet run +``` + +{{% /codetab %}} + +{{% codetab %}} + +```java +//dependencies +import io.dapr.client.DaprClient; +import io.dapr.client.DaprClientBuilder; +import io.dapr.client.domain.Metadata; +import static java.util.Collections.singletonMap; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +//code +@SpringBootApplication +public class OrderProcessingServiceApplication { + + private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); + + public static void main(String[] args) throws InterruptedException{ + String MESSAGE_TTL_IN_SECONDS = "1000"; + String TOPIC_NAME = "orders"; + String PUBSUB_NAME = "order_pub_sub"; + + while(true) { + TimeUnit.MILLISECONDS.sleep(5000); + Random random = new Random(); + int orderId = random.nextInt(1000-1) + 1; + DaprClient client = new DaprClientBuilder().build(); + //Using Dapr SDK to publish a topic + client.publishEvent( + PUBSUB_NAME, + TOPIC_NAME, + orderId, + singletonMap(Metadata.TTL_IN_SECONDS, MESSAGE_TTL_IN_SECONDS)).block(); + log.info("Published data:" + orderId); + } + } +} +``` + +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 +import random +from time import sleep +import requests +import logging +import json +from dapr.clients import DaprClient + +#code +logging.basicConfig(level = logging.INFO) +while True: + sleep(random.randrange(50, 5000) / 1000) + orderId = random.randint(1, 1000) + PUBSUB_NAME = 'order_pub_sub' + TOPIC_NAME = 'orders' + with DaprClient() as client: + #Using Dapr SDK to publish a topic + result = client.publish_event( + pubsub_name=PUBSUB_NAME, + topic_name=TOPIC_NAME, + data=json.dumps(orderId), + data_content_type='application/json', + ) + logging.info('Published data: ' + str(orderId)) +``` + +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 --app-protocol grpc python3 OrderProcessingService.py +``` + +{{% /codetab %}} + +{{% codetab %}} + +```go +//dependencies +import ( + "context" + "log" + "math/rand" + "time" + "strconv" + dapr "github.com/dapr/go-sdk/client" +) + +//code +var ( + PUBSUB_NAME = "order_pub_sub" + TOPIC_NAME = "orders" +) + +func main() { + for i := 0; i < 10; i++ { + time.Sleep(5000) + orderId := rand.Intn(1000-1) + 1 + client, err := dapr.NewClient() + if err != nil { + panic(err) + } + defer client.Close() + ctx := context.Background() + //Using Dapr SDK to publish a topic + if err := client.PublishEvent(ctx, PUBSUB_NAME, TOPIC_NAME, []byte(strconv.Itoa(orderId))); + err != nil { + panic(err) + } + + log.Println("Published data: " + strconv.Itoa(orderId)) + } +} +``` + +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 go run OrderProcessingService.go +``` + +{{% /codetab %}} + +{{% codetab %}} + +```javascript +//dependencies +import { DaprServer, DaprClient, CommunicationProtocolEnum } from 'dapr-client'; + +const daprHost = "127.0.0.1"; + +var main = function() { + for(var i=0;i<10;i++) { + sleep(5000); + var orderId = Math.floor(Math.random() * (1000 - 1) + 1); + start(orderId).catch((e) => { + console.error(e); + process.exit(1); + }); + } +} + +async function start(orderId) { + const PUBSUB_NAME = "order_pub_sub" + const TOPIC_NAME = "orders" + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + console.log("Published data:" + orderId) + //Using Dapr SDK to publish a topic + await client.pubsub.publish(PUBSUB_NAME, TOPIC_NAME, orderId); +} + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +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 %}} {{< /tabs >}} +## Step 4: ACK-ing a message + +In order to tell Dapr that a message was processed successfully, return a `200 OK` response. If Dapr receives any other return status code than `200`, or if your app crashes, Dapr will attempt to redeliver the message following at-least-once semantics. + ## Sending a custom CloudEvent Dapr automatically takes the data sent on the publish request and wraps it in a CloudEvent 1.0 envelope. @@ -491,23 +674,23 @@ Read about content types [here](#content-types), and about the [Cloud Events mes {{< tabs "Dapr CLI" "HTTP API (Bash)" "HTTP API (PowerShell)">}} {{% codetab %}} -Publish a custom CloudEvent to the `deathStarStatus` topic: +Publish a custom CloudEvent to the `orders` topic: ```bash -dapr publish --publish-app-id testpubsub --pubsub pubsub --topic deathStarStatus --data '{"specversion" : "1.0", "type" : "com.dapr.cloudevent.sent", "source" : "testcloudeventspubsub", "subject" : "Cloud Events Test", "id" : "someCloudEventId", "time" : "2021-08-02T09:00:00Z", "datacontenttype" : "application/cloudevents+json", "data" : {"status": "completed"}}' +dapr publish --publish-app-id orderprocessing --pubsub order_pub_sub --topic orders --data '{"specversion" : "1.0", "type" : "com.dapr.cloudevent.sent", "source" : "testcloudeventspubsub", "subject" : "Cloud Events Test", "id" : "someCloudEventId", "time" : "2021-08-02T09:00:00Z", "datacontenttype" : "application/cloudevents+json", "data" : {"orderId": "100"}}' ``` {{% /codetab %}} {{% codetab %}} -Publish a custom CloudEvent to the `deathStarStatus` topic: +Publish a custom CloudEvent to the `orders` topic: ```bash -curl -X POST http://localhost:3500/v1.0/publish/pubsub/deathStarStatus -H "Content-Type: application/cloudevents+json" -d '{"specversion" : "1.0", "type" : "com.dapr.cloudevent.sent", "source" : "testcloudeventspubsub", "subject" : "Cloud Events Test", "id" : "someCloudEventId", "time" : "2021-08-02T09:00:00Z", "datacontenttype" : "application/cloudevents+json", "data" : {"status": "completed"}}' +curl -X POST http://localhost:3601/v1.0/publish/order_pub_sub/orders -H "Content-Type: application/cloudevents+json" -d '{"specversion" : "1.0", "type" : "com.dapr.cloudevent.sent", "source" : "testcloudeventspubsub", "subject" : "Cloud Events Test", "id" : "someCloudEventId", "time" : "2021-08-02T09:00:00Z", "datacontenttype" : "application/cloudevents+json", "data" : {"orderId": "100"}}' ``` {{% /codetab %}} {{% codetab %}} -Publish a custom CloudEvent to the `deathStarStatus` topic: +Publish a custom CloudEvent to the `orders` topic: ```powershell -Invoke-RestMethod -Method Post -ContentType 'application/cloudevents+json' -Body '{"specversion" : "1.0", "type" : "com.dapr.cloudevent.sent", "source" : "testcloudeventspubsub", "subject" : "Cloud Events Test", "id" : "someCloudEventId", "time" : "2021-08-02T09:00:00Z", "datacontenttype" : "application/cloudevents+json", "data" : {"status": "completed"}}' -Uri 'http://localhost:3500/v1.0/publish/pubsub/deathStarStatus' +Invoke-RestMethod -Method Post -ContentType 'application/cloudevents+json' -Body '{"specversion" : "1.0", "type" : "com.dapr.cloudevent.sent", "source" : "testcloudeventspubsub", "subject" : "Cloud Events Test", "id" : "someCloudEventId", "time" : "2021-08-02T09:00:00Z", "datacontenttype" : "application/cloudevents+json", "data" : {"orderId": "100"}}' -Uri 'http://localhost:3601/v1.0/publish/order_pub_sub/orders' ``` {{% /codetab %}} @@ -521,4 +704,4 @@ Invoke-RestMethod -Method Post -ContentType 'application/cloudevents+json' -Body - Learn about [message time-to-live]({{< ref pubsub-message-ttl.md >}}) - Learn [how to configure Pub/Sub components with multiple namespaces]({{< ref pubsub-namespaces.md >}}) - List of [pub/sub components]({{< ref setup-pubsub >}}) -- Read the [API reference]({{< ref pubsub_api.md >}}) \ No newline at end of file +- Read the [API reference]({{< ref pubsub_api.md >}}) diff --git a/daprdocs/content/en/developing-applications/building-blocks/secrets/howto-secrets.md b/daprdocs/content/en/developing-applications/building-blocks/secrets/howto-secrets.md index 73565e055..4ac50cf55 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/secrets/howto-secrets.md +++ b/daprdocs/content/en/developing-applications/building-blocks/secrets/howto-secrets.md @@ -55,7 +55,6 @@ To configure a different kind of secret store see the guidance on [how to config Run the Dapr sidecar with the application. - {{< tabs Dotnet Java Python Go Javascript>}} {{% codetab %}} @@ -110,9 +109,16 @@ Once you have a secret store, call Dapr to get the secrets from your application {{% codetab %}} ```csharp - //dependencies +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; using Dapr.Client; +using Microsoft.AspNetCore.Mvc; +using System.Threading; +using System.Text.Json; //code namespace EventService @@ -126,54 +132,59 @@ namespace EventService //Using Dapr SDK to get a secret var secret = await client.GetSecretAsync(SECRET_STORE_NAME, "secret"); Console.WriteLine($"Result: {string.Join(", ", secret)}"); - Console.WriteLine($"Result for bulk: {string.Join(", ", secret.Keys)}"); } } } - ``` {{% /codetab %}} {{% codetab %}} ```java - //dependencies +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.Map; + //code @SpringBootApplication public class OrderProcessingServiceApplication { - private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); - private static final ObjectMapper JSON_SERIALIZER = new ObjectMapper(); + private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); + private static final ObjectMapper JSON_SERIALIZER = new ObjectMapper(); - private static final String SECRET_STORE_NAME = "localsecretstore"; + private static final String SECRET_STORE_NAME = "localsecretstore"; - public static void main(String[] args) throws InterruptedException, JsonProcessingException { - DaprClient client = new DaprClientBuilder().build(); + public static void main(String[] args) throws InterruptedException, JsonProcessingException { + DaprClient client = new DaprClientBuilder().build(); //Using Dapr SDK to get a secret - Map secret = client.getSecret(SECRET_STORE_NAME, "secret").block(); - log.info("Result: " + JSON_SERIALIZER.writeValueAsString(secret)); - } + Map secret = client.getSecret(SECRET_STORE_NAME, "secret").block(); + log.info("Result: " + JSON_SERIALIZER.writeValueAsString(secret)); + } } - ``` {{% /codetab %}} {{% codetab %}} ```python - #dependencies +import random +from time import sleep +import requests +import logging from dapr.clients import DaprClient from dapr.clients.grpc._state import StateItem from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType #code logging.basicConfig(level = logging.INFO) - DAPR_STORE_NAME = "localsecretstore" key = 'secret' @@ -182,21 +193,21 @@ with DaprClient() as client: secret = client.get_secret(store_name=DAPR_STORE_NAME, key=key) logging.info('Result: ') logging.info(secret.secret) + #Using Dapr SDK to get bulk secrets secret = client.get_bulk_secret(store_name=DAPR_STORE_NAME) logging.info('Result for bulk secret: ') logging.info(sorted(secret.secrets.items())) - ``` {{% /codetab %}} {{% codetab %}} ```go - //dependencies import ( "context" "log" + dapr "github.com/dapr/go-sdk/client" ) @@ -209,35 +220,26 @@ func main() { } defer client.Close() ctx := context.Background() - + //Using Dapr SDK to get a secret secret, err := client.GetSecret(ctx, SECRET_STORE_NAME, "secret", nil) - if err != nil { - return nil, errors.Wrap(err, "Got error for accessing key") - } - if secret != nil { log.Println("Result : ") log.Println(secret) } - - secretRandom, err := client.GetBulkSecret(ctx, SECRET_STORE_NAME, nil) - if err != nil { - return nil, errors.Wrap(err, "Got error for accessing key") - } + //Using Dapr SDK to get bulk secrets + secretBulk, err := client.GetBulkSecret(ctx, SECRET_STORE_NAME, nil) if secret != nil { log.Println("Result for bulk: ") - log.Println(secretRandom) + log.Println(secretBulk) } } - ``` {{% /codetab %}} {{% codetab %}} ```javascript - //dependencies import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; @@ -247,14 +249,15 @@ const daprHost = "127.0.0.1"; async function main() { const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); const SECRET_STORE_NAME = "localsecretstore"; + //Using Dapr SDK to get a secret var secret = await client.secret.get(SECRET_STORE_NAME, "secret"); console.log("Result: " + secret); + //Using Dapr SDK to get bulk secrets secret = await client.secret.getBulk(SECRET_STORE_NAME); console.log("Result for bulk: " + secret); } main(); - ``` {{% /codetab %}} @@ -267,4 +270,4 @@ main(); - [Configure a secret store]({{}}) - [Supported secrets]({{}}) - [Using secrets in components]({{}}) -- [Secret stores quickstart](https://github.com/dapr/quickstarts/tree/master/secretstore) \ No newline at end of file +- [Secret stores quickstart](https://github.com/dapr/quickstarts/tree/master/secretstore) 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 4f0cb2127..77dd0e386 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 @@ -183,9 +183,15 @@ Below are code examples that leverage Dapr SDKs for service invocation. {{% codetab %}} ```csharp - //dependencies +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; using Dapr.Client; +using Microsoft.AspNetCore.Mvc; +using System.Threading; //code namespace EventService @@ -194,113 +200,164 @@ namespace EventService { 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); + while(true) { + System.Threading.Thread.Sleep(5000); + Random random = new Random(); + int orderId = random.Next(1,1000); + CancellationTokenSource source = new CancellationTokenSource(); + CancellationToken cancellationToken = source.Token; + using var client = new DaprClientBuilder().Build(); + //Using Dapr SDK to invoke a method + var result = client.CreateInvokeMethodRequest(HttpMethod.Get, "checkout", "checkout/" + orderId, cancellationToken); + await client.InvokeMethodAsync(result); + Console.WriteLine("Order requested: " + orderId); + Console.WriteLine("Result: " + result); + } } } } - ``` {{% /codetab %}} {{% codetab %}} ```java - //dependencies import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; import io.dapr.client.domain.HttpExtension; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.Random; +import java.util.concurrent.TimeUnit; //code @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 - ); + + private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); + + public static void main(String[] args) throws InterruptedException{ + while(true) { + TimeUnit.MILLISECONDS.sleep(5000); + Random random = new Random(); + int orderId = random.nextInt(1000-1) + 1; + DaprClient daprClient = new DaprClientBuilder().build(); + //Using Dapr SDK to invoke a method + var result = daprClient.invokeMethod( + "checkout", + "checkout/" + orderId, + null, + HttpExtension.GET, + String.class + ); + log.info("Order requested: " + orderId); + log.info("Result: " + result); + } } } - ``` {{% /codetab %}} {{% codetab %}} ```python - #dependencies +import random +from time import sleep +import logging from dapr.clients import DaprClient #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" - ) - +logging.basicConfig(level = logging.INFO) +while True: + sleep(random.randrange(50, 5000) / 1000) + orderId = random.randint(1, 1000) + with DaprClient() as daprClient: + #Using Dapr SDK to invoke a method + result = daprClient.invoke_method( + "checkout", + f"checkout/{orderId}", + data=b'', + http_verb="GET" + ) + logging.basicConfig(level = logging.INFO) + logging.info('Order requested: ' + str(orderId)) + logging.info('Result: ' + str(result)) ``` {{% /codetab %}} {{% codetab %}} ```go - //dependencies import ( + "context" + "log" + "math/rand" + "time" "strconv" dapr "github.com/dapr/go-sdk/client" ) //code -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") +type Order struct { + orderName string + orderNum string } +func main() { + for i := 0; i < 10; i++ { + time.Sleep(5000) + orderId := rand.Intn(1000-1) + 1 + client, err := dapr.NewClient() + if err != nil { + panic(err) + } + defer client.Close() + ctx := context.Background() + //Using Dapr SDK to invoke a method + result, err := client.InvokeMethod(ctx, "checkout", "checkout/" + strconv.Itoa(orderId), "get") + log.Println("Order requested: " + strconv.Itoa(orderId)) + log.Println("Result: ") + log.Println(result) + } +} ``` {{% /codetab %}} {{% codetab %}} ```javascript - //dependencies import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; //code -const daprHost = "127.0.0.1"; +const daprHost = "127.0.0.1"; 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); + for(var i=0;i<10;i++) { + sleep(5000); + var orderId = Math.floor(Math.random() * (1000 - 1) + 1); + start(orderId).catch((e) => { + console.error(e); + process.exit(1); + }); + } +} + +async function start(orderId) { + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + //Using Dapr SDK to invoke a method + const result = await client.invoker.invoke('checkoutservice' , "checkout/" + orderId , HttpMethod.GET); + console.log("Order requested: " + orderId); + console.log("Result: " + result); +} + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); } main(); - ``` {{% /codetab %}} 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 1684b7ddb..e40eb7995 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 @@ -81,7 +81,15 @@ Below are code examples that leverage Dapr SDKs for saving and retrieving a sing ```csharp //dependencies +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; using Dapr.Client; +using Microsoft.AspNetCore.Mvc; +using System.Threading; +using System.Text.Json; //code namespace EventService @@ -91,17 +99,20 @@ namespace EventService 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()); + while(true) { + System.Threading.Thread.Sleep(5000); + using var client = new DaprClientBuilder().Build(); + Random random = new Random(); + int orderId = random.Next(1,1000); + //Using Dapr SDK to save and get state + 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()); + Console.WriteLine("Result after get: " + result); + } } } } - ``` Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: @@ -116,12 +127,17 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```java - //dependencies import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; import io.dapr.client.domain.State; +import io.dapr.client.domain.TransactionalStateOperation; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import reactor.core.publisher.Mono; +import java.util.Random; +import java.util.concurrent.TimeUnit; //code @SpringBootApplication @@ -129,18 +145,23 @@ 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"; + private static final 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); + public static void main(String[] args) throws InterruptedException{ + while(true) { + TimeUnit.MILLISECONDS.sleep(5000); + Random random = new Random(); + int orderId = random.nextInt(1000-1) + 1; + DaprClient client = new DaprClientBuilder().build(); + //Using Dapr SDK to save and get state + 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); + log.info("Result after get" + result); + } } -} +} ``` Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: @@ -155,22 +176,26 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```python - #dependencies +import random +from time import sleep +import requests +import logging from dapr.clients import DaprClient +from dapr.clients.grpc._state import StateItem +from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType #code logging.basicConfig(level = logging.INFO) - DAPR_STORE_NAME = "statestore" - -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)) - +while True: + sleep(random.randrange(50, 5000) / 1000) + orderId = random.randint(1, 1000) + with DaprClient() as client: + #Using Dapr SDK to save and get state + client.save_state(DAPR_STORE_NAME, "order_1", str(orderId)) + result = client.get_state(DAPR_STORE_NAME, "order_1") + logging.info('Result after get: ' + result.data.decode('utf-8')) ``` Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: @@ -185,7 +210,6 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```go - //dependencies import ( "context" @@ -194,34 +218,32 @@ import ( "time" "strconv" dapr "github.com/dapr/go-sdk/client" - ) //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) - } + for i := 0; i < 10; i++ { + time.Sleep(5000) + orderId := rand.Intn(1000-1) + 1 + client, err := dapr.NewClient() + STATE_STORE_NAME := "statestore" + if err != nil { + panic(err) + } + defer client.Close() + ctx := context.Background() + //Using Dapr SDK to save and get state + 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_2") + if err != nil { + panic(err) + } + log.Println("Result after get: ") + log.Println(result) + } } - ``` Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: @@ -236,19 +258,26 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% 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"; + for(var i=0;i<10;i++) { + sleep(5000); + var orderId = Math.floor(Math.random() * (1000 - 1) + 1); + start(orderId).catch((e) => { + console.error(e); + process.exit(1); + }); + } +} - var orderId = 100; - //Using Dapr SDK to save and get state +async function start(orderId) { const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + const STATE_STORE_NAME = "statestore"; + //Using Dapr SDK to save and get state await client.state.save(STATE_STORE_NAME, [ { key: "order_1", @@ -260,10 +289,14 @@ var main = function() { } ]); var result = await client.state.get(STATE_STORE_NAME, "order_1"); + console.log("Result after get: " + result); +} + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); } main(); - ``` Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: @@ -329,7 +362,6 @@ Below are code examples that leverage Dapr SDKs for deleting the state. {{% codetab %}} ```csharp - //dependencies using Dapr.Client; @@ -347,7 +379,6 @@ namespace EventService } } } - ``` Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: @@ -362,10 +393,10 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```java - //dependencies import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; +import org.springframework.boot.autoconfigure.SpringBootApplication; //code @SpringBootApplication @@ -379,7 +410,6 @@ public class OrderProcessingServiceApplication { 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: @@ -394,19 +424,16 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```python - #dependencies from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType #code logging.basicConfig(level = logging.INFO) - DAPR_STORE_NAME = "statestore" #Using Dapr SDK to delete the state with DaprClient() as client: client.delete_state(store_name=DAPR_STORE_NAME, key="order_1") - ``` Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: @@ -421,7 +448,6 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```go - //dependencies import ( "context" @@ -431,9 +457,7 @@ import ( //code func main() { - STATE_STORE_NAME := "statestore" - //Using Dapr SDK to delete the state client, err := dapr.NewClient() if err != nil { @@ -446,7 +470,6 @@ func main() { panic(err) } } - ``` Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: @@ -461,23 +484,19 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% 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"; - //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(); - ``` Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: @@ -515,11 +534,11 @@ Below are code examples that leverage Dapr SDKs for saving and retrieving multip {{% codetab %}} ```java - //dependencies import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; import io.dapr.client.domain.State; +import java.util.Arrays; //code @SpringBootApplication @@ -529,15 +548,12 @@ public class OrderProcessingServiceApplication { 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: @@ -548,27 +564,22 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% /codetab %}} - {{% codetab %}} ```python - #dependencies from dapr.clients import DaprClient from dapr.clients.grpc._state import StateItem #code logging.basicConfig(level = logging.INFO) - DAPR_STORE_NAME = "statestore" - 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)) - ``` Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: @@ -579,21 +590,16 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% /codetab %}} - - {{% 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); @@ -611,7 +617,6 @@ var main = function() { } main(); - ``` Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: @@ -662,9 +667,16 @@ Below are code examples that leverage Dapr SDKs for performing state transaction {{% codetab %}} ```csharp - //dependencies +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; using Dapr.Client; +using Microsoft.AspNetCore.Mvc; +using System.Threading; +using System.Text.Json; //code namespace EventService @@ -674,22 +686,26 @@ namespace EventService 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); + while(true) { + System.Threading.Thread.Sleep(5000); + Random random = new Random(); + int orderId = random.Next(1,1000); + 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; + //Using Dapr SDK to perform the state transactions + await client.ExecuteStateTransactionAsync(DAPR_STORE_NAME, requests, cancellationToken: cancellationToken); + Console.WriteLine("Order requested: " + orderId); + Console.WriteLine("Result: " + result); + } } } } - ``` Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: @@ -704,13 +720,19 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```java - //dependencies import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; import io.dapr.client.domain.State; import io.dapr.client.domain.TransactionalStateOperation; - +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.TimeUnit; //code @SpringBootApplication @@ -718,21 +740,26 @@ public class OrderProcessingServiceApplication { private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); + private static final String STATE_STORE_NAME = "statestore"; + 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(); + while(true) { + TimeUnit.MILLISECONDS.sleep(5000); + Random random = new Random(); + int orderId = random.nextInt(1000-1) + 1; + 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"))); + //Using Dapr SDK to perform the state transactions + client.executeStateTransaction(STATE_STORE_NAME, operationList).block(); + log.info("Order requested: " + orderId); + } } -} +} ``` Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: @@ -743,37 +770,42 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% /codetab %}} - {{% codetab %}} - ```python - #dependencies +import random +from time import sleep +import requests +import logging from dapr.clients import DaprClient from dapr.clients.grpc._state import StateItem from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType #code -logging.basicConfig(level = logging.INFO) - +logging.basicConfig(level = logging.INFO) DAPR_STORE_NAME = "statestore" +while True: + sleep(random.randrange(50, 5000) / 1000) + orderId = random.randint(1, 1000) + with DaprClient() as client: + #Using Dapr SDK to perform the state transactions + 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)) + ]) -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)) - ]) - + client.delete_state(store_name=DAPR_STORE_NAME, key="order_1") + logging.basicConfig(level = logging.INFO) + logging.info('Order requested: ' + str(orderId)) + logging.info('Result: ' + str(result)) ``` Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: @@ -788,38 +820,48 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% 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"; + for(var i=0;i<10;i++) { + sleep(5000); + var orderId = Math.floor(Math.random() * (1000 - 1) + 1); + start(orderId).catch((e) => { + console.error(e); + process.exit(1); + }); + } +} - var orderId = 100; - //Using Dapr SDK to save and retrieve multiple states +async function start(orderId) { const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + const STATE_STORE_NAME = "statestore"; + //Using Dapr SDK to save and retrieve multiple states await client.state.transaction(STATE_STORE_NAME, [ { - operation: "upsert", - request: { - key: "order_3", - value: orderId.toString() - } + operation: "upsert", + request: { + key: "order_3", + value: orderId.toString() + } }, { - operation: "delete", - request: { - key: "order_2" - } + operation: "delete", + request: { + key: "order_2" + } } ]); } -main(); +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} +main(); ``` Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: diff --git a/daprdocs/content/en/developing-applications/integrations/autoscale-keda.md b/daprdocs/content/en/developing-applications/integrations/autoscale-keda.md index e6fd74149..7e685010f 100644 --- a/daprdocs/content/en/developing-applications/integrations/autoscale-keda.md +++ b/daprdocs/content/en/developing-applications/integrations/autoscale-keda.md @@ -6,7 +6,7 @@ description: "How to configure your Dapr application to autoscale using KEDA" weight: 2000 --- -Dapr, with its modular building-block approach, along with the 10+ different [pub/sub components]({{< ref pubsub >}}), make it easy to write message processing applications. Since Dapr can run in many environments (e.g. VM, bare-metal, Cloud, or Edge) the autoscaling of Dapr applications is managed by the hosting later. +Dapr, with its modular building-block approach, along with the 10+ different [pub/sub components]({{< ref pubsub >}}), make it easy to write message processing applications. Since Dapr can run in many environments (e.g. VM, bare-metal, Cloud, or Edge) the autoscaling of Dapr applications is managed by the hosting layer. For Kubernetes, Dapr integrates with [KEDA](https://github.com/kedacore/keda), an event driven autoscaler for Kubernetes. Many of Dapr's pub/sub components overlap with the scalers provided by [KEDA](https://github.com/kedacore/keda) so it's easy to configure your Dapr deployment on Kubernetes to autoscale based on the back pressure using KEDA. diff --git a/daprdocs/content/en/getting-started/quickstarts.md b/daprdocs/content/en/getting-started/quickstarts.md index bfb892117..de6a3313c 100644 --- a/daprdocs/content/en/getting-started/quickstarts.md +++ b/daprdocs/content/en/getting-started/quickstarts.md @@ -6,7 +6,7 @@ weight: 60 description: "Tutorials with code samples that are aimed to get you started quickly with Dapr" --- -The [Dapr Quickstarts](https://github.com/dapr/quickstarts/tree/v1.0.0) are a collection of tutorials with code samples that are aimed to get you started quickly with Dapr, each highlighting a different Dapr capability. +The [Dapr Quickstarts](https://github.com/dapr/quickstarts/tree/v1.5.0) are a collection of tutorials with code samples that are aimed to get you started quickly with Dapr, each highlighting a different Dapr capability. - A good place to start is the hello-world quickstart, it demonstrates how to run Dapr in standalone mode locally on your machine and demonstrates state management and service invocation in a simple application. - Next, if you are familiar with Kubernetes and want to see how to run the same application in a Kubernetes environment, look for the hello-kubernetes quickstart. Other quickstarts such as pub-sub, bindings and the distributed-calculator quickstart explore different Dapr capabilities include instructions for running both locally and on Kubernetes and can be completed in any order. A full list of the quickstarts can be found below. @@ -17,12 +17,12 @@ The [Dapr Quickstarts](https://github.com/dapr/quickstarts/tree/v1.0.0) are a co | Quickstart | Description | |--------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [Hello World](https://github.com/dapr/quickstarts/tree/v1.4.0/hello-world) | Demonstrates how to run Dapr locally. Highlights service invocation and state management. | -| [Hello Kubernetes](https://github.com/dapr/quickstarts/tree/v1.4.0/hello-kubernetes) | Demonstrates how to run Dapr in Kubernetes. Highlights service invocation and state management. | -| [Distributed Calculator](https://github.com/dapr/quickstarts/tree/v1.4.0/distributed-calculator) | Demonstrates a distributed calculator application that uses Dapr services to power a React web app. Highlights polyglot (multi-language) programming, service invocation and state management. | -| [Pub/Sub](https://github.com/dapr/quickstarts/tree/v1.4.0/pub-sub) | Demonstrates how to use Dapr to enable pub-sub applications. Uses Redis as a pub-sub component. | -| [Bindings](https://github.com/dapr/quickstarts/tree/v1.4.0/bindings) | Demonstrates how to use Dapr to create input and output bindings to other components. Uses bindings to Kafka. | -| [Middleware](https://github.com/dapr/quickstarts/tree/v1.4.0/middleware) | Demonstrates use of Dapr middleware to enable OAuth 2.0 authorization. | -| [Observability](https://github.com/dapr/quickstarts/tree/v1.4.0/observability) | Demonstrates Dapr tracing capabilities. Uses Zipkin as a tracing component. | -| [Secret Store](https://github.com/dapr/quickstarts/tree/v1.4.0/secretstore) | Demonstrates the use of Dapr Secrets API to access secret stores. | +| [Hello World](https://github.com/dapr/quickstarts/tree/v1.5.0/hello-world) | Demonstrates how to run Dapr locally. Highlights service invocation and state management. | +| [Hello Kubernetes](https://github.com/dapr/quickstarts/tree/v1.5.0/hello-kubernetes) | Demonstrates how to run Dapr in Kubernetes. Highlights service invocation and state management. | +| [Distributed Calculator](https://github.com/dapr/quickstarts/tree/v1.5.0/distributed-calculator) | Demonstrates a distributed calculator application that uses Dapr services to power a React web app. Highlights polyglot (multi-language) programming, service invocation and state management. | +| [Pub/Sub](https://github.com/dapr/quickstarts/tree/v1.5.0/pub-sub) | Demonstrates how to use Dapr to enable pub-sub applications. Uses Redis as a pub-sub component. | +| [Bindings](https://github.com/dapr/quickstarts/tree/v1.5.0/bindings) | Demonstrates how to use Dapr to create input and output bindings to other components. Uses bindings to Kafka. | +| [Middleware](https://github.com/dapr/quickstarts/tree/v1.5.0/middleware) | Demonstrates use of Dapr middleware to enable OAuth 2.0 authorization. | +| [Observability](https://github.com/dapr/quickstarts/tree/v1.5.0/observability) | Demonstrates Dapr tracing capabilities. Uses Zipkin as a tracing component. | +| [Secret Store](https://github.com/dapr/quickstarts/tree/v1.5.0/secretstore) | Demonstrates the use of Dapr Secrets API to access secret stores. | diff --git a/daprdocs/content/en/operations/monitoring/tracing/supported-tracing-backends/jaeger.md b/daprdocs/content/en/operations/monitoring/tracing/supported-tracing-backends/jaeger.md index 44a453649..e4e6b4903 100644 --- a/daprdocs/content/en/operations/monitoring/tracing/supported-tracing-backends/jaeger.md +++ b/daprdocs/content/en/operations/monitoring/tracing/supported-tracing-backends/jaeger.md @@ -83,7 +83,7 @@ spec: #### Production -Jaeger uses Elasticsearch as the backend storage, and you can create a secret in k8s cluster to access Elasticsearch server with access control. See [Configuring and Deploying Jaeger](https://docs.openshift.com/container-platform/4.7/jaeger/jaeger_install/rhbjaeger-deploying.html) +Jaeger uses Elasticsearch as the backend storage, and you can create a secret in k8s cluster to access Elasticsearch server with access control. See [Configuring and Deploying Jaeger](https://docs.openshift.com/container-platform/4.9/distr_tracing/distr_tracing_install/distr-tracing-deploying.html) ```shell kubectl create secret generic jaeger-secret --from-literal=ES_PASSWORD='xxx' --from-literal=ES_USERNAME='xxx' -n ${NAMESPACE} diff --git a/daprdocs/content/en/operations/security/oauth.md b/daprdocs/content/en/operations/security/oauth.md index 5fedd524d..ef663cbff 100644 --- a/daprdocs/content/en/operations/security/oauth.md +++ b/daprdocs/content/en/operations/security/oauth.md @@ -34,12 +34,14 @@ To figure the Dapr OAuth middleware, you'll need to collect the following inform Authorization/Token URLs of some of the popular authorization servers: + | Server | Authorization URL | Token URL | |---------|-------------------|-----------| |Azure AAD||| |GitHub||| |Google|| | |Twitter||| + ## Define the middleware component definition diff --git a/daprdocs/content/en/operations/support/support-release-policy.md b/daprdocs/content/en/operations/support/support-release-policy.md index 318036117..6bd27c761 100644 --- a/daprdocs/content/en/operations/support/support-release-policy.md +++ b/daprdocs/content/en/operations/support/support-release-policy.md @@ -45,7 +45,9 @@ The table below shows the versions of Dapr releases that have been tested togeth | Sep 22nd 2021 | 1.4.1
| 1.4.0 | Java 1.3.0
Go 1.2.0
PHP 1.1.0
Python 1.3.0
.NET 1.4.0 | 0.8.0 | Supported | Sep 24th 2021 | 1.4.2
| 1.4.0 | Java 1.3.0
Go 1.2.0
PHP 1.1.0
Python 1.3.0
.NET 1.4.0 | 0.8.0 | Supported | | Oct 7th 2021 | 1.4.3
| 1.4.0 | Java 1.3.0
Go 1.2.0
PHP 1.1.0
Python 1.3.0
.NET 1.4.0 | 0.8.0 | Supported | -| Nov 11th 2021 | 1.5.0
| 1.5.0 | Java 1.3.0
Go 1.3.0
PHP 1.1.0
Python 1.4.0
.NET 1.5.0
JS 1.0.2 | 0.9.0 | Supported (current) | +| Dev 6th 2021 | 1.4.4
| 1.4.0 | Java 1.3.0
Go 1.2.0
PHP 1.1.0
Python 1.3.0
.NET 1.4.0 | 0.8.0 | Supported | +| Nov 11th 2021 | 1.5.0
| 1.5.0 | Java 1.3.0
Go 1.3.0
PHP 1.1.0
Python 1.4.0
.NET 1.5.0
JS 1.0.2 | 0.9.0 | Supported (current) | +| Dec 6th 2021 | 1.5.1
| 1.5.1 | Java 1.3.0
Go 1.3.0
PHP 1.1.0
Python 1.4.0
.NET 1.5.0
JS 1.0.2 | 0.9.0 | Supported (current) | ## Upgrade paths After the 1.0 release of the runtime there may be situations where it is necessary to explicitly upgrade through an additional release to reach the desired target. For example an upgrade from v1.0 to v1.2 may need go pass through v1.1 @@ -59,23 +61,22 @@ General guidance on upgrading can be found for [self hosted mode]({{}}) +* [How to setup logging in Dapr]({{< ref "logging.md" >}}) diff --git a/daprdocs/content/en/reference/api/service_invocation_api.md b/daprdocs/content/en/reference/api/service_invocation_api.md index 94888067b..15de82351 100644 --- a/daprdocs/content/en/reference/api/service_invocation_api.md +++ b/daprdocs/content/en/reference/api/service_invocation_api.md @@ -16,7 +16,7 @@ This endpoint lets you invoke a method in another Dapr enabled app. ### HTTP Request ``` -POST/GET/PUT/DELETE http://localhost:/v1.0/invoke//method/ +PATCH/POST/GET/PUT/DELETE http://localhost:/v1.0/invoke//method/ ``` ### HTTP Response codes diff --git a/daprdocs/content/en/reference/components-reference/supported-bindings/s3.md b/daprdocs/content/en/reference/components-reference/supported-bindings/s3.md index b48aa37d2..68754b50a 100644 --- a/daprdocs/content/en/reference/components-reference/supported-bindings/s3.md +++ b/daprdocs/content/en/reference/components-reference/supported-bindings/s3.md @@ -27,6 +27,8 @@ spec: value: mybucket - name: region value: us-west-2 + - name: endpoint + value: s3-us-west-2.amazonaws.com - name: accessKey value: ***************** - name: secretKey @@ -37,6 +39,8 @@ spec: value: - name: encodeBase64 value: + - name: forcePathStyle + value: ``` {{% alert title="Warning" color="warning" %}} @@ -49,9 +53,11 @@ The above example uses secrets as plain strings. It is recommended to use a secr |--------------------|:--------:|------------|-----|---------| | bucket | Y | Output | The name of the S3 bucket to write to | `"bucket"` | | region | Y | Output | The specific AWS region | `"us-east-1"` | +| endpoint | N | Output | The specific AWS endpoint | `"s3-us-east-1.amazonaws.com"` | | accessKey | Y | Output | The AWS Access Key to access this resource | `"key"` | | secretKey | Y | Output | The AWS Secret Access Key to access this resource | `"secretAccessKey"` | | sessionToken | N | Output | The AWS session token to use | `"sessionToken"` | +| forcePathStyle | N | Output | Currently Amazon S3 SDK supports virtual hosted-style and path-style access. `true` is path-style format like `https:////`. `false` is hosted-style format like `https://./`. Defaults to `false` | `true`, `false` | | decodeBase64 | N | Output | Configuration to decode base64 file content before saving to bucket storage. (In case of saving a file with binary content). `true` is the only allowed positive value. Other positive variations like `"True", "1"` are not acceptable. Defaults to `false` | `true`, `false` | | encodeBase64 | N | Output | Configuration to encode base64 file content before return the content. (In case of opening a file with binary content). `true` is the only allowed positive value. Other positive variations like `"True", "1"` are not acceptable. Defaults to `false` | `true`, `false` | @@ -136,6 +142,8 @@ spec: value: mybucket - name: region value: us-west-2 + - name: endpoint + value: s3-us-west-2.amazonaws.com - name: accessKey value: ***************** - name: secretKey @@ -144,6 +152,8 @@ spec: value: mysession - name: decodeBase64 value: + - name: forcePathStyle + value: ``` Then you can upload it as you would normally: diff --git a/daprdocs/layouts/shortcodes/dapr-latest-version.html b/daprdocs/layouts/shortcodes/dapr-latest-version.html index 60da16a51..77638b68f 100644 --- a/daprdocs/layouts/shortcodes/dapr-latest-version.html +++ b/daprdocs/layouts/shortcodes/dapr-latest-version.html @@ -1 +1 @@ -{{- if .Get "short" }}1.5{{ else if .Get "long" }}1.5.0{{ else if .Get "cli" }}1.5.0{{ else }}1.5.0{{ end -}} +{{- if .Get "short" }}1.5{{ else if .Get "long" }}1.5.1{{ else if .Get "cli" }}1.5.1{{ else }}1.5.1{{ end -}} diff --git a/daprdocs/static/images/building-block-input-binding-example.png b/daprdocs/static/images/building-block-input-binding-example.png new file mode 100644 index 000000000..408c37378 Binary files /dev/null and b/daprdocs/static/images/building-block-input-binding-example.png differ diff --git a/daprdocs/static/images/building-block-output-binding-example.png b/daprdocs/static/images/building-block-output-binding-example.png new file mode 100644 index 000000000..ac114b4bd Binary files /dev/null and b/daprdocs/static/images/building-block-output-binding-example.png differ diff --git a/daprdocs/static/images/building-block-pub-sub-example.png b/daprdocs/static/images/building-block-pub-sub-example.png new file mode 100644 index 000000000..4ffe87ce4 Binary files /dev/null and b/daprdocs/static/images/building-block-pub-sub-example.png differ diff --git a/sdkdocs/dotnet b/sdkdocs/dotnet index fadb6c265..d60eaf1c0 160000 --- a/sdkdocs/dotnet +++ b/sdkdocs/dotnet @@ -1 +1 @@ -Subproject commit fadb6c2654255864438c387a1a2b862eaf3af240 +Subproject commit d60eaf1c0e3bb75af480eda34307926be9865570