Skip to content

Commit 250a0a1

Browse files
authored
feat!: expose a version agnostic event emitter (#141)
* feat!: expose a version agnostic event emitter This is a breaking change. This commit exposes an HTTP based event emitter that simplifes the API. To use it, simply import the SDK and start emitting. The default spec version is 1.0, but you can use 0.3 by supplying that to the constructor. By default, CloudEvents are emitted in binary mode, but this can be changed by providing the "structured" parameter to the `send()` function. This commit also eliminates the version specific emitters and receivers from the `v1` and `v03` exports, and eliminates the explicit usage of versioned emitters from `lib/bindings/http`. Finally, the CE headers can be retrieved from the emitter for a given event by passing the event to the `headers()` function. Fixes: #124 Fixes: #149 Signed-off-by: Lance Ball <[email protected]>
1 parent 7665969 commit 250a0a1

15 files changed

+589
-323
lines changed

README.md

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,25 +62,60 @@ console.log(receivedEvent.format());
6262

6363
#### Emitting Events
6464

65-
Currently, to emit events, you'll need to decide whether the event is in
65+
To emit events, you'll need to decide whether the event should be sent in
6666
binary or structured format, and determine what version of the CloudEvents
6767
specification you want to send the event as.
6868

69-
```js
70-
const { CloudEvent } = require("cloudevents-sdk");
71-
const { StructuredHTTPEmitter } = require("cloudevents-sdk/v1");
69+
By default, the `HTTPEmitter` will emit events over HTTP POST using the
70+
1.0 specification, in binary mode. You can emit 0.3 events by providing
71+
the specication version in the constructor to `HTTPEmitter`. To send
72+
structured events, add that string as a parameter to `emitter.sent()`.
7273

73-
const myevent = new CloudEvent()
74-
.type("com.github.pull.create")
75-
.source("urn:event:from:myapi/resource/123");
74+
```js
75+
const { CloudEvent, HTTPEmitter } = require("cloudevents-sdk");
7676

77-
const emitter = new StructuredHTTPEmitter({
78-
method: "POST",
79-
url : "https://myserver.com"
77+
// With only an endpoint URL, this creates a v1 emitter
78+
const v1Emitter = new HTTPEmitter({
79+
url: "https://cloudevents.io/example"
8080
});
81+
const event = new CloudEvent()
82+
.type(type)
83+
.source(source)
84+
.time(new Date())
85+
.data(data)
86+
87+
// By default, the emitter will send binary events
88+
v1Emitter.send(event).then((response) => {
89+
// handle the response
90+
}).catch(console.error);
91+
92+
// To send a structured event, just add that as an option
93+
v1Emitter.send(event, { mode: "structured" })
94+
.then((response) => {
95+
// handle the response
96+
}).catch(console.error);
97+
98+
// To send an event to an alternate URL, add that as an option
99+
v1Emitter.send(event, { url: "https://alternate.com/api" })
100+
.then((response) => {
101+
// handle the response
102+
}).catch(console.error);
103+
104+
// Sending a v0.3 event works the same, just let the emitter know when
105+
// you create it that you are working with the 0.3 spec
106+
const v03Emitter = new HTTPEmitter({
107+
url: "https://cloudevents.io/example",
108+
version: "0.3"
109+
});
110+
111+
// Again, the default is to send binary events
112+
// To send a structured event or to an alternate URL, provide those
113+
// as parameters in a options object as above
114+
v3Emitter.send(event)
115+
.then((response) => {
116+
// handle the response
117+
}).catch(console.error);
81118

82-
// Emit the event
83-
emitter.emit(myevent)
84119
```
85120

86121
## Supported specification features

index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
const CloudEvent = require("./lib/cloudevent.js");
22
const HTTPReceiver = require("./lib/bindings/http/http_receiver.js");
3+
const HTTPEmitter = require("./lib/bindings/http/http_emitter.js");
34

45
module.exports = {
56
CloudEvent,
6-
HTTPReceiver
7+
HTTPReceiver,
8+
HTTPEmitter
79
};

lib/bindings/http/emitter_binary.js

Lines changed: 79 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,85 @@
11
const axios = require("axios");
22

3-
const Constants = require("./constants.js");
4-
const defaults = {};
5-
defaults[Constants.HEADERS] = {};
6-
defaults[Constants.HEADERS][Constants.HEADER_CONTENT_TYPE] =
7-
Constants.DEFAULT_CONTENT_TYPE;
8-
9-
function BinaryHTTPEmitter(config, headerByGetter, extensionPrefix) {
10-
this.config = Object.assign({}, defaults, config);
11-
this.headerByGetter = headerByGetter;
12-
this.extensionPrefix = extensionPrefix;
13-
}
3+
const {
4+
HEADERS,
5+
BINARY_HEADERS_03,
6+
BINARY_HEADERS_1,
7+
HEADER_CONTENT_TYPE,
8+
DEFAULT_CONTENT_TYPE,
9+
DATA_ATTRIBUTE,
10+
SPEC_V1,
11+
SPEC_V03
12+
} = require("./constants.js");
1413

15-
BinaryHTTPEmitter.prototype.emit = function(cloudevent) {
16-
const config = Object.assign({}, this.config);
17-
const headers = Object.assign({}, this.config[Constants.HEADERS]);
18-
19-
Object.keys(this.headerByGetter)
20-
.filter((getter) => cloudevent[getter]())
21-
.forEach((getter) => {
22-
const header = this.headerByGetter[getter];
23-
headers[header.name] =
24-
header.parser(
25-
cloudevent[getter]()
26-
);
27-
});
28-
29-
// Set the cloudevent payload
30-
const formatted = cloudevent.format();
31-
let data = formatted.data;
32-
data = (formatted.data_base64 ? formatted.data_base64 : data);
33-
34-
// Have extensions?
35-
const exts = cloudevent.getExtensions();
36-
Object.keys(exts)
37-
.filter((ext) => Object.hasOwnProperty.call(exts, ext))
38-
.forEach((ext) => {
39-
headers[this.extensionPrefix + ext] = exts[ext];
40-
});
41-
42-
config[Constants.DATA_ATTRIBUTE] = data;
43-
config.headers = headers;
44-
45-
// Return the Promise
46-
return axios.request(config);
14+
const defaults = {
15+
[HEADERS]: {
16+
[HEADER_CONTENT_TYPE]: DEFAULT_CONTENT_TYPE
17+
},
18+
method: "POST"
4719
};
4820

21+
/**
22+
* A class to emit binary CloudEvents over HTTP.
23+
*/
24+
class BinaryHTTPEmitter {
25+
/**
26+
* Create a new {BinaryHTTPEmitter} for the provided CloudEvent specification version.
27+
* Once an instance is created for a given spec version, it may only be used to send
28+
* events for that version.
29+
* Default version is 1.0
30+
* @param {string} version - the CloudEvent HTTP specification version.
31+
* Default: 1.0
32+
*/
33+
constructor(version) {
34+
if (version === SPEC_V1) {
35+
this.headerByGetter = require("./emitter_binary_1.js");
36+
this.extensionPrefix = BINARY_HEADERS_1.EXTENSIONS_PREFIX;
37+
} else if (version === SPEC_V03) {
38+
this.headerByGetter = require("./emitter_binary_0_3.js");
39+
this.extensionPrefix = BINARY_HEADERS_03.EXTENSIONS_PREFIX;
40+
}
41+
}
42+
43+
/**
44+
* Sends this cloud event to a receiver over HTTP.
45+
*
46+
* @param {Object} options The configuration options for this event. Options
47+
* provided other than `url` will be passed along to Node.js `http.request`.
48+
* https://nodejs.org/api/http.html#http_http_request_options_callback
49+
* @param {URL} options.url The HTTP/S url that should receive this event
50+
* @param {Object} cloudevent the CloudEvent to be sent
51+
* @returns {Promise} Promise with an eventual response from the receiver
52+
*/
53+
async emit(options, cloudevent) {
54+
const config = { ...options, ...defaults };
55+
const headers = config[HEADERS];
56+
57+
Object.keys(this.headerByGetter)
58+
.filter((getter) => cloudevent[getter]())
59+
.forEach((getter) => {
60+
const header = this.headerByGetter[getter];
61+
headers[header.name] = header.parser(cloudevent[getter]());
62+
});
63+
64+
// Set the cloudevent payload
65+
const formatted = cloudevent.format();
66+
let data = formatted.data;
67+
data = (formatted.data_base64 ? formatted.data_base64 : data);
68+
69+
// Have extensions?
70+
const exts = cloudevent.getExtensions();
71+
Object.keys(exts)
72+
.filter((ext) => Object.hasOwnProperty.call(exts, ext))
73+
.forEach((ext) => {
74+
headers[this.extensionPrefix + ext] = exts[ext];
75+
});
76+
77+
config[DATA_ATTRIBUTE] = data;
78+
config.headers = headers;
79+
80+
// Return the Promise
81+
return axios.request(config);
82+
}
83+
}
84+
4985
module.exports = BinaryHTTPEmitter;
Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,53 @@
1-
const BinaryHTTPEmitter = require("./emitter_binary.js");
2-
3-
const Constants = require("./constants.js");
1+
const {
2+
HEADER_CONTENT_TYPE,
3+
BINARY_HEADERS_03
4+
} = require("./constants.js");
45

56
const headerByGetter = {};
67

78
headerByGetter.getDataContentType = {
8-
name: Constants.HEADER_CONTENT_TYPE,
9+
name: HEADER_CONTENT_TYPE,
910
parser: (v) => v
1011
};
1112

1213
headerByGetter.getDataContentEncoding = {
13-
name: Constants.BINARY_HEADERS_03.CONTENT_ENCONDING,
14+
name: BINARY_HEADERS_03.CONTENT_ENCONDING,
1415
parser: (v) => v
1516
};
1617

1718
headerByGetter.getSubject = {
18-
name: Constants.BINARY_HEADERS_03.SUBJECT,
19+
name: BINARY_HEADERS_03.SUBJECT,
1920
parser: (v) => v
2021
};
2122

2223
headerByGetter.getType = {
23-
name: Constants.BINARY_HEADERS_03.TYPE,
24+
name: BINARY_HEADERS_03.TYPE,
2425
parser: (v) => v
2526
};
2627

2728
headerByGetter.getSpecversion = {
28-
name: Constants.BINARY_HEADERS_03.SPEC_VERSION,
29+
name: BINARY_HEADERS_03.SPEC_VERSION,
2930
parser: (v) => v
3031
};
3132

3233
headerByGetter.getSource = {
33-
name: Constants.BINARY_HEADERS_03.SOURCE,
34+
name: BINARY_HEADERS_03.SOURCE,
3435
parser: (v) => v
3536
};
3637

3738
headerByGetter.getId = {
38-
name: Constants.BINARY_HEADERS_03.ID,
39+
name: BINARY_HEADERS_03.ID,
3940
parser: (v) => v
4041
};
4142

4243
headerByGetter.getTime = {
43-
name: Constants.BINARY_HEADERS_03.TIME,
44+
name: BINARY_HEADERS_03.TIME,
4445
parser: (v) => v
4546
};
4647

4748
headerByGetter.getSchemaurl = {
48-
name: Constants.BINARY_HEADERS_03.SCHEMA_URL,
49+
name: BINARY_HEADERS_03.SCHEMA_URL,
4950
parser: (v) => v
5051
};
5152

52-
function HTTPBinary(configuration) {
53-
this.emitter = new BinaryHTTPEmitter(
54-
configuration,
55-
headerByGetter,
56-
Constants.BINARY_HEADERS_03.EXTENSIONS_PREFIX
57-
);
58-
}
59-
60-
HTTPBinary.prototype.emit = function(cloudevent) {
61-
return this.emitter.emit(cloudevent);
62-
};
63-
64-
module.exports = HTTPBinary;
53+
module.exports = headerByGetter;

lib/bindings/http/emitter_binary_1.js

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,48 @@
1-
const BinaryHTTPEmitter = require("./emitter_binary.js");
2-
3-
const Constants = require("./constants.js");
1+
const {
2+
HEADER_CONTENT_TYPE,
3+
BINARY_HEADERS_1
4+
} = require("./constants.js");
45

56
const headerByGetter = {};
67

78
headerByGetter.getDataContentType = {
8-
name: Constants.HEADER_CONTENT_TYPE,
9+
name: HEADER_CONTENT_TYPE,
910
parser: (v) => v
1011
};
1112

1213
headerByGetter.getSubject = {
13-
name: Constants.BINARY_HEADERS_1.SUBJECT,
14+
name: BINARY_HEADERS_1.SUBJECT,
1415
parser: (v) => v
1516
};
1617

1718
headerByGetter.getType = {
18-
name: Constants.BINARY_HEADERS_1.TYPE,
19+
name: BINARY_HEADERS_1.TYPE,
1920
parser: (v) => v
2021
};
2122

2223
headerByGetter.getSpecversion = {
23-
name: Constants.BINARY_HEADERS_1.SPEC_VERSION,
24+
name: BINARY_HEADERS_1.SPEC_VERSION,
2425
parser: (v) => v
2526
};
2627

2728
headerByGetter.getSource = {
28-
name: Constants.BINARY_HEADERS_1.SOURCE,
29+
name: BINARY_HEADERS_1.SOURCE,
2930
parser: (v) => v
3031
};
3132

3233
headerByGetter.getId = {
33-
name: Constants.BINARY_HEADERS_1.ID,
34+
name: BINARY_HEADERS_1.ID,
3435
parser: (v) => v
3536
};
3637

3738
headerByGetter.getTime = {
38-
name: Constants.BINARY_HEADERS_1.TIME,
39+
name: BINARY_HEADERS_1.TIME,
3940
parser: (v) => v
4041
};
4142

4243
headerByGetter.getDataschema = {
43-
name: Constants.BINARY_HEADERS_1.DATA_SCHEMA,
44+
name: BINARY_HEADERS_1.DATA_SCHEMA,
4445
parser: (v) => v
4546
};
4647

47-
function HTTPBinary(configuration) {
48-
this.emitter = new BinaryHTTPEmitter(
49-
configuration,
50-
headerByGetter,
51-
Constants.BINARY_HEADERS_1.EXTENSIONS_PREFIX
52-
);
53-
}
54-
55-
HTTPBinary.prototype.emit = function(cloudevent) {
56-
return this.emitter.emit(cloudevent);
57-
};
58-
59-
module.exports = HTTPBinary;
48+
module.exports = headerByGetter;

0 commit comments

Comments
 (0)