Skip to content

Commit 3e08667

Browse files
author
Carlos Santana
committed
Add sample for cloudevents-nodejs
Signed-off-by: Carlos Santana <[email protected]>
1 parent a3af009 commit 3e08667

File tree

8 files changed

+3320
-0
lines changed

8 files changed

+3320
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Dockerfile
2+
README.md
3+
node_modules/
4+
npm-debug.log
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules/
2+
npm-debug.log*
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
FROM registry.access.redhat.com/ubi8/nodejs-12
2+
3+
# Copy application dependency manifests to the container image.
4+
# A wildcard is used to ensure both package.json AND package-lock.json are copied.
5+
# Copying this separately prevents re-running npm install on every code change.
6+
COPY package*.json ./
7+
8+
# Use ci is faster and more reliable following package-lock.json
9+
RUN npm ci --only=production
10+
11+
# Doc port listening port
12+
ENV PORT 8080
13+
14+
EXPOSE $PORT
15+
16+
ARG ENV=production
17+
18+
ENV NODE_ENV $ENV
19+
20+
# Run the web service on container startup.
21+
CMD npm run $NODE_ENV
22+
23+
# Copy local code to the container image.
24+
COPY . ./
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
---
2+
title: "Cloud Events - Node.js"
3+
linkTitle: "Node.js"
4+
weight: 1
5+
type: "docs"
6+
---
7+
8+
A simple web app written in Node.js that can receive and send Cloud Events that you
9+
can use for testing. It supports running in two modes:
10+
11+
1. The default mode has the app reply to your input events with the output
12+
event, which is simplest for demonstrating things working in isolation, but
13+
is also the model for working for the Knative Eventing `Broker` concept.
14+
15+
2. `K_SINK` mode has the app send events to the destination encoded in
16+
`$K_SINK`, which is useful to demonstrate how folks can synthesize events to
17+
send to a Service or Broker when not initiated by a Broker invocation (e.g.
18+
implementing an event source)
19+
20+
The application will use `$K_SINK`-mode whenever the environment variable is
21+
specified.
22+
23+
Follow the steps below to create the sample code and then deploy the app to your
24+
cluster. You can also download a working copy of the sample, by running the
25+
following commands:
26+
27+
```shell
28+
git clone -b "{{< branch >}}" https://github.com/knative/docs knative-docs
29+
cd knative-docs/docs/serving/samples/cloudevents/cloudevents-nodejs
30+
```
31+
32+
## Before you begin
33+
34+
- A Kubernetes cluster with Knative installed and DNS configured. Follow the
35+
[installation instructions](../../../../install/README.md) if you need to
36+
create one.
37+
- [Docker](https://www.docker.com) installed and running on your local machine,
38+
and a Docker Hub account configured (we'll use it for a container registry).
39+
40+
## The sample code.
41+
42+
1. If you look in `index.js`, you will see two key functions for the
43+
different modes of operation:
44+
45+
```js
46+
const receiveAndSend = (cloudEvent, res) => {
47+
// This is called whenever an event is received if $K_SINK is set, and sends a new event
48+
// to the url in $K_SINK.
49+
}
50+
51+
const receiveAndReply = (cloudEvent, res) => {
52+
// This is called whenever an event is received if $K_SINK is NOT set, and it replies with
53+
// the new event instead.
54+
}
55+
```
56+
57+
1. If you look in `Dockerfile`, you will see how the dependencies are installed using npm.
58+
You can build and push this to your registry of choice via:
59+
60+
```shell
61+
docker build -t <image> .
62+
docker push <image>
63+
```
64+
65+
1. If you look in `service.yaml`, take the `<image>` name above and insert it
66+
into the `image:`.
67+
68+
```shell
69+
kubectl apply -f service.yaml
70+
```
71+
72+
## Testing the sample
73+
74+
Get the URL for your Service with:
75+
76+
```shell
77+
$ kubectl get ksvc
78+
NAME URL LATESTCREATED LATESTREADY READY REASON
79+
cloudevents-nodejs http://cloudevents-nodejs.default.1.2.3.4.xip.io cloudevents-nodejs-ss5pj cloudevents-nodejs-ss5pj True
80+
```
81+
82+
Then send a cloud event to it with:
83+
84+
```shell
85+
$ curl -X POST \
86+
-H "content-type: application/json" \
87+
-H "ce-specversion: 1.0" \
88+
-H "ce-source: curl-command" \
89+
-H "ce-type: curl.demo" \
90+
-H "ce-id: 123-abc" \
91+
-d '{"name":"Dave"}' \
92+
http://cloudevents-nodejs.default.1.2.3.4.xip.io
93+
```
94+
95+
You will get back:
96+
97+
```shell
98+
{"message":"Hello, Dave"}
99+
```
100+
101+
## Removing the sample app deployment
102+
103+
To remove the sample app from your cluster, delete the service record:
104+
105+
```shell
106+
kubectl delete --filename service.yaml
107+
```
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
const express = require('express')
2+
const { CloudEvent, HTTPReceiver } = require('cloudevents-sdk')
3+
const HTTPBinary = require('cloudevents-sdk/lib/bindings/http/emitter_binary_1.js')
4+
const PORT = process.env.PORT || 8080
5+
const target = process.env.K_SINK
6+
const app = express()
7+
const receiver = new HTTPReceiver()
8+
9+
const main = () => {
10+
app.listen(PORT, function () {
11+
console.log(`Cookie monster is hungry for some cloudevents on port ${PORT}!`)
12+
const modeMessage = target ? `send cloudevents to K_SINK: ${target}` : 'reply back with cloudevents'
13+
console.log(`Cookie monster is going to ${modeMessage}`)
14+
})
15+
}
16+
17+
// handle shared the logic for producing the Response event from the Request.
18+
const handle = (data) => {
19+
return { message: `Hello, ${data.name ? data.name : 'nameless'}` }
20+
}
21+
22+
// receiveAndSend is invoked whenever we receive an event.
23+
const receiveAndSend = (cloudEvent, res) => {
24+
const newCloudEvent = new CloudEvent()
25+
.type('dev.knative.docs.sample')
26+
.source('https://github.com/knative/docs/docs/serving/samples/cloudevents/cloudevents-nodejs')
27+
.time(new Date())
28+
29+
const body = handle(cloudEvent.getData())
30+
newCloudEvent.data(body)
31+
const emitter = new HTTPBinary({
32+
method: 'POST',
33+
url: target
34+
}).emitter
35+
res.status(201).end()
36+
emitter.emit(newCloudEvent).then((res) => {
37+
console.log(`Sent event: ${JSON.stringify(newCloudEvent.format(), null, 2)}`)
38+
console.log(`K_SINK responded: ${JSON.stringify({ status: res.status, headers: res.headers, data: res.data }, null, 2)}`)
39+
})
40+
}
41+
42+
// receiveAndReply is invoked whenever we receive an event.
43+
const receiveAndReply = (cloudEvent, res) => {
44+
const newCloudEvent = new CloudEvent()
45+
.type('dev.knative.docs.sample')
46+
.source('https://github.com/knative/docs/docs/serving/samples/cloudevents/cloudevents-nodejs')
47+
.time(new Date())
48+
49+
const body = handle(cloudEvent.getData())
50+
newCloudEvent.data(body)
51+
console.log(`Reply event: ${JSON.stringify(newCloudEvent.format(), null, 2)}`)
52+
res.set(getHeaders(newCloudEvent))
53+
res.status(200).send(body)
54+
}
55+
56+
const getHeaders = (cloudEvent) => {
57+
/* TODO: This function should be provided by sdk */
58+
const emitter = new HTTPBinary().emitter
59+
const headers = {}
60+
Object.keys(emitter.headerByGetter)
61+
.filter((getter) => cloudEvent[getter]())
62+
.forEach((getter) => {
63+
const header = emitter.headerByGetter[getter]
64+
headers[header.name] =
65+
header.parser(
66+
cloudEvent[getter]()
67+
)
68+
})
69+
return headers
70+
}
71+
72+
app.use((req, res, next) => {
73+
let data = ''
74+
req.setEncoding('utf8')
75+
req.on('data', function (chunk) {
76+
data += chunk
77+
})
78+
req.on('end', function () {
79+
req.body = data
80+
next()
81+
})
82+
})
83+
84+
app.post('/', function (req, res) {
85+
try {
86+
const event = receiver.accept(req.headers, req.body)
87+
console.log(`Accepted event: ${JSON.stringify(event.format(), null, 2)}`)
88+
target ? receiveAndSend(event, res) : receiveAndReply(event, res)
89+
} catch (err) {
90+
console.error(err)
91+
res.status(415)
92+
.header('Content-Type', 'application/json')
93+
.send(JSON.stringify(err))
94+
}
95+
})
96+
97+
main()

0 commit comments

Comments
 (0)