Skip to content

Commit 5b35414

Browse files
authored
Adding pubsub sample (#82)
1 parent f38ce9f commit 5b35414

File tree

12 files changed

+271
-0
lines changed

12 files changed

+271
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ If you are new to Dapr, you may want to review following resources first:
2727
| [Dapr integration in Azure APIM](./dapr-apim-integration) | Dapr configuration in Azure API Management service using self-hosted gateway on Kubernetes. Illustrates exposing Dapr API for service method invocation, publishing content to a Pub/Sub topic, and binding invocation with request content transformation. |
2828
| [Distributed Calendar](./dapr-distributed-calendar) | Shows use of statestore, pubsub and output binding features of Dapr to roughly create a distributed version of a MVCS architecture application. |
2929
| [Hello Service Fabric](./hello-service-fabric) | Shows use of statestore, pubsub and service invocation in a Service Fabric environment running the Dapr sidecar as a guest executable. |
30+
| [Pub-sub routing](./pub-sub-routing) | Demonstrates how to use Dapr to enable pub-sub applications with message routing. |
3031

3132
## External samples
3233

pub-sub-routing/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

pub-sub-routing/Dockerfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
FROM node:8-alpine
2+
WORKDIR /usr/src/app
3+
COPY . .
4+
RUN npm install
5+
EXPOSE 3000
6+
CMD [ "node", "app.js" ]

pub-sub-routing/README.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Dapr Pub-Sub routing
2+
3+
In this quickstart, you'll run a subscriber application that makes use of Pub-Sub routing. The application can be quickly changed to:
4+
5+
* Toggle between programmatic and declarative subscriptions (or use both as long as there is no overlap)
6+
* Add other routing rules with different [CEL](https://github.com/google/cel-spec) expressions
7+
* Enable/disable routing (disable `PubSub.Routing` in `config.yaml`)
8+
9+
> **Note**: Because this example is intended to be something to play around with, it makes use of the `in-memory` pubsub component and is not intended to be deployed to Kubernetes.
10+
11+
**Install dependencies**
12+
13+
<!-- STEP
14+
name: Install node dependencies
15+
working_dir: .
16+
-->
17+
18+
```bash
19+
npm install
20+
```
21+
22+
<!-- END_STEP -->
23+
24+
**Run the application**
25+
26+
<!-- STEP
27+
name: Run node subscriber
28+
expected_stdout_lines:
29+
- "WIDGET:"
30+
- "GADGET:"
31+
- "PRODUCT (default):"
32+
expected_stderr_lines:
33+
output_match_mode: substring
34+
working_dir: .
35+
background: true
36+
sleep: 5
37+
-->
38+
39+
```bash
40+
dapr run --app-id pubsub-routing --config config.yaml --components-path ./components --app-port 3000 node app.js
41+
```
42+
43+
<!-- END_STEP -->
44+
45+
**Switch between programmatic and declarative subscriptions**
46+
47+
In `app.js`, the handler for `/dapr/subscribe` returns the programmatic subscriptions. The contents are commented out to demonstrate usage of a declarative subscription in `components/subscription.yaml`.
48+
49+
You can switch from declarative to programmatic subscriptions by:
50+
51+
1) Changing `kind: Subscription` to `kind: Subscription_disabled` in `components/subscription.yaml` so that `daprd` does not load the file
52+
2) Uncommenting the JSON response for `/dapr/subscribe` in `app.js`
53+
3) Stop (CTRL-C) and restart the application using the `dapr run` command above
54+
55+
**Publish messages to route**
56+
57+
Try the following `curl` commands in a separate terminal.
58+
59+
Publish a widget
60+
61+
<!-- STEP
62+
name: Curl publish message
63+
expected_stdout_lines:
64+
- "OK"
65+
- "OK"
66+
- "OK"
67+
expected_stderr_lines:
68+
-->
69+
70+
```bash
71+
curl -s http://localhost:3000/publish -H Content-Type:application/json --data @messages/widget.json
72+
```
73+
74+
Publish a gadget
75+
76+
```bash
77+
curl -s http://localhost:3000/publish -H Content-Type:application/json --data @messages/gadget.json
78+
```
79+
80+
Publish a thingamajig
81+
82+
```bash
83+
curl -s http://localhost:3000/publish -H Content-Type:application/json --data @messages/thingamajig.json
84+
```
85+
86+
<!-- END_STEP -->
87+
88+
<!-- STEP
89+
expected_stdout_lines:
90+
- 'app stopped successfully: pubsub-routing'
91+
expected_stderr_lines:
92+
output_match_mode: substring
93+
name: Shutdown dapr
94+
-->
95+
96+
```bash
97+
dapr stop --app-id pubsub-routing
98+
```
99+
100+
<!-- END_STEP -->

pub-sub-routing/app.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// ------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation.
3+
// Licensed under the MIT License.
4+
// ------------------------------------------------------------
5+
6+
const daprPort = process.env.DAPR_HTTP_PORT || "3500";
7+
8+
// The Dapr endpoint for the state store component to store the tweets.
9+
const pubsubEndpoint = `http://localhost:${daprPort}/v1.0/publish/pubsub/inventory`;
10+
11+
const express = require('express');
12+
const axios = require('axios');
13+
14+
const app = express();
15+
// Dapr publishes messages with the application/cloudevents+json content-type
16+
app.use(express.json({ type: ['application/json', 'application/*+json'] }));
17+
18+
const port = 3000;
19+
20+
app.get('/dapr/subscribe', (_req, res) => {
21+
// Programmatic subscriptions
22+
// See ./components/subscription.yaml for declarative subscription.
23+
//
24+
res.json([
25+
// Previous subscription structure.
26+
// {
27+
// pubsubname: "pubsub",
28+
// topic: "inventory",
29+
// route: "products"
30+
// }
31+
//
32+
// Subscription with routing rules and default route.
33+
// {
34+
// pubsubname: "pubsub",
35+
// topic: "inventory",
36+
// routes: {
37+
// rules: [
38+
// {
39+
// match: `event.type == "widget"`,
40+
// path: "widgets"
41+
// },
42+
// {
43+
// match: `event.type == "gadget"`,
44+
// path: "gadgets"
45+
// }
46+
// ],
47+
// default: "products"
48+
// }
49+
// }
50+
]);
51+
});
52+
53+
// Default product handler.
54+
app.post('/products', (req, res) => {
55+
console.log("🤔 PRODUCT (default): ", req.body);
56+
console.log();
57+
res.sendStatus(200);
58+
});
59+
60+
// Specific handler for widgets.
61+
app.post('/widgets', (req, res) => {
62+
console.log("🪛 WIDGET: ", req.body);
63+
console.log();
64+
res.sendStatus(200);
65+
});
66+
67+
// Specific handler for gadgets.
68+
app.post('/gadgets', (req, res) => {
69+
console.log("📱 GADGET: ", req.body);
70+
console.log();
71+
res.sendStatus(200);
72+
});
73+
74+
// Allow publishing freeform cloud events to the topic.
75+
app.post('/publish', async (req, res) => {
76+
console.log("publishing", req.body);
77+
console.log();
78+
axios.post(pubsubEndpoint, req.body, {
79+
headers: {
80+
'content-type': 'application/cloudevents+json'
81+
}
82+
})
83+
.then(() => { res.sendStatus(200); })
84+
.catch(error => {
85+
res.sendStatus(500);
86+
console.error('There was an error!', error);
87+
});
88+
});
89+
90+
app.listen(port, () => console.log(`Node App listening on port ${port}!`));
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: dapr.io/v1alpha1
2+
kind: Component
3+
metadata:
4+
name: pubsub
5+
namespace: default
6+
spec:
7+
type: pubsub.in-memory
8+
version: v1
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
apiVersion: dapr.io/v2alpha1
2+
kind: Subscription
3+
metadata:
4+
name: mysubscriptions
5+
namespace: default
6+
spec:
7+
pubsubname: pubsub
8+
topic: inventory
9+
routes:
10+
rules:
11+
- match: "event.type == 'widget'"
12+
path: widgets
13+
- match: "event.type == 'gadget'"
14+
path: gadgets
15+
default: products

pub-sub-routing/config.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: dapr.io/v1alpha1
2+
kind: Configuration
3+
metadata:
4+
name: pubsubroutingconfig
5+
spec:
6+
features:
7+
- name: PubSub.Routing
8+
enabled: true

pub-sub-routing/messages/gadget.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "gadget",
3+
"source": "routing-demo",
4+
"data": {
5+
"description": "Hello, Gadget!",
6+
"price": 75.00,
7+
"gadgetField": "gadgets only"
8+
}
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"type": "thingamajig",
3+
"source": "routing-demo",
4+
"data": {
5+
"description": "Hello, Thingamajig!",
6+
"price": 5.00
7+
}
8+
}

pub-sub-routing/messages/widget.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "widget",
3+
"source": "routing-demo",
4+
"data": {
5+
"description": "Hello, Widget!",
6+
"price": 25.00,
7+
"widgetField": "widgets only"
8+
}
9+
}

pub-sub-routing/package.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "node-pubsub-routing",
3+
"version": "1.0.0",
4+
"private": true,
5+
"description": "A routing subscriber sample",
6+
"main": "app.js",
7+
"scripts": {
8+
"test": "echo \"Error: no test specified\" && exit 1"
9+
},
10+
"author": "Phil Kedy",
11+
"license": "ISC",
12+
"dependencies": {
13+
"axios": "^0.21.4",
14+
"express": "^4.17.1"
15+
}
16+
}

0 commit comments

Comments
 (0)