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>
This commit is contained in:
Lucas Holmquist 2020-07-15 09:53:52 -04:00 committed by GitHub
parent de6f0a2945
commit 84f1ed9cfe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 26 additions and 15 deletions

View File

@ -96,6 +96,11 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 {
// finally process any remaining properties - these are extensions
for (const [key, value] of Object.entries(properties)) {
// Extension names should only allow lowercase a-z and 0-9 in the name
// names should not exceed 20 characters in length
if (!key.match(/^[a-z0-9]{1,20}$/)) {
throw new ValidationError("invalid extension name");
}
this[key] = value;
}

View File

@ -25,6 +25,18 @@ describe("A CloudEvent", () => {
const ce = new CloudEvent(fixture);
expect(ce.toString()).to.deep.equal(JSON.stringify(ce));
});
it("Throw a validation error for invalid extension names", () => {
expect(() => {
new CloudEvent({ "ext-1": "extension1", ...fixture });
}).throw("invalid extension name");
});
it("Throw a validation error for invalid extension names, more than 20 chars", () => {
expect(() => {
new CloudEvent({ "123456789012345678901": "extension1", ...fixture });
}).throw("invalid extension name");
});
});
describe("A 1.0 CloudEvent", () => {
@ -92,13 +104,13 @@ describe("A 1.0 CloudEvent", () => {
it("can be constructed with extensions", () => {
const extensions = {
"extension-key": "extension-value",
extensionkey: "extension-value",
};
const ce = new CloudEvent({
...extensions,
...fixture,
});
expect(ce["extension-key"]).to.equal(extensions["extension-key"]);
expect(ce["extensionkey"]).to.equal(extensions["extensionkey"]);
});
it("throws ValidationError if the CloudEvent does not conform to the schema");

View File

@ -11,8 +11,6 @@ const source = "urn:event:from:myapi/resourse/123";
const time = new Date();
const dataschema = "http://cloudevents.io/schema.json";
const ceContentType = "application/json";
const data = {
foo: "bar",
};
@ -108,7 +106,6 @@ describe("HTTP Transport Binding Structured Receiver for CloudEvents v1.0", () =
time,
data,
dataschema,
dataContentType: ceContentType,
};
const headers = {
"content-type": "application/cloudevents+json",
@ -124,14 +121,13 @@ describe("HTTP Transport Binding Structured Receiver for CloudEvents v1.0", () =
it("Should accept 'extension1'", () => {
// setup
const extension1 = "mycustom-ext1";
const extension1 = "mycustomext1";
const event = {
type,
source,
time,
data,
dataschema,
dataContentType: ceContentType,
extension1,
};
@ -152,7 +148,6 @@ describe("HTTP Transport Binding Structured Receiver for CloudEvents v1.0", () =
time,
dataschema,
data: data,
dataContentType: ceContentType,
};
const headers = {
@ -173,7 +168,6 @@ describe("HTTP Transport Binding Structured Receiver for CloudEvents v1.0", () =
type,
source,
data: bindata,
dataContentType: ceContentType,
};
const headers = {

View File

@ -65,26 +65,26 @@ describe("CloudEvents Spec v1.0", () => {
describe("Extensions Constraints", () => {
it("should be ok when type is 'boolean'", () => {
expect(cloudevent.cloneWith({ "ext-boolean": true }).validate()).to.equal(true);
expect(cloudevent.cloneWith({ extboolean: true }).validate()).to.equal(true);
});
it("should be ok when type is 'integer'", () => {
expect(cloudevent.cloneWith({ "ext-integer": 2019 }).validate()).to.equal(true);
expect(cloudevent.cloneWith({ extinteger: 2019 }).validate()).to.equal(true);
});
it("should be ok when type is 'string'", () => {
expect(cloudevent.cloneWith({ "ext-string": "an-string" }).validate()).to.equal(true);
expect(cloudevent.cloneWith({ extstring: "an-string" }).validate()).to.equal(true);
});
it("should be ok when type is 'Uint32Array' for 'Binary'", () => {
const myBinary = new Uint32Array(2019);
expect(cloudevent.cloneWith({ "ext-binary": myBinary }).validate()).to.equal(true);
expect(cloudevent.cloneWith({ extbinary: myBinary }).validate()).to.equal(true);
});
// URI
it("should be ok when type is 'Date' for 'Timestamp'", () => {
const myDate = new Date();
expect(cloudevent.cloneWith({ "ext-date": myDate }).validate()).to.equal(true);
expect(cloudevent.cloneWith({ extdate: myDate }).validate()).to.equal(true);
});
// even though the spec doesn't allow object types for
@ -92,7 +92,7 @@ describe("CloudEvents Spec v1.0", () => {
// is transmitted across the wire, this value will be
// converted to JSON
it("should be ok when the type is an object", () => {
expect(cloudevent.cloneWith({ "object-extension": { some: "object" } }).validate()).to.equal(true);
expect(cloudevent.cloneWith({ objectextension: { some: "object" } }).validate()).to.equal(true);
});
});