Skip to content

Commit 252555f

Browse files
committed
feat: add a constructor parameter for loose validation
This commit adds a second, optional boolean parameter to the `CloudEvent` constructor. When `false` is provided, the event constructor will not perform validation of the event properties, values and extension names. Fixes: #325 Signed-off-by: Lance Ball <lball@redhat.com>
1 parent 17d4bc8 commit 252555f

File tree

2 files changed

+32
-5
lines changed

2 files changed

+32
-5
lines changed

src/event/cloudevent.ts

+15-5
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,15 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
4646
schemaurl?: string;
4747
datacontentencoding?: string;
4848

49-
constructor(event: CloudEventV1 | CloudEventV1Attributes | CloudEventV03 | CloudEventV03Attributes) {
49+
/**
50+
* Creates a new CloudEvent object with the provided properties. If there is a chance that the event
51+
* properties will not conform to the CloudEvent specification, you may pass a boolean `false` as a
52+
* second parameter to bypass event validation.
53+
*
54+
* @param {object} event the event properties
55+
* @param {boolean?} strict whether to perform event validation when creating the object - default: true
56+
*/
57+
constructor(event: CloudEventV1 | CloudEventV1Attributes | CloudEventV03 | CloudEventV03Attributes, strict = true) {
5058
// copy the incoming event so that we can delete properties as we go
5159
// everything left after we have deleted know properties becomes an extension
5260
const properties = { ...event };
@@ -105,20 +113,20 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
105113
for (const [key, value] of Object.entries(properties)) {
106114
// Extension names should only allow lowercase a-z and 0-9 in the name
107115
// names should not exceed 20 characters in length
108-
if (!key.match(/^[a-z0-9]{1,20}$/)) {
116+
if (!key.match(/^[a-z0-9]{1,20}$/) && strict) {
109117
throw new ValidationError("invalid extension name");
110118
}
111119

112120
// Value should be spec compliant
113121
// https://github.com/cloudevents/spec/blob/master/spec.md#type-system
114-
if (!isValidType(value)) {
122+
if (!isValidType(value) && strict) {
115123
throw new ValidationError("invalid extension value");
116124
}
117125

118126
this[key] = value;
119127
}
120128

121-
this.validate();
129+
strict ? this.validate() : undefined;
122130

123131
Object.freeze(this);
124132
}
@@ -187,6 +195,7 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
187195
/**
188196
* Clone a CloudEvent with new/update attributes
189197
* @param {object} options attributes to augment the CloudEvent with
198+
* @param {boolean} strict whether or not to use strict validation when cloning (default: true)
190199
* @throws if the CloudEvent does not conform to the schema
191200
* @return {CloudEvent} returns a new CloudEvent
192201
*/
@@ -198,7 +207,8 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
198207
| CloudEventV03
199208
| CloudEventV03Attributes
200209
| CloudEventV03OptionalAttributes,
210+
strict = true,
201211
): CloudEvent {
202-
return new CloudEvent(Object.assign({}, this.toJSON(), options) as CloudEvent);
212+
return new CloudEvent(Object.assign({}, this.toJSON(), options) as CloudEvent, strict);
203213
}
204214
}

test/integration/cloud_event_test.ts

+17
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { ValidationError } from "ajv";
12
import { expect } from "chai";
23
import { CloudEvent, Version } from "../../src";
34
import { CloudEventV03, CloudEventV1 } from "../../src/event/interfaces";
@@ -20,6 +21,22 @@ describe("A CloudEvent", () => {
2021
expect(ce.source).to.equal(source);
2122
});
2223

24+
it("Can be constructed with loose validation", () => {
25+
const ce = new CloudEvent({} as CloudEventV1, false);
26+
expect(ce).to.be.instanceOf(CloudEvent);
27+
});
28+
29+
it("Loosely validated events can be cloned", () => {
30+
const ce = new CloudEvent({} as CloudEventV1, false);
31+
expect(ce.cloneWith({}, false)).to.be.instanceOf(CloudEvent);
32+
console.error(ce);
33+
});
34+
35+
it("Loosely validated events throw when validated", () => {
36+
const ce = new CloudEvent({} as CloudEventV1, false);
37+
expect(ce.validate).to.throw(TypeError, "invalid payload");
38+
});
39+
2340
it("serializes as JSON with toString()", () => {
2441
const ce = new CloudEvent(fixture);
2542
expect(ce.toString()).to.deep.equal(JSON.stringify(ce));

0 commit comments

Comments
 (0)