Skip to content

Commit 54f242b

Browse files
authored
feat: expose a mode and version agnostic event receiver (cloudevents#120)
Event receivers in the wild may not always know what version or mode an incoming event is. Instead of requiring developers to inspect the headers themselves, the SDK should provide an HTTP receiver that is capable of figuring out what the version and mode (structured/binary) of an incoming event is and handle it appropriately. In determining the best way to expose this, I chose to modify the API a little bit. Now, instead of `const CloudEvent = require('cloudevents-sdk');` users need to destructure it. ```js const { HTTPReceiver, CloudEvent } = require('cloudevents-sdk'); ``` This change should not be backported to 1.x. Fixes: cloudevents#93 Signed-off-by: Lance Ball <[email protected]>
1 parent d9e9ae6 commit 54f242b

File tree

7 files changed

+160
-5
lines changed

7 files changed

+160
-5
lines changed

index.js

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

3-
module.exports = CloudEvent;
4+
module.exports = {
5+
CloudEvent,
6+
HTTPReceiver
7+
};

lib/bindings/http/http_receiver.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const V03Binary = require("./receiver_binary_0_3");
2+
const V03Structured = require("./receiver_structured_0_3.js");
3+
const V1Binary = require("./receiver_binary_1.js");
4+
const V1Structured = require("./receiver_structured_1.js");
5+
const constants = require("./constants");
6+
7+
class HTTPReceiver {
8+
constructor() {
9+
this.receivers = {
10+
v1: {
11+
structured: new V1Structured(),
12+
binary: new V1Binary()
13+
},
14+
v03: {
15+
structured: new V03Structured(),
16+
binary: new V03Binary()
17+
}
18+
};
19+
}
20+
21+
accept(headers, body) {
22+
const mode = getMode(headers);
23+
const version = getVersion(mode, headers, body);
24+
switch (version) {
25+
case constants.SPEC_V1:
26+
return this.receivers.v1[mode].parse(body, headers);
27+
case constants.SPEC_V03:
28+
return this.receivers.v03[mode].parse(body, headers);
29+
default:
30+
console.error(
31+
`Unknown spec version ${version}. Default to ${constants.SPEC_V1}`);
32+
return this.receivers.v1[mode].parse(body, headers);
33+
}
34+
}
35+
}
36+
37+
function getMode(headers) {
38+
let mode = "binary";
39+
const contentType = headers[constants.HEADER_CONTENT_TYPE];
40+
if (contentType && contentType.startsWith(constants.MIME_CE)) {
41+
mode = "structured";
42+
}
43+
return mode;
44+
}
45+
46+
function getVersion(mode, headers, body) {
47+
let version = constants.SPEC_V1; // default to 1.0
48+
49+
if (mode === "binary") {
50+
// Check the headers for the version
51+
const versionHeader = headers[constants.DEFAULT_SPEC_VERSION_HEADER];
52+
if (versionHeader) { version = versionHeader; }
53+
} else {
54+
// structured mode - the version is in the body
55+
version = body instanceof String
56+
? JSON.parse(body).specversion : body.specversion;
57+
}
58+
return version;
59+
}
60+
61+
module.exports = HTTPReceiver;
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
const { expect } = require("chai");
2+
const { CloudEvent, HTTPReceiver } = require("../../../index.js");
3+
const constants = require("../../../lib/bindings/http/constants.js");
4+
5+
const receiver = new HTTPReceiver();
6+
const id = "1234";
7+
const type = "org.cncf.cloudevents.test";
8+
const source = "urn:event:from:myapi/resourse/123";
9+
const data = {
10+
lunch: "sushi"
11+
};
12+
13+
describe("HTTP Transport Binding Receiver for CloudEvents", () => {
14+
describe("V1", () => {
15+
const specversion = "1.0";
16+
17+
it("Structured data returns a CloudEvent", () => {
18+
const payload = {
19+
id,
20+
type,
21+
source,
22+
data,
23+
specversion
24+
};
25+
26+
const headers = {
27+
[constants.HEADER_CONTENT_TYPE]: constants.MIME_CE_JSON
28+
};
29+
30+
const event = receiver.accept(headers, payload);
31+
validateEvent(event, specversion);
32+
});
33+
34+
it("Binary data returns a CloudEvent", () => {
35+
const headers = {
36+
[constants.HEADER_CONTENT_TYPE]: constants.DEFAULT_CONTENT_TYPE,
37+
[constants.DEFAULT_SPEC_VERSION_HEADER]: specversion,
38+
[constants.BINARY_HEADERS_1.ID]: id,
39+
[constants.BINARY_HEADERS_1.TYPE]: type,
40+
[constants.BINARY_HEADERS_1.SOURCE]: source
41+
};
42+
43+
const event = receiver.accept(headers, data);
44+
validateEvent(event, specversion);
45+
});
46+
});
47+
48+
describe("V03", () => {
49+
const specversion = "0.3";
50+
51+
it("Structured data returns a CloudEvent", () => {
52+
const payload = {
53+
id,
54+
type,
55+
source,
56+
data,
57+
specversion
58+
};
59+
60+
const headers = {
61+
[constants.HEADER_CONTENT_TYPE]: constants.MIME_CE_JSON
62+
};
63+
64+
const event = receiver.accept(headers, payload);
65+
validateEvent(event, specversion);
66+
});
67+
68+
it("Binary data returns a CloudEvent", () => {
69+
const headers = {
70+
[constants.HEADER_CONTENT_TYPE]: constants.DEFAULT_CONTENT_TYPE,
71+
[constants.DEFAULT_SPEC_VERSION_HEADER]: specversion,
72+
[constants.BINARY_HEADERS_03.ID]: id,
73+
[constants.BINARY_HEADERS_03.TYPE]: type,
74+
[constants.BINARY_HEADERS_03.SOURCE]: source
75+
};
76+
77+
const event = receiver.accept(headers, data);
78+
validateEvent(event, specversion);
79+
});
80+
});
81+
});
82+
83+
function validateEvent(event, specversion) {
84+
expect(event instanceof CloudEvent).to.equal(true);
85+
expect(event.getId()).to.equal(id);
86+
expect(event.getType()).to.equal(type);
87+
expect(event.getSource()).to.equal(source);
88+
expect(event.getData()).to.deep.equal(data);
89+
expect(event.getSpecversion()).to.equal(specversion);
90+
}

test/bindings/http/receiver_structured_1_test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const expect = require("chai").expect;
22
const v1 = require("../../../v1/index.js");
3-
const CloudEvent = require("../../../index.js");
3+
const { CloudEvent } = require("../../../index.js");
44

55
const { asBase64 } = require("../../../lib/utils/fun.js");
66

test/bindings/http/unmarshaller_0_3_tests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const expect = require("chai").expect;
22
const Unmarshaller = require("../../../lib/bindings/http/unmarshaller_0_3.js");
3-
const CloudEvent = require("../../../index.js");
3+
const { CloudEvent } = require("../../../index.js");
44
const v03 = require("../../../v03/index.js");
55

66
const type = "com.github.pull.create";

test/spec_0_3_tests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const expect = require("chai").expect;
22
const Spec03 = require("../lib/specs/spec_0_3.js");
3-
const CloudEvent = require("../index.js");
3+
const { CloudEvent } = require("../index.js");
44
const { v4: uuidv4 } = require("uuid");
55

66
const id = uuidv4();

test/spec_1_tests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const expect = require("chai").expect;
22
const Spec1 = require("../lib/specs/spec_1.js");
3-
const CloudEvent = require("../index.js");
3+
const { CloudEvent } = require("../index.js");
44
const { v4: uuidv4 } = require("uuid");
55
const { asBase64 } = require("../lib/utils/fun.js");
66

0 commit comments

Comments
 (0)