Skip to content

Commit 84f1ed9

Browse files
authored
feat(src): add ext name validation (#246)
BREAKING CHANGE: * Extension names are now validated during object creation. The values are defined by the specification, and can be lowercase(a-z) or digits(0-9) and must be no longer that 20 characters Signed-off-by: Lucas Holmquist <lholmqui@redhat.com>
1 parent de6f0a2 commit 84f1ed9

File tree

4 files changed

+26
-15
lines changed

4 files changed

+26
-15
lines changed

Diff for: src/event/cloudevent.ts

+5
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
9696

9797
// finally process any remaining properties - these are extensions
9898
for (const [key, value] of Object.entries(properties)) {
99+
// Extension names should only allow lowercase a-z and 0-9 in the name
100+
// names should not exceed 20 characters in length
101+
if (!key.match(/^[a-z0-9]{1,20}$/)) {
102+
throw new ValidationError("invalid extension name");
103+
}
99104
this[key] = value;
100105
}
101106

Diff for: test/integration/cloud_event_test.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ describe("A CloudEvent", () => {
2525
const ce = new CloudEvent(fixture);
2626
expect(ce.toString()).to.deep.equal(JSON.stringify(ce));
2727
});
28+
29+
it("Throw a validation error for invalid extension names", () => {
30+
expect(() => {
31+
new CloudEvent({ "ext-1": "extension1", ...fixture });
32+
}).throw("invalid extension name");
33+
});
34+
35+
it("Throw a validation error for invalid extension names, more than 20 chars", () => {
36+
expect(() => {
37+
new CloudEvent({ "123456789012345678901": "extension1", ...fixture });
38+
}).throw("invalid extension name");
39+
});
2840
});
2941

3042
describe("A 1.0 CloudEvent", () => {
@@ -92,13 +104,13 @@ describe("A 1.0 CloudEvent", () => {
92104

93105
it("can be constructed with extensions", () => {
94106
const extensions = {
95-
"extension-key": "extension-value",
107+
extensionkey: "extension-value",
96108
};
97109
const ce = new CloudEvent({
98110
...extensions,
99111
...fixture,
100112
});
101-
expect(ce["extension-key"]).to.equal(extensions["extension-key"]);
113+
expect(ce["extensionkey"]).to.equal(extensions["extensionkey"]);
102114
});
103115

104116
it("throws ValidationError if the CloudEvent does not conform to the schema");

Diff for: test/integration/receiver_structured_1_test.ts

+1-7
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ const source = "urn:event:from:myapi/resourse/123";
1111
const time = new Date();
1212
const dataschema = "http://cloudevents.io/schema.json";
1313

14-
const ceContentType = "application/json";
15-
1614
const data = {
1715
foo: "bar",
1816
};
@@ -108,7 +106,6 @@ describe("HTTP Transport Binding Structured Receiver for CloudEvents v1.0", () =
108106
time,
109107
data,
110108
dataschema,
111-
dataContentType: ceContentType,
112109
};
113110
const headers = {
114111
"content-type": "application/cloudevents+json",
@@ -124,14 +121,13 @@ describe("HTTP Transport Binding Structured Receiver for CloudEvents v1.0", () =
124121

125122
it("Should accept 'extension1'", () => {
126123
// setup
127-
const extension1 = "mycustom-ext1";
124+
const extension1 = "mycustomext1";
128125
const event = {
129126
type,
130127
source,
131128
time,
132129
data,
133130
dataschema,
134-
dataContentType: ceContentType,
135131
extension1,
136132
};
137133

@@ -152,7 +148,6 @@ describe("HTTP Transport Binding Structured Receiver for CloudEvents v1.0", () =
152148
time,
153149
dataschema,
154150
data: data,
155-
dataContentType: ceContentType,
156151
};
157152

158153
const headers = {
@@ -173,7 +168,6 @@ describe("HTTP Transport Binding Structured Receiver for CloudEvents v1.0", () =
173168
type,
174169
source,
175170
data: bindata,
176-
dataContentType: ceContentType,
177171
};
178172

179173
const headers = {

Diff for: test/integration/spec_1_tests.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -65,34 +65,34 @@ describe("CloudEvents Spec v1.0", () => {
6565

6666
describe("Extensions Constraints", () => {
6767
it("should be ok when type is 'boolean'", () => {
68-
expect(cloudevent.cloneWith({ "ext-boolean": true }).validate()).to.equal(true);
68+
expect(cloudevent.cloneWith({ extboolean: true }).validate()).to.equal(true);
6969
});
7070

7171
it("should be ok when type is 'integer'", () => {
72-
expect(cloudevent.cloneWith({ "ext-integer": 2019 }).validate()).to.equal(true);
72+
expect(cloudevent.cloneWith({ extinteger: 2019 }).validate()).to.equal(true);
7373
});
7474

7575
it("should be ok when type is 'string'", () => {
76-
expect(cloudevent.cloneWith({ "ext-string": "an-string" }).validate()).to.equal(true);
76+
expect(cloudevent.cloneWith({ extstring: "an-string" }).validate()).to.equal(true);
7777
});
7878

7979
it("should be ok when type is 'Uint32Array' for 'Binary'", () => {
8080
const myBinary = new Uint32Array(2019);
81-
expect(cloudevent.cloneWith({ "ext-binary": myBinary }).validate()).to.equal(true);
81+
expect(cloudevent.cloneWith({ extbinary: myBinary }).validate()).to.equal(true);
8282
});
8383

8484
// URI
8585
it("should be ok when type is 'Date' for 'Timestamp'", () => {
8686
const myDate = new Date();
87-
expect(cloudevent.cloneWith({ "ext-date": myDate }).validate()).to.equal(true);
87+
expect(cloudevent.cloneWith({ extdate: myDate }).validate()).to.equal(true);
8888
});
8989

9090
// even though the spec doesn't allow object types for
9191
// extensions, it could be JSON. And before a JS CE
9292
// is transmitted across the wire, this value will be
9393
// converted to JSON
9494
it("should be ok when the type is an object", () => {
95-
expect(cloudevent.cloneWith({ "object-extension": { some: "object" } }).validate()).to.equal(true);
95+
expect(cloudevent.cloneWith({ objectextension: { some: "object" } }).validate()).to.equal(true);
9696
});
9797
});
9898

0 commit comments

Comments
 (0)