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..fa4308921 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(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,38 +67,228 @@ 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 for saving and retrieving a single state. + +{{< tabs Dotnet Java Python Go Javascript>}} + +{{% codetab %}} + +```csharp +//dependencies +using Dapr.Client; + +//code +namespace EventService +{ + class Program + { + static async Task Main(string[] args) + { + string BINDING_NAME = "checkout"; + string BINDING_OPERATION = "create"; + int orderId = 100; + 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; + +//code +@SpringBootApplication +public class OrderProcessingServiceApplication { + + public static void main(String[] args) throws InterruptedException{ + String BINDING_NAME = "checkout"; + String BINDING_OPERATION = "create"; + int orderId = 100; + 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 +from dapr.clients import DaprClient + +#code +BINDING_NAME = 'checkout' +BINDING_OPERATION = 'create' + +orderId = 100 +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 --dapr-grpc-port 60001 -- python3 OrderProcessingService.py +``` + +{{% /codetab %}} + +{{% codetab %}} + +```go +//dependencies +import ( + "context" + "strconv" + dapr "github.com/dapr/go-sdk/client" + +) + +//code +func main() { + BINDING_NAME := "checkout"; + BINDING_OPERATION := "create"; + orderId := 100 + 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() { + var orderId = 100; + 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); +} + +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 >}}) 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..5cf5f1b22 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,79 +18,265 @@ 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(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 output binding represents a resource that Dapr uses to invoke and send messages to. -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 the different binding specs [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 for 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 reactor.core.publisher.Mono; + +//code +@RestController +@RequestMapping("/") +public class CheckoutServiceController { + @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 +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 --dapr-grpc-port 60002 -- python3 CheckoutService.py +``` + +{{% /codetab %}} + +{{% codetab %}} + +```go +//dependencies +import ( + "encoding/json" + "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() -``` - ### Specifying a custom route By default, incoming events will be sent to an HTTP endpoint that corresponds to the name of the input binding. @@ -108,7 +294,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/static/images/building-block-bindings-example.png b/daprdocs/static/images/building-block-bindings-example.png new file mode 100644 index 000000000..4d2f92cc8 Binary files /dev/null and b/daprdocs/static/images/building-block-bindings-example.png differ