diff --git a/src/event/cloudevent.ts b/src/event/cloudevent.ts index 4cc7fcb..0cab0da 100644 --- a/src/event/cloudevent.ts +++ b/src/event/cloudevent.ts @@ -9,7 +9,7 @@ import { CloudEventV1OptionalAttributes, } from "./interfaces"; import { validateV1, validateV03 } from "./spec"; -import { ValidationError, isBinary, asBase64 } from "./validation"; +import { ValidationError, isBinary, asBase64, isValidType } from "./validation"; import CONSTANTS from "../constants"; import { isString } from "util"; @@ -108,6 +108,13 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 { if (!key.match(/^[a-z0-9]{1,20}$/)) { throw new ValidationError("invalid extension name"); } + + // Value should be spec compliant + // https://github.com/cloudevents/spec/blob/master/spec.md#type-system + if (!isValidType(value)) { + throw new ValidationError("invalid extension value"); + } + this[key] = value; } diff --git a/src/event/validation.ts b/src/event/validation.ts index 56b97e5..6ca0258 100644 --- a/src/event/validation.ts +++ b/src/event/validation.ts @@ -81,5 +81,5 @@ export const asData = (data: unknown, contentType: string): string => { return isBinary(maybeJson) ? asBase64(maybeJson) : maybeJson; }; -export const isValidType = (v: boolean | number | string | Date | Uint32Array): boolean => - isBoolean(v) || isInteger(v) || isString(v) || isDate(v) || isBinary(v); +export const isValidType = (v: boolean | number | string | Date | Uint32Array | unknown): boolean => + isBoolean(v) || isInteger(v) || isString(v) || isDate(v) || isBinary(v) || isObject(v); diff --git a/test/integration/http_emitter_test.ts b/test/integration/http_emitter_test.ts index 1644af9..b5f211b 100644 --- a/test/integration/http_emitter_test.ts +++ b/test/integration/http_emitter_test.ts @@ -15,6 +15,8 @@ const ext1Name = "lunch"; const ext1Value = "tacos"; const ext2Name = "supper"; const ext2Value = "sushi"; +const ext3Name = "snack"; +const ext3Value = { value: "chips" }; const data = { lunchBreak: "noon", @@ -45,6 +47,7 @@ describe("HTTP Transport Binding Emitter for CloudEvents", () => { data, [ext1Name]: ext1Value, [ext2Name]: ext2Value, + [ext3Name]: ext3Value, }); it("Sends a binary 1.0 CloudEvent by default", () => { @@ -59,6 +62,7 @@ describe("HTTP Transport Binding Emitter for CloudEvents", () => { // Ensure extensions are handled properly expect(response.data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext1Name}`]).to.equal(ext1Value); expect(response.data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext2Name}`]).to.equal(ext2Value); + expect(response.data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext3Name}`].value).to.equal(ext3Value.value); }) .catch(expect.fail); }); @@ -142,6 +146,7 @@ describe("HTTP Transport Binding Emitter for CloudEvents", () => { data, [ext1Name]: ext1Value, [ext2Name]: ext2Value, + [ext3Name]: ext3Value, }); it("Sends a binary 0.3 CloudEvent", () => { @@ -156,6 +161,7 @@ describe("HTTP Transport Binding Emitter for CloudEvents", () => { // Ensure extensions are handled properly expect(response.data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext1Name}`]).to.equal(ext1Value); expect(response.data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext2Name}`]).to.equal(ext2Value); + expect(response.data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext3Name}`].value).to.equal(ext3Value.value); }) .catch(expect.fail); }); diff --git a/test/integration/spec_1_tests.ts b/test/integration/spec_1_tests.ts index d580283..e39d3a5 100644 --- a/test/integration/spec_1_tests.ts +++ b/test/integration/spec_1_tests.ts @@ -87,13 +87,13 @@ describe("CloudEvents Spec v1.0", () => { expect(cloudevent.cloneWith({ extdate: myDate }).validate()).to.equal(true); }); - // even though the spec doesn't allow object types for - // extensions, it could be JSON. And before a JS CE - // 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({ objectextension: { some: "object" } }).validate()).to.equal(true); }); + + it("should be ok when the type is an string converted from an object", () => { + expect(cloudevent.cloneWith({ objectextension: JSON.stringify({ some: "object" }) }).validate()).to.equal(true); + }); }); describe("The Constraints check", () => {