Skip to content

Commit 276b810

Browse files
authored
BREAKING CHANGE(lib): rewrite in TypeScript (#226)
This is a major rewrite of the entire codebase into TypeScript. Nearly all tests have been retained except where behavior is significantly different. Some highlights of these changes: * lowercase all CloudEvent properties and fix base64 encoded data Previously there was a format() function that would convert a CloudEvent object into JSON with all of the properties lowercased. With this rewrite a CloudEvent object can be converted to JSON simply with JSON.stringify(). However, in order to be compliant with the JSON representation outlined in the spec here https://github.com/cloudevents/spec/blob/v1.0/json-format.md all of the event properties must be all lowercase. * lib(transport): make transport mode an Enum * src: allow custom headers (#1) * lib(exports): export explicitly versioned names where appropriate * lib(cloudevent): modify ctor to accept extensions inline * lib(cloudevent): make extensions a part of the event object * test: convert all tests to typescript * examples: update all examples with latest API changes * docs: update README with latest API changes * src: add prettier for code style and fix a lot of linting errors * lib: move data decoding to occur within the CloudEvent object Signed-off-by: Lance Ball <lball@redhat.com>
1 parent 060b21b commit 276b810

File tree

104 files changed

+4570
-5864
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+4570
-5864
lines changed

.eslintrc

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
{
2-
"extends": "eslint:recommended",
2+
"parser": "@typescript-eslint/parser",
3+
"parserOptions": {
4+
"ecmaVersion": 2015,
5+
"sourceType": "module"
6+
},
7+
"extends": [
8+
"plugin:@typescript-eslint/recommended",
9+
"prettier/@typescript-eslint",
10+
"plugin:prettier/recommended"
11+
],
312
"env": {
413
"es6": true,
514
"node": true,
615
"mocha": true
716
},
817
"rules": {
918
"no-var": "error",
10-
"space-before-function-paren": ["error", "never"],
1119
"standard/no-callback-literal": "off",
1220
"arrow-spacing": "error",
1321
"arrow-parens": ["error", "always"],
@@ -20,7 +28,7 @@
2028
"no-console": ["error", {
2129
"allow": ["warn", "error"]
2230
}],
23-
"valid-jsdoc": "error",
31+
"valid-jsdoc": "warn",
2432
"semi": ["error", "always"],
2533
"quotes": ["error", "double", { "allowTemplateLiterals": true }]
2634
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ index.js
1111
/lib
1212
/browser
1313
/bundles
14+
/dist
1415

1516
# Runtime data
1617
pids

.prettierrc.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = {
2+
semi: true,
3+
trailingComma: "all",
4+
doubleQuote: true,
5+
printWidth: 120,
6+
tabWidth: 2
7+
}

README.md

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,73 +36,70 @@ binary and structured events in either the 1.0 or 0.3 protocol formats.
3636
```js
3737
const {
3838
CloudEvent,
39-
HTTPReceiver
39+
Receiver
4040
} = require("cloudevents-sdk");
4141

4242
// Create a receiver to accept events over HTTP
43-
const receiver = new HTTPReceiver();
43+
const receiver = new Receiver();
4444

4545
// body and headers come from an incoming HTTP request, e.g. express.js
4646
const receivedEvent = receiver.accept(req.headers, req.body);
47-
console.log(receivedEvent.format());
47+
console.log(receivedEvent);
4848
```
4949

5050
#### Emitting Events
5151

52-
To emit events, you'll need to decide whether the event should be sent in
53-
binary or structured format, and determine what version of the CloudEvents
54-
specification you want to send the event as.
52+
You can send events over HTTP in either binary or structured format.
5553

56-
By default, the `HTTPEmitter` will emit events over HTTP POST using the
57-
latest supported specification version, in binary mode. You can emit version specific events by providing
58-
the specication version in the constructor to `HTTPEmitter`. To send
59-
structured events, add that string as a parameter to `emitter.send()`.
54+
By default, the `Emitter` will emit events over HTTP POST using the
55+
binary transport protocol. The `Emitter` will examine the `specversion`
56+
of the event being sent, and use the appropriate protocol version. To send
57+
structured events, add `Protocol.HTTPStructured` as a parameter to
58+
`emitter.send()`.
6059

6160
```js
62-
const { CloudEvent, HTTPEmitter } = require("cloudevents-sdk");
61+
const { CloudEvent, Emitter, Protocol, Version } = require("cloudevents-sdk");
6362

6463
// With only an endpoint URL, this creates a v1 emitter
65-
const v1Emitter = new HTTPEmitter({
64+
const emitter = new Emitter({
6665
url: "https://cloudevents.io/example"
6766
});
6867
const event = new CloudEvent({
6968
type, source, data
7069
});
7170

7271
// By default, the emitter will send binary events
73-
v1Emitter.send(event).then((response) => {
72+
emitter.send(event).then((response) => {
7473
// handle the response
7574
}).catch(console.error);
7675

7776
// To send a structured event, just add that as an option
78-
v1Emitter.send(event, { mode: "structured" })
77+
emitter.send(event, { protocol: Protocol.HTTPStructured })
7978
.then((response) => {
8079
// handle the response
8180
}).catch(console.error);
8281

8382
// To send an event to an alternate URL, add that as an option
84-
v1Emitter.send(event, { url: "https://alternate.com/api" })
83+
emitter.send(event, { url: "https://alternate.com/api" })
8584
.then((response) => {
8685
// handle the response
8786
}).catch(console.error);
8887

89-
// Sending a v0.3 event works the same, just let the emitter know when
90-
// you create it that you are working with the 0.3 spec
91-
const v03Emitter = new HTTPEmitter({
92-
url: "https://cloudevents.io/example",
93-
version: "0.3"
94-
});
95-
96-
// Again, the default is to send binary events
97-
// To send a structured event or to an alternate URL, provide those
98-
// as parameters in a options object as above
99-
v3Emitter.send(event)
88+
// Sending a v0.3 event works the same, If your event has a
89+
// specversion property of Version.V03, then it will be sent
90+
// using the 0.3 transport protocol
91+
emitter.send(new CloudEvent({ specversion: Version.V03, source, type }))
10092
.then((response) => {
10193
// handle the response
10294
}).catch(console.error);
103-
10495
```
10596

97+
### Example Applications
98+
99+
There are a few trivial example applications in
100+
[the examples folder](https://github.com/cloudevents/sdk-javascript/tree/master/examples).
101+
There you will find Express.js, TypeScript and Websocket examples.
102+
106103
## Supported specification features
107104

108105
| | [v0.3](https://github.com/cloudevents/spec/tree/v0.3) | [v1.0](https://github.com/cloudevents/spec/tree/v1.0) |

examples/express-ex/index.js

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,39 @@
11
/* eslint-disable no-console */
22

33
const express = require("express");
4-
const { HTTPReceiver } = require("cloudevents-sdk");
4+
const { Receiver } = require("cloudevents-sdk");
55

66
const app = express();
7-
const receiver = new HTTPReceiver();
7+
const receiver = new Receiver();
88

99
app.use((req, res, next) => {
1010
let data = "";
1111

1212
req.setEncoding("utf8");
13-
req.on("data", function(chunk) {
13+
req.on("data", function (chunk) {
1414
data += chunk;
1515
});
1616

17-
req.on("end", function() {
17+
req.on("end", function () {
1818
req.body = data;
1919
next();
2020
});
2121
});
2222

23-
app.post("/", function(req, res) {
24-
console.log(req.headers);
25-
console.log(req.body);
23+
app.post("/", function (req, res) {
24+
console.log("HEADERS", req.headers);
25+
console.log("BODY", req.body);
2626

2727
try {
2828
const event = receiver.accept(req.headers, req.body);
29-
const asJSON = event.format();
30-
console.log(`Accepted event: ${JSON.stringify(event.format(), null, 2)}`);
31-
res.status(201).json(asJSON);
29+
console.log(`Accepted event: ${event}`);
30+
res.status(201).json(event);
3231
} catch (err) {
3332
console.error(err);
34-
res.status(415)
35-
.header("Content-Type", "application/json")
36-
.send(JSON.stringify(err));
33+
res.status(415).header("Content-Type", "application/json").send(JSON.stringify(err));
3734
}
3835
});
3936

40-
app.listen(3000, function() {
37+
app.listen(3000, function () {
4138
console.log("Example app listening on port 3000!");
4239
});

examples/typescript-ex/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"check": "gts check",
1717
"clean": "gts clean",
1818
"compile": "tsc -p .",
19+
"watch": "tsc -p . --watch",
1920
"fix": "gts fix",
2021
"prepare": "npm run compile",
2122
"pretest": "npm run compile",

examples/typescript-ex/src/index.ts

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,33 @@
1-
import { CloudEvent, HTTPReceiver } from "cloudevents-sdk";
2-
import { CloudEventV1 } from "cloudevents-sdk/lib/v1";
1+
import { CloudEvent, CloudEventV1, Receiver } from "cloudevents-sdk";
32

43
export function doSomeStuff() {
5-
const receiver = new HTTPReceiver();
4+
const receiver = new Receiver();
65

76
const myevent: CloudEventV1 = new CloudEvent({
87
source: "/source",
98
type: "type",
10-
dataContentType: "text/plain",
11-
dataSchema: "https://d.schema.com/my.json",
9+
datacontenttype: "text/plain",
10+
dataschema: "https://d.schema.com/my.json",
1211
subject: "cha.json",
13-
data: "my-data"
12+
data: "my-data",
1413
});
15-
myevent.addExtension("extension-1", "some extension data");
14+
myevent.extension1 = "some extension data";
1615

17-
console.log("My structured event:", myevent.toString());
18-
console.log("My structured event extensions:", myevent.getExtensions());
16+
console.log("My structured event:", myevent);
1917

2018
// ------ receiver structured
2119
// The header names should be standarized to use lowercase
2220
const headers = {
23-
"content-type": "application/cloudevents+json"
21+
"content-type": "application/cloudevents+json",
2422
};
2523

2624
// Typically used with an incoming HTTP request where myevent.format() is the actual
2725
// body of the HTTP
28-
console.log("Received structured event:", receiver.accept(headers, myevent.format()).toString());
26+
console.log("Received structured event:", receiver.accept(headers, myevent));
2927

3028
// ------ receiver binary
3129
const data = {
32-
"data": "dataString"
30+
data: "dataString",
3331
};
3432
const attributes = {
3533
"ce-type": "type",
@@ -39,11 +37,11 @@ export function doSomeStuff() {
3937
"ce-time": "2019-06-16T11:42:00Z",
4038
"ce-dataschema": "http://schema.registry/v1",
4139
"Content-Type": "application/json",
42-
"ce-extension1": "extension1"
40+
"ce-extension1": "extension1",
4341
};
4442

45-
console.log("My binary event:", receiver.accept(attributes, data).toString());
46-
console.log("My binary event extensions:", receiver.accept(attributes, data).toString());
43+
console.log("My binary event:", receiver.accept(attributes, data));
44+
console.log("My binary event extensions:", receiver.accept(attributes, data));
4745

4846
return true;
4947
}

examples/websocket/client.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const { CloudEvent } = require("cloudevents-sdk");
77

88
const rl = readline.createInterface({
99
input: process.stdin,
10-
output: process.stdout
10+
output: process.stdout,
1111
});
1212

1313
rl.on("close", (_) => console.log("\n\nConnection closed! Press CTL-C to exit."));
@@ -25,16 +25,16 @@ ws.on("message", function incoming(message) {
2525
function ask() {
2626
rl.question("Would you like to see the current weather? Provide a zip code: ", function (zip) {
2727
console.log("Fetching weather data from server...");
28-
ws.send(new CloudEvent({
28+
const event = new CloudEvent({
2929
type: "weather.query",
3030
source: "/weather.client",
31-
data: zip
32-
}).toString());
31+
data: { zip },
32+
});
33+
ws.send(event.toString());
3334
});
3435
}
3536

3637
function print(data) {
37-
data = JSON.parse(data);
3838
console.log(`
3939
Current weather for ${data.name}: ${data.weather[0].main}
4040
------------------------------------------

examples/websocket/index.html

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
<html>
33
<head>
44
<title>CloudEvent Example</title>
5-
<script src="../../bundles/cloudevents-sdk.js"></script>
5+
<script src="../../_bundles/cloudevents-sdk.js"></script>
66
<script>
77
const CloudEvent = window['cloudevents-sdk'].CloudEvent;
8+
const Version = window['cloudevents-sdk'].Version;
89
const socket = new WebSocket("ws://localhost:8080");
910

1011
function print(weather) {
11-
const data = JSON.parse(weather);
12+
const data = weather;
1213
const summary = `
1314
<h2>Current weather for ${data.name}: ${data.weather[0].main}</h2>
1415
<hr/>
@@ -23,7 +24,7 @@ <h2>Current weather for ${data.name}: ${data.weather[0].main}</h2>
2324

2425
function initialize() {
2526
socket.onmessage = function(message) {
26-
console.log(message.data)
27+
console.log(message.data);
2728
const event = new CloudEvent(JSON.parse(message.data));
2829
if (event.type === "weather.error") {
2930
console.error(`Error: ${event.data}`);
@@ -37,11 +38,13 @@ <h2>Current weather for ${data.name}: ${data.weather[0].main}</h2>
3738
input.addEventListener("keyup", function(event) {
3839
if (event.keyCode === 13) {
3940
event.preventDefault();
40-
socket.send(new CloudEvent({
41+
const ce = new CloudEvent({
4142
type: "weather.query",
4243
source: "/weather.client",
43-
data: input.value
44-
}).toString());
44+
data: { zip: input.value }
45+
});
46+
console.log(ce);
47+
socket.send(JSON.stringify(ce));
4548
}
4649
});
4750

examples/websocket/server.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,29 @@ console.log("WebSocket server started. Waiting for events.");
1313
wss.on("connection", function connection(ws) {
1414
console.log("Connection received");
1515
ws.on("message", function incoming(message) {
16+
console.log(`Message received: ${message}`);
1617
const event = new CloudEvent(JSON.parse(message));
17-
console.log(`Message received: ${event.toString()}`);
18-
fetch(event.data)
18+
fetch(event.data.zip)
1919
.then((weather) => {
20-
ws.send(new CloudEvent({
20+
const response = new CloudEvent({
2121
dataContentType: "application/json",
2222
type: "current.weather",
2323
source: "/weather.server",
24-
data: weather
25-
}).toString());
24+
data: weather,
25+
});
26+
ws.send(JSON.stringify(response));
2627
})
2728
.catch((err) => {
2829
console.error(err);
29-
ws.send(new CloudEvent({
30-
type: "weather.error",
31-
source: "/weather.server",
32-
data: err.toString()
33-
}).toString());
30+
ws.send(
31+
JSON.stringify(
32+
new CloudEvent({
33+
type: "weather.error",
34+
source: "/weather.server",
35+
data: err.toString(),
36+
}),
37+
),
38+
);
3439
});
3540
});
3641
});
@@ -39,7 +44,7 @@ function fetch(zip) {
3944
const query = `${api}?zip=${zip}&appid=${key}&units=imperial`;
4045
return new Promise((resolve, reject) => {
4146
got(query)
42-
.then((response) => resolve(response.body))
47+
.then((response) => resolve(JSON.parse(response.body)))
4348
.catch((err) => reject(err.message));
4449
});
4550
}

0 commit comments

Comments
 (0)