diff --git a/CHANGELOG.md b/CHANGELOG.md index 4da4e176..a9253890 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,93 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [3.0.1](https://github.com/cloudevents/sdk-javascript/compare/v3.0.0...v3.0.1) (2020-07-29) + + +### Bug Fixes + +* ensure that event data can be an array, number, boolean or null ([#281](https://github.com/cloudevents/sdk-javascript/issues/281)) ([b99f728](https://github.com/cloudevents/sdk-javascript/commit/b99f7281904b41d9058fec8f51019c5937821dc9)) + + +### Miscellaneous + +* move typedoc them to a dev dependency. ([#279](https://github.com/cloudevents/sdk-javascript/issues/279)) ([c76dda6](https://github.com/cloudevents/sdk-javascript/commit/c76dda6d1052964b772533306f58ef46c8c9b642)), closes [#278](https://github.com/cloudevents/sdk-javascript/issues/278) + +## [3.0.0](https://github.com/cloudevents/sdk-javascript/compare/v2.0.2...v3.0.0) (2020-07-27) + + +### ⚠ BREAKING CHANGES + +* This validates the value of the cloud event extension based on the spec, +https://github.com/cloudevents/spec/blob/master/spec.md#type-system + +Signed-off-by: Lucas Holmquist +* This changes the modules name from cloudevents-sdk to cloudevents + +* feat: use npm name cloudevents +* **src:** * 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 +* **src:** * This change makes the CloudEvent Read-only and validates the input during object creation. + +* To augment an already created CloudEvent object, we have added a `cloneWith` method that takes attributes to add/update. + +Signed-off-by: Lucas Holmquist + +### Features + +* add types to package.json ([#216](https://github.com/cloudevents/sdk-javascript/issues/216)) ([4265281](https://github.com/cloudevents/sdk-javascript/commit/42652819f32df245e4e314d2df3758f6e5ca6926)) +* introduce browser support ([#201](https://github.com/cloudevents/sdk-javascript/issues/201)) ([8b2725b](https://github.com/cloudevents/sdk-javascript/commit/8b2725b10a10ba5da842e23d3d2673b27ca450df)) +* pass extension into the constructor. ([#214](https://github.com/cloudevents/sdk-javascript/issues/214)) ([0378f4c](https://github.com/cloudevents/sdk-javascript/commit/0378f4cdf9fde647ac3ffe1f9fb6bd3ff0924280)), closes [#209](https://github.com/cloudevents/sdk-javascript/issues/209) +* remove unused plugins ([#262](https://github.com/cloudevents/sdk-javascript/issues/262)) ([4014da2](https://github.com/cloudevents/sdk-javascript/commit/4014da26f5ae00d8846ab804ba0fb847051c44eb)) +* simplify validation logic/imports ([#265](https://github.com/cloudevents/sdk-javascript/issues/265)) ([4b54b27](https://github.com/cloudevents/sdk-javascript/commit/4b54b272a5578523c3a03b21720cb89c0d5177db)) +* use npm name cloudevents ([#260](https://github.com/cloudevents/sdk-javascript/issues/260)) ([565f867](https://github.com/cloudevents/sdk-javascript/commit/565f8674246b5f60ff78141ea9548b4137e2befc)), closes [#215](https://github.com/cloudevents/sdk-javascript/issues/215) +* **src:** A CloudEvent should be readonly but provide a way to augment itself. ([#234](https://github.com/cloudevents/sdk-javascript/issues/234)) ([c7a8477](https://github.com/cloudevents/sdk-javascript/commit/c7a84772d59647e2a94991a3051b37b097b0d404)) +* **src:** add ext name validation ([#246](https://github.com/cloudevents/sdk-javascript/issues/246)) ([84f1ed9](https://github.com/cloudevents/sdk-javascript/commit/84f1ed9cfe54d60978c525de5b0c49cf38839deb)) + + +### Bug Fixes + +* do not require an HTTP body on incoming binary event messages ([a7c326b](https://github.com/cloudevents/sdk-javascript/commit/a7c326b48cf702a077fc4af1eda3f686429ba9df)) +* ensure that the HTTP receiver sanitizes headers in accept() ([#239](https://github.com/cloudevents/sdk-javascript/issues/239)) ([51035dc](https://github.com/cloudevents/sdk-javascript/commit/51035dc65b98ce7912d57a78d214612c05c5dc00)) +* package.json & package-lock.json to reduce vulnerabilities ([#253](https://github.com/cloudevents/sdk-javascript/issues/253)) ([2ed5f84](https://github.com/cloudevents/sdk-javascript/commit/2ed5f844570efdf3ccc301fd5854fe4100921e04)) +* parse method mutating its input ([#231](https://github.com/cloudevents/sdk-javascript/issues/231)) ([060b21b](https://github.com/cloudevents/sdk-javascript/commit/060b21ba36a37afc9002fe2e3d2d3b161633f9ae)) +* upgrade uuid from 8.0.0 to 8.1.0 ([#220](https://github.com/cloudevents/sdk-javascript/issues/220)) ([25077a9](https://github.com/cloudevents/sdk-javascript/commit/25077a9b43bc2e54cd52266fb664a9c523d3f65c)) +* upgrade uuid from 8.1.0 to 8.2.0 ([#250](https://github.com/cloudevents/sdk-javascript/issues/250)) ([13bcdb4](https://github.com/cloudevents/sdk-javascript/commit/13bcdb4b9817bcf28991269f554f73893b42b458)) + + +### Tests + +* inplement the cucumber conformance tests from cloudevents/spec ([#238](https://github.com/cloudevents/sdk-javascript/issues/238)) ([dca2811](https://github.com/cloudevents/sdk-javascript/commit/dca2811627f36427a3b9bc710cc338d76f5d6f8b)) + + +### Documentation + +* clean up spec compliance table on README.md ([#252](https://github.com/cloudevents/sdk-javascript/issues/252)) ([c496931](https://github.com/cloudevents/sdk-javascript/commit/c49693189d16655b0de50f1c52baea6ec3e1d9ba)) +* **README:** fix wrong order of arguments in the accept example ([#224](https://github.com/cloudevents/sdk-javascript/issues/224)) ([850e893](https://github.com/cloudevents/sdk-javascript/commit/850e893ca7103846e5e85905523a0913ee8a8f4e)), closes [#222](https://github.com/cloudevents/sdk-javascript/issues/222) +* **README:** Update readme to mention that CloudEvents are read-only now ([#248](https://github.com/cloudevents/sdk-javascript/issues/248)) ([de6f0a2](https://github.com/cloudevents/sdk-javascript/commit/de6f0a2945f4fdfb3fdee75c0e20c863a356ff90)) +* generate api documentation as a GitHub workflow ([#217](https://github.com/cloudevents/sdk-javascript/issues/217)) ([44b791b](https://github.com/cloudevents/sdk-javascript/commit/44b791bf97fdbcd7e1c4a0a7725e4707892653b7)) +* Update references of specific versions to use Latest Supported. ([#211](https://github.com/cloudevents/sdk-javascript/issues/211)) ([ed1d328](https://github.com/cloudevents/sdk-javascript/commit/ed1d3286fa85a0f2eb4c02f4588d9f80dccc5ece)), closes [#160](https://github.com/cloudevents/sdk-javascript/issues/160) + + +### lib + +* validate extension values ([#251](https://github.com/cloudevents/sdk-javascript/issues/251)) ([3c8273f](https://github.com/cloudevents/sdk-javascript/commit/3c8273f1140455ce8346918942c44dda241ead4c)) + + +### Miscellaneous + +* add vscode task JSON and GitHub issue/pr templates ([#268](https://github.com/cloudevents/sdk-javascript/issues/268)) ([1613595](https://github.com/cloudevents/sdk-javascript/commit/1613595a38ba1d268dc45da4b09239dfac8afee8)) +* adds the return type for the extensions ([#221](https://github.com/cloudevents/sdk-javascript/issues/221)) ([5ab8164](https://github.com/cloudevents/sdk-javascript/commit/5ab81641aeaa7ef2d5dc23265277c65be144a881)) +* bump GH stale action to v3 ([#243](https://github.com/cloudevents/sdk-javascript/issues/243)) ([90a9984](https://github.com/cloudevents/sdk-javascript/commit/90a998472c133dfc54c497eb87224fecce2ae031)) +* combine v03 and v1 event interfaces, specs and schemas into single files([#270](https://github.com/cloudevents/sdk-javascript/issues/270)) ([129ec48](https://github.com/cloudevents/sdk-javascript/commit/129ec485d96dd38cb1d12f8074c6fcfb4cf6e689)) +* consolidate HTTP parsers and header maps into single files ([#267](https://github.com/cloudevents/sdk-javascript/issues/267)) ([45850e3](https://github.com/cloudevents/sdk-javascript/commit/45850e329a636904924d9d719771fdf4ec683a5d)) +* simplify ce version parsers ([#274](https://github.com/cloudevents/sdk-javascript/issues/274)) ([3d82fb6](https://github.com/cloudevents/sdk-javascript/commit/3d82fb629182edaee11e571ea895a5f8a7456a0e)) +* simplify parser logic and duplicated code ([#269](https://github.com/cloudevents/sdk-javascript/issues/269)) ([a6124cc](https://github.com/cloudevents/sdk-javascript/commit/a6124cc350a106ea41bba2ccd1e0cb42a67f99bf)) +* **actions:** don't auto-close stale issues and pull requests ([#235](https://github.com/cloudevents/sdk-javascript/issues/235)) ([d65b013](https://github.com/cloudevents/sdk-javascript/commit/d65b0135e0b5238f7d6e0c4a683401c1dc625b38)) +* Update examples to use the latest sdk version(2.0.2) ([#206](https://github.com/cloudevents/sdk-javascript/issues/206)) ([dcb3c4e](https://github.com/cloudevents/sdk-javascript/commit/dcb3c4e98acb5fc4fca518d2121a46797388456f)) +* webpack should publish to bundles not _bundles ([#227](https://github.com/cloudevents/sdk-javascript/issues/227)) ([7012433](https://github.com/cloudevents/sdk-javascript/commit/701243307440da7b9890e10faf6d57368912c4a7)) + ### [2.0.2](https://github.com/cloudevents/sdk-javascript/compare/v2.0.1...v2.0.2) (2020-06-08) diff --git a/README.md b/README.md index e8fb4107..c128711f 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,8 @@ const { Receiver } = require("cloudevents"); -// Create a receiver to accept events over HTTP -const receiver = new Receiver(); - // body and headers come from an incoming HTTP request, e.g. express.js -const receivedEvent = receiver.accept(req.headers, req.body); +const receivedEvent = Receiver.accept(req.headers, req.body); console.log(receivedEvent); ``` @@ -157,4 +154,4 @@ We love contributions from the community! Please check the for information on how to get involved. [v1spec]: https://github.com/cloudevents/spec/tree/v1.0 -[v103pec]: https://github.com/cloudevents/spec/tree/v0.3 \ No newline at end of file +[v103pec]: https://github.com/cloudevents/spec/tree/v0.3 diff --git a/package-lock.json b/package-lock.json index 9ca47155..bed30cc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "cloudevents-sdk", - "version": "2.0.2", + "name": "cloudevents", + "version": "3.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -9256,7 +9256,8 @@ "typedoc-clarity-theme": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/typedoc-clarity-theme/-/typedoc-clarity-theme-1.1.0.tgz", - "integrity": "sha1-ESlK3eTzQuL8ZqX6Yqb7dDjMVhw=" + "integrity": "sha1-ESlK3eTzQuL8ZqX6Yqb7dDjMVhw=", + "dev": true }, "typedoc-default-themes": { "version": "0.10.1", diff --git a/package.json b/package.json index 060ed762..5660c230 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cloudevents", - "version": "2.0.2", + "version": "3.0.1", "description": "CloudEvents SDK for JavaScript", "main": "dist/index.js", "scripts": { @@ -97,7 +97,6 @@ "dependencies": { "ajv": "~6.12.3", "axios": "~0.19.2", - "typedoc-clarity-theme": "^1.1.0", "uuid": "~8.2.0" }, "devDependencies": { @@ -129,6 +128,7 @@ "standard-version": "^8.0.1", "ts-node": "^8.10.2", "typedoc": "^0.17.7", + "typedoc-clarity-theme": "~1.1.0", "typescript": "^3.8.3", "webpack": "^4.43.0", "webpack-cli": "^3.3.11" diff --git a/src/event/cloudevent.ts b/src/event/cloudevent.ts index 4cc7fcbb..0cab0dad 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/schemas.ts b/src/event/schemas.ts index 6591e3f3..51d21e14 100644 --- a/src/event/schemas.ts +++ b/src/event/schemas.ts @@ -10,7 +10,7 @@ export const schemaV1 = { type: "string", }, data: { - type: ["object", "string"], + type: ["object", "string", "array", "number", "boolean", "null"], }, data_base64: { type: "string", @@ -89,7 +89,7 @@ export const schemaV03 = { type: "string", }, data: { - type: ["object", "string"], + type: ["object", "string", "array", "number", "boolean", "null"], }, event: { properties: { diff --git a/src/event/spec.ts b/src/event/spec.ts index 6317130f..afa75681 100644 --- a/src/event/spec.ts +++ b/src/event/spec.ts @@ -1,9 +1,7 @@ import Ajv from "ajv"; -import { v4 as uuidv4 } from "uuid"; import { ValidationError, isBase64 } from "./validation"; -import { CloudEvent } from "./cloudevent"; -import { CloudEventV1, CloudEventV1Attributes, CloudEventV03, CloudEventV03Attributes } from "./interfaces"; +import { CloudEventV1, CloudEventV03 } from "./interfaces"; import { schemaV03, schemaV1 } from "./schemas"; import CONSTANTS from "../constants"; @@ -11,16 +9,6 @@ const ajv = new Ajv({ extendRefs: true }); const isValidAgainstSchemaV1 = ajv.compile(schemaV1); const isValidAgainstSchemaV03 = ajv.compile(schemaV03); -export function createV1(attributes: CloudEventV1Attributes): CloudEventV1 { - const event: CloudEventV1 = { - specversion: schemaV1.definitions.specversion.const, - id: uuidv4(), - time: new Date().toISOString(), - ...attributes, - }; - return new CloudEvent(event); -} - export function validateV1(event: CloudEventV1): boolean { if (!isValidAgainstSchemaV1(event)) { throw new ValidationError("invalid payload", isValidAgainstSchemaV1.errors); @@ -28,16 +16,6 @@ export function validateV1(event: CloudEventV1): boolean { return true; } -export function createV03(attributes: CloudEventV03Attributes): CloudEventV03 { - const event: CloudEventV03 = { - specversion: schemaV03.definitions.specversion.const, - id: uuidv4(), - time: new Date().toISOString(), - ...attributes, - }; - return new CloudEvent(event); -} - export function validateV03(event: CloudEventV03): boolean { if (!isValidAgainstSchemaV03(event)) { throw new ValidationError("invalid payload", isValidAgainstSchemaV03.errors); diff --git a/src/event/validation.ts b/src/event/validation.ts index 56b97e5d..6ca0258d 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/src/transport/http/binary_receiver.ts b/src/transport/http/binary_receiver.ts index 91fd579d..3c6fb2c3 100644 --- a/src/transport/http/binary_receiver.ts +++ b/src/transport/http/binary_receiver.ts @@ -2,8 +2,7 @@ import { CloudEvent, Version } from "../.."; import { CloudEventV1, CloudEventV03 } from "../../event/interfaces"; import { validateV1, validateV03 } from "../../event/spec"; import { Headers, validate } from "./headers"; -import { binaryParsers as v1Parsers } from "./v1"; -import { binaryParsers as v03Parsers } from "./v03"; +import { v03binaryParsers, v1binaryParsers } from "./versions"; import { parserByContentType, MappedParser } from "../../parsers"; import { isString, isBase64, ValidationError, isStringOrObjectOrThrow } from "../../event/validation"; import CONSTANTS from "../../constants"; @@ -52,7 +51,7 @@ export class BinaryHTTPReceiver { const sanitizedHeaders = validate(headers); const eventObj: { [key: string]: unknown | string | Record } = {}; - const parserMap: Record = this.version === Version.V1 ? v1Parsers : v03Parsers; + const parserMap: Record = this.version === Version.V1 ? v1binaryParsers : v03binaryParsers; for (const header in parserMap) { if (sanitizedHeaders[header]) { diff --git a/src/transport/http/headers.ts b/src/transport/http/headers.ts index 8c800748..044aef74 100644 --- a/src/transport/http/headers.ts +++ b/src/transport/http/headers.ts @@ -1,6 +1,5 @@ import { ValidationError, CloudEvent } from "../.."; -import { headerMap as v1Map } from "./v1"; -import { headerMap as v03Map } from "./v03"; +import { v03headerMap, v1headerMap } from "./versions"; import { Version } from "../../event/cloudevent"; import { MappedParser } from "../../parsers"; import CONSTANTS from "../../constants"; @@ -60,9 +59,9 @@ export function headersFor(event: CloudEvent): Headers { const headers: Headers = {}; let headerMap: Readonly<{ [key: string]: MappedParser }>; if (event.specversion === Version.V1) { - headerMap = v1Map; + headerMap = v1headerMap; } else { - headerMap = v03Map; + headerMap = v03headerMap; } // iterate over the event properties - generate a header for each diff --git a/src/transport/http/structured_receiver.ts b/src/transport/http/structured_receiver.ts index 9a073ba5..383c5a75 100644 --- a/src/transport/http/structured_receiver.ts +++ b/src/transport/http/structured_receiver.ts @@ -2,8 +2,7 @@ import { CloudEvent, Version } from "../.."; import { Headers, sanitize } from "./headers"; import { Parser, JSONParser, MappedParser } from "../../parsers"; import { parserByContentType } from "../../parsers"; -import { structuredParsers as v1Parsers } from "./v1"; -import { structuredParsers as v03Parsers } from "./v03"; +import { v1structuredParsers, v03structuredParsers } from "./versions"; import { isString, isBase64, ValidationError, isStringOrObjectOrThrow } from "../../event/validation"; import { CloudEventV1, CloudEventV03 } from "../../event/interfaces"; import { validateV1, validateV03 } from "../../event/spec"; @@ -55,7 +54,8 @@ export class StructuredHTTPReceiver { const incoming = { ...(parser.parse(payload) as Record) }; const eventObj: { [key: string]: unknown } = {}; - const parserMap: Record = this.version === Version.V1 ? v1Parsers : v03Parsers; + const parserMap: Record = + this.version === Version.V1 ? v1structuredParsers : v03structuredParsers; for (const key in parserMap) { const property = incoming[key]; diff --git a/src/transport/http/v03.ts b/src/transport/http/v03.ts deleted file mode 100644 index 2390eadd..00000000 --- a/src/transport/http/v03.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { MappedParser, DateParser, PassThroughParser } from "../../parsers"; -import CONSTANTS from "../../constants"; - -function parser(header: string, parser = new PassThroughParser()): MappedParser { - return { name: header, parser }; -} - -/** - * A utility Map used to retrieve the header names for a CloudEvent - * using the CloudEvent getter function. - */ -export const headerMap: Readonly<{ [key: string]: MappedParser }> = Object.freeze({ - [CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE]: parser(CONSTANTS.HEADER_CONTENT_TYPE), - [CONSTANTS.CE_ATTRIBUTES.SUBJECT]: parser(CONSTANTS.CE_HEADERS.SUBJECT), - [CONSTANTS.CE_ATTRIBUTES.TYPE]: parser(CONSTANTS.CE_HEADERS.TYPE), - [CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION]: parser(CONSTANTS.CE_HEADERS.SPEC_VERSION), - [CONSTANTS.CE_ATTRIBUTES.SOURCE]: parser(CONSTANTS.CE_HEADERS.SOURCE), - [CONSTANTS.CE_ATTRIBUTES.ID]: parser(CONSTANTS.CE_HEADERS.ID), - [CONSTANTS.CE_ATTRIBUTES.TIME]: parser(CONSTANTS.CE_HEADERS.TIME), - [CONSTANTS.STRUCTURED_ATTRS_03.CONTENT_ENCODING]: parser(CONSTANTS.BINARY_HEADERS_03.CONTENT_ENCODING), - [CONSTANTS.STRUCTURED_ATTRS_03.SCHEMA_URL]: parser(CONSTANTS.BINARY_HEADERS_03.SCHEMA_URL), -}); - -export const binaryParsers: Record = Object.freeze({ - [CONSTANTS.CE_HEADERS.TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.TYPE), - [CONSTANTS.CE_HEADERS.SPEC_VERSION]: parser(CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION), - [CONSTANTS.CE_HEADERS.SOURCE]: parser(CONSTANTS.CE_ATTRIBUTES.SOURCE), - [CONSTANTS.CE_HEADERS.ID]: parser(CONSTANTS.CE_ATTRIBUTES.ID), - [CONSTANTS.CE_HEADERS.TIME]: parser(CONSTANTS.CE_ATTRIBUTES.TIME, new DateParser()), - [CONSTANTS.BINARY_HEADERS_03.SCHEMA_URL]: parser(CONSTANTS.STRUCTURED_ATTRS_03.SCHEMA_URL), - [CONSTANTS.CE_HEADERS.SUBJECT]: parser(CONSTANTS.CE_ATTRIBUTES.SUBJECT), - [CONSTANTS.BINARY_HEADERS_03.CONTENT_ENCODING]: parser(CONSTANTS.STRUCTURED_ATTRS_03.CONTENT_ENCODING), - [CONSTANTS.HEADER_CONTENT_TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE), -}); - -export const structuredParsers: Record = Object.freeze({ - [CONSTANTS.CE_ATTRIBUTES.TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.TYPE), - [CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION]: parser(CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION), - [CONSTANTS.CE_ATTRIBUTES.SOURCE]: parser(CONSTANTS.CE_ATTRIBUTES.SOURCE), - [CONSTANTS.CE_ATTRIBUTES.ID]: parser(CONSTANTS.CE_ATTRIBUTES.ID), - [CONSTANTS.CE_ATTRIBUTES.TIME]: parser(CONSTANTS.CE_ATTRIBUTES.TIME, new DateParser()), - [CONSTANTS.STRUCTURED_ATTRS_03.SCHEMA_URL]: parser(CONSTANTS.STRUCTURED_ATTRS_03.SCHEMA_URL), - [CONSTANTS.STRUCTURED_ATTRS_03.CONTENT_ENCODING]: parser(CONSTANTS.STRUCTURED_ATTRS_03.CONTENT_ENCODING), - [CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE), - [CONSTANTS.CE_ATTRIBUTES.SUBJECT]: parser(CONSTANTS.CE_ATTRIBUTES.SUBJECT), - [CONSTANTS.CE_ATTRIBUTES.DATA]: parser(CONSTANTS.CE_ATTRIBUTES.DATA), -}); diff --git a/src/transport/http/v1.ts b/src/transport/http/v1.ts deleted file mode 100644 index 4176c1ad..00000000 --- a/src/transport/http/v1.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { PassThroughParser, DateParser, MappedParser } from "../../parsers"; -import CONSTANTS from "../../constants"; - -function parser(name: string, parser = new PassThroughParser()): MappedParser { - return { name: name, parser: parser }; -} - -/** - * A utility Map used to retrieve the header names for a CloudEvent - * using the CloudEvent getter function. - */ -export const headerMap: Readonly<{ [key: string]: MappedParser }> = Object.freeze({ - [CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE]: parser(CONSTANTS.HEADER_CONTENT_TYPE), - [CONSTANTS.CE_ATTRIBUTES.SUBJECT]: parser(CONSTANTS.CE_HEADERS.SUBJECT), - [CONSTANTS.CE_ATTRIBUTES.TYPE]: parser(CONSTANTS.CE_HEADERS.TYPE), - [CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION]: parser(CONSTANTS.CE_HEADERS.SPEC_VERSION), - [CONSTANTS.CE_ATTRIBUTES.SOURCE]: parser(CONSTANTS.CE_HEADERS.SOURCE), - [CONSTANTS.CE_ATTRIBUTES.ID]: parser(CONSTANTS.CE_HEADERS.ID), - [CONSTANTS.CE_ATTRIBUTES.TIME]: parser(CONSTANTS.CE_HEADERS.TIME), - [CONSTANTS.STRUCTURED_ATTRS_1.DATA_SCHEMA]: parser(CONSTANTS.BINARY_HEADERS_1.DATA_SCHEMA), -}); - -export const binaryParsers: Record = Object.freeze({ - [CONSTANTS.CE_HEADERS.TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.TYPE), - [CONSTANTS.CE_HEADERS.SPEC_VERSION]: parser(CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION), - [CONSTANTS.CE_HEADERS.SOURCE]: parser(CONSTANTS.CE_ATTRIBUTES.SOURCE), - [CONSTANTS.CE_HEADERS.ID]: parser(CONSTANTS.CE_ATTRIBUTES.ID), - [CONSTANTS.CE_HEADERS.TIME]: parser(CONSTANTS.CE_ATTRIBUTES.TIME, new DateParser()), - [CONSTANTS.BINARY_HEADERS_1.DATA_SCHEMA]: parser(CONSTANTS.STRUCTURED_ATTRS_1.DATA_SCHEMA), - [CONSTANTS.CE_HEADERS.SUBJECT]: parser(CONSTANTS.CE_ATTRIBUTES.SUBJECT), - [CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE), - [CONSTANTS.HEADER_CONTENT_TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE), -}); - -export const structuredParsers: Record = Object.freeze({ - [CONSTANTS.CE_ATTRIBUTES.TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.TYPE), - [CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION]: parser(CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION), - [CONSTANTS.CE_ATTRIBUTES.SOURCE]: parser(CONSTANTS.CE_ATTRIBUTES.SOURCE), - [CONSTANTS.CE_ATTRIBUTES.ID]: parser(CONSTANTS.CE_ATTRIBUTES.ID), - [CONSTANTS.CE_ATTRIBUTES.TIME]: parser(CONSTANTS.CE_ATTRIBUTES.TIME, new DateParser()), - [CONSTANTS.STRUCTURED_ATTRS_1.DATA_SCHEMA]: parser(CONSTANTS.STRUCTURED_ATTRS_1.DATA_SCHEMA), - [CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE), - [CONSTANTS.CE_ATTRIBUTES.SUBJECT]: parser(CONSTANTS.CE_ATTRIBUTES.SUBJECT), - [CONSTANTS.CE_ATTRIBUTES.DATA]: parser(CONSTANTS.CE_ATTRIBUTES.DATA), - [CONSTANTS.STRUCTURED_ATTRS_1.DATA_BASE64]: parser(CONSTANTS.STRUCTURED_ATTRS_1.DATA_BASE64), -}); diff --git a/src/transport/http/versions.ts b/src/transport/http/versions.ts new file mode 100644 index 00000000..70ddc560 --- /dev/null +++ b/src/transport/http/versions.ts @@ -0,0 +1,87 @@ +import { PassThroughParser, DateParser, MappedParser } from "../../parsers"; +import CONSTANTS from "../../constants"; + +function parser(name: string, parser = new PassThroughParser()): MappedParser { + return { name: name, parser: parser }; +} + +/** + * A utility Map used to retrieve the header names for a CloudEvent + * using the CloudEvent getter function. + */ +export const v1headerMap: Readonly<{ [key: string]: MappedParser }> = Object.freeze({ + [CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE]: parser(CONSTANTS.HEADER_CONTENT_TYPE), + [CONSTANTS.CE_ATTRIBUTES.SUBJECT]: parser(CONSTANTS.CE_HEADERS.SUBJECT), + [CONSTANTS.CE_ATTRIBUTES.TYPE]: parser(CONSTANTS.CE_HEADERS.TYPE), + [CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION]: parser(CONSTANTS.CE_HEADERS.SPEC_VERSION), + [CONSTANTS.CE_ATTRIBUTES.SOURCE]: parser(CONSTANTS.CE_HEADERS.SOURCE), + [CONSTANTS.CE_ATTRIBUTES.ID]: parser(CONSTANTS.CE_HEADERS.ID), + [CONSTANTS.CE_ATTRIBUTES.TIME]: parser(CONSTANTS.CE_HEADERS.TIME), + [CONSTANTS.STRUCTURED_ATTRS_1.DATA_SCHEMA]: parser(CONSTANTS.BINARY_HEADERS_1.DATA_SCHEMA), +}); + +export const v1binaryParsers: Record = Object.freeze({ + [CONSTANTS.CE_HEADERS.TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.TYPE), + [CONSTANTS.CE_HEADERS.SPEC_VERSION]: parser(CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION), + [CONSTANTS.CE_HEADERS.SOURCE]: parser(CONSTANTS.CE_ATTRIBUTES.SOURCE), + [CONSTANTS.CE_HEADERS.ID]: parser(CONSTANTS.CE_ATTRIBUTES.ID), + [CONSTANTS.CE_HEADERS.TIME]: parser(CONSTANTS.CE_ATTRIBUTES.TIME, new DateParser()), + [CONSTANTS.BINARY_HEADERS_1.DATA_SCHEMA]: parser(CONSTANTS.STRUCTURED_ATTRS_1.DATA_SCHEMA), + [CONSTANTS.CE_HEADERS.SUBJECT]: parser(CONSTANTS.CE_ATTRIBUTES.SUBJECT), + [CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE), + [CONSTANTS.HEADER_CONTENT_TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE), +}); + +export const v1structuredParsers: Record = Object.freeze({ + [CONSTANTS.CE_ATTRIBUTES.TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.TYPE), + [CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION]: parser(CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION), + [CONSTANTS.CE_ATTRIBUTES.SOURCE]: parser(CONSTANTS.CE_ATTRIBUTES.SOURCE), + [CONSTANTS.CE_ATTRIBUTES.ID]: parser(CONSTANTS.CE_ATTRIBUTES.ID), + [CONSTANTS.CE_ATTRIBUTES.TIME]: parser(CONSTANTS.CE_ATTRIBUTES.TIME, new DateParser()), + [CONSTANTS.STRUCTURED_ATTRS_1.DATA_SCHEMA]: parser(CONSTANTS.STRUCTURED_ATTRS_1.DATA_SCHEMA), + [CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE), + [CONSTANTS.CE_ATTRIBUTES.SUBJECT]: parser(CONSTANTS.CE_ATTRIBUTES.SUBJECT), + [CONSTANTS.CE_ATTRIBUTES.DATA]: parser(CONSTANTS.CE_ATTRIBUTES.DATA), + [CONSTANTS.STRUCTURED_ATTRS_1.DATA_BASE64]: parser(CONSTANTS.STRUCTURED_ATTRS_1.DATA_BASE64), +}); + +/** + * A utility Map used to retrieve the header names for a CloudEvent + * using the CloudEvent getter function. + */ +export const v03headerMap: Readonly<{ [key: string]: MappedParser }> = Object.freeze({ + [CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE]: parser(CONSTANTS.HEADER_CONTENT_TYPE), + [CONSTANTS.CE_ATTRIBUTES.SUBJECT]: parser(CONSTANTS.CE_HEADERS.SUBJECT), + [CONSTANTS.CE_ATTRIBUTES.TYPE]: parser(CONSTANTS.CE_HEADERS.TYPE), + [CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION]: parser(CONSTANTS.CE_HEADERS.SPEC_VERSION), + [CONSTANTS.CE_ATTRIBUTES.SOURCE]: parser(CONSTANTS.CE_HEADERS.SOURCE), + [CONSTANTS.CE_ATTRIBUTES.ID]: parser(CONSTANTS.CE_HEADERS.ID), + [CONSTANTS.CE_ATTRIBUTES.TIME]: parser(CONSTANTS.CE_HEADERS.TIME), + [CONSTANTS.STRUCTURED_ATTRS_03.CONTENT_ENCODING]: parser(CONSTANTS.BINARY_HEADERS_03.CONTENT_ENCODING), + [CONSTANTS.STRUCTURED_ATTRS_03.SCHEMA_URL]: parser(CONSTANTS.BINARY_HEADERS_03.SCHEMA_URL), +}); + +export const v03binaryParsers: Record = Object.freeze({ + [CONSTANTS.CE_HEADERS.TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.TYPE), + [CONSTANTS.CE_HEADERS.SPEC_VERSION]: parser(CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION), + [CONSTANTS.CE_HEADERS.SOURCE]: parser(CONSTANTS.CE_ATTRIBUTES.SOURCE), + [CONSTANTS.CE_HEADERS.ID]: parser(CONSTANTS.CE_ATTRIBUTES.ID), + [CONSTANTS.CE_HEADERS.TIME]: parser(CONSTANTS.CE_ATTRIBUTES.TIME, new DateParser()), + [CONSTANTS.BINARY_HEADERS_03.SCHEMA_URL]: parser(CONSTANTS.STRUCTURED_ATTRS_03.SCHEMA_URL), + [CONSTANTS.CE_HEADERS.SUBJECT]: parser(CONSTANTS.CE_ATTRIBUTES.SUBJECT), + [CONSTANTS.BINARY_HEADERS_03.CONTENT_ENCODING]: parser(CONSTANTS.STRUCTURED_ATTRS_03.CONTENT_ENCODING), + [CONSTANTS.HEADER_CONTENT_TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE), +}); + +export const v03structuredParsers: Record = Object.freeze({ + [CONSTANTS.CE_ATTRIBUTES.TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.TYPE), + [CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION]: parser(CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION), + [CONSTANTS.CE_ATTRIBUTES.SOURCE]: parser(CONSTANTS.CE_ATTRIBUTES.SOURCE), + [CONSTANTS.CE_ATTRIBUTES.ID]: parser(CONSTANTS.CE_ATTRIBUTES.ID), + [CONSTANTS.CE_ATTRIBUTES.TIME]: parser(CONSTANTS.CE_ATTRIBUTES.TIME, new DateParser()), + [CONSTANTS.STRUCTURED_ATTRS_03.SCHEMA_URL]: parser(CONSTANTS.STRUCTURED_ATTRS_03.SCHEMA_URL), + [CONSTANTS.STRUCTURED_ATTRS_03.CONTENT_ENCODING]: parser(CONSTANTS.STRUCTURED_ATTRS_03.CONTENT_ENCODING), + [CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE]: parser(CONSTANTS.CE_ATTRIBUTES.CONTENT_TYPE), + [CONSTANTS.CE_ATTRIBUTES.SUBJECT]: parser(CONSTANTS.CE_ATTRIBUTES.SUBJECT), + [CONSTANTS.CE_ATTRIBUTES.DATA]: parser(CONSTANTS.CE_ATTRIBUTES.DATA), +}); diff --git a/src/transport/index.ts b/src/transport/index.ts index 8a077285..24c5755d 100644 --- a/src/transport/index.ts +++ b/src/transport/index.ts @@ -1,6 +1,4 @@ export * from "./emitter"; export * from "./receiver"; -export * as v1Headers from "./http/v1"; -export * as v03Headers from "./http/v03"; export * as http from "./http/headers"; export * from "./protocols"; diff --git a/test/integration/cloud_event_test.ts b/test/integration/cloud_event_test.ts index 7de5da77..de733c66 100644 --- a/test/integration/cloud_event_test.ts +++ b/test/integration/cloud_event_test.ts @@ -101,6 +101,38 @@ describe("A 1.0 CloudEvent", () => { expect(ce.data).to.deep.equal({ lunch: "tacos" }); }); + it("can be constructed with data as an Array", () => { + const ce = new CloudEvent({ + ...fixture, + data: [{ lunch: "tacos" }, { supper: "sushi" }], + }); + expect(ce.data).to.deep.equal([{ lunch: "tacos" }, { supper: "sushi" }]); + }); + + it("can be constructed with data as a number", () => { + const ce = new CloudEvent({ + ...fixture, + data: 100, + }); + expect(ce.data).to.equal(100); + }); + + it("can be constructed with null data", () => { + const ce = new CloudEvent({ + ...fixture, + data: null, + }); + expect(ce.data).to.equal(null); + }); + + it("can be constructed with data as a boolean", () => { + const ce = new CloudEvent({ + ...fixture, + data: true, + }); + expect(ce.data).to.be.true; + }); + it("can be constructed with extensions", () => { const extensions = { extensionkey: "extension-value", diff --git a/test/integration/http_emitter_test.ts b/test/integration/http_emitter_test.ts index 1644af9c..b5f211be 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 d5802832..e39d3a56 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", () => {