Compare commits
83 Commits
Author | SHA1 | Date |
---|---|---|
|
3c8819e2bc | |
|
beac7356a7 | |
|
fc760976bf | |
|
3ff6fdd3bf | |
|
c07afa9b77 | |
|
f475cdfd7e | |
|
8357719bab | |
|
6977113d7b | |
|
154e913717 | |
|
e7626077ed | |
|
c65afe94d2 | |
|
245bae92d1 | |
|
f93f896002 | |
|
16d0c0f91f | |
|
db60220762 | |
|
1ed43c8486 | |
|
df059e9696 | |
|
15f6505a58 | |
|
089520a4cc | |
|
fa388f7dc6 | |
|
0d923a4f92 | |
|
a0d8682613 | |
|
023171d9a0 | |
|
3d961371da | |
|
f3659ebfc6 | |
|
11442d32d3 | |
|
3931b224cb | |
|
43c3584b98 | |
|
1995b5778e | |
|
0120d224ab | |
|
2cb9364a25 | |
|
4626529d56 | |
|
ec83abc827 | |
|
1cf8f8acae | |
|
343382ebde | |
|
c06ffc1963 | |
|
7ff64f8b82 | |
|
870d2118cd | |
|
78b8e0a372 | |
|
953bc2a143 | |
|
bc3aaca2ef | |
|
e5ee8369ba | |
|
b374d9ac33 | |
|
64e527c120 | |
|
1b449c4c9a | |
|
eccc00ee67 | |
|
94f1a3d470 | |
|
3619ef2bbd | |
|
2d5fab1b71 | |
|
c09a9cc20a | |
|
4831e6a1a5 | |
|
760a024067 | |
|
c282922ef9 | |
|
ea94a4d779 | |
|
847f6bfcc7 | |
|
ed63f14339 | |
|
921e273ede | |
|
a62eb44669 | |
|
d6f52ca65f | |
|
ce02e0a1f3 | |
|
d9ee0e05d1 | |
|
addbd9acf1 | |
|
a512aad5d5 | |
|
c0b1f7705a | |
|
0164f72eaa | |
|
4ab6356bd7 | |
|
0362a4f11c | |
|
b4d7aa9adb | |
|
6204805bfc | |
|
ae8cb96f8a | |
|
c420da4793 | |
|
b13bde9b49 | |
|
4d8f03f7c6 | |
|
9046b369cf | |
|
c3d9f39a53 | |
|
b5c0b56f52 | |
|
f36a1f0428 | |
|
cd4dea954b | |
|
8abbc114af | |
|
349b84c3da | |
|
c603831e93 | |
|
ae8fa799af | |
|
225836f68f |
|
@ -15,6 +15,7 @@
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"header"
|
"header"
|
||||||
],
|
],
|
||||||
|
"ignorePatterns": ["**/schema/*"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-var": "error",
|
"no-var": "error",
|
||||||
"standard/no-callback-literal": "off",
|
"standard/no-callback-literal": "off",
|
||||||
|
|
|
@ -8,10 +8,10 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
-
|
-
|
||||||
name: Generate API documentation
|
name: Generate API documentation
|
||||||
run: npm install && npm run generate-docs
|
run: npm install && npm run build:schema && npm run generate-docs
|
||||||
-
|
-
|
||||||
name: Deploy to GitHub Pages
|
name: Deploy to GitHub Pages
|
||||||
if: success()
|
if: success()
|
||||||
|
|
|
@ -15,12 +15,12 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [12.x, 14.x]
|
node-version: [20.x, 22.x, 24.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Test on Node.js ${{ matrix.node-version }}
|
- name: Test on Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
|
@ -31,18 +31,18 @@ jobs:
|
||||||
name: Code coverage
|
name: Code coverage
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Generate coverage report
|
- name: Generate coverage report
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 14.x
|
node-version: 22.x
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run build --if-present
|
- run: npm run build --if-present
|
||||||
- run: npm run coverage
|
- run: npm run coverage
|
||||||
- name: Upload coverage report to storage
|
- name: Upload coverage report to storage
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: coverage
|
name: coverage
|
||||||
path: coverage/lcov.info
|
path: coverage/lcov.info
|
||||||
|
@ -52,15 +52,15 @@ jobs:
|
||||||
needs: coverage
|
needs: coverage
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Download coverage report from storage
|
- name: Download coverage report from storage
|
||||||
uses: actions/download-artifact@v1
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: coverage
|
name: coverage
|
||||||
- name: Upload coverage report to codacy
|
- name: Upload coverage report to codacy
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 14.x
|
node-version: 22.x
|
||||||
- run: |
|
- run: |
|
||||||
( [[ "${CODACY_PROJECT_TOKEN}" != "" ]] && npm run coverage-publish ) || echo "Coverage report not published"
|
( [[ "${CODACY_PROJECT_TOKEN}" != "" ]] && npm run coverage-publish ) || echo "Coverage report not published"
|
||||||
env:
|
env:
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
name: Publish to npmjs
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [created]
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '22.x'
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
- run: npm install -g npm
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm publish --provenance --access public
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.CLOUDEVENTS_PUBLISH }}
|
|
@ -7,9 +7,11 @@ jobs:
|
||||||
release-please:
|
release-please:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: GoogleCloudPlatform/release-please-action@v2.5.5
|
- uses: GoogleCloudPlatform/release-please-action@v3
|
||||||
|
id: release
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.CLOUDEVENTS_RELEASES_TOKEN }}
|
||||||
release-type: node
|
release-type: node
|
||||||
package-name: cloudevents
|
package-name: cloudevents
|
||||||
|
signoff: "Lucas Holmquist <lholmqui@redhat.com>"
|
||||||
changelog-types: '[{"type":"feat","section":"Features","hidden":false},{"type":"fix","section":"Bug Fixes","hidden":false},{"type":"docs","section":"Documentation","hidden":false},{"type":"chore","section":"Miscellaneous","hidden":false},{"type":"src","section":"Miscellaneous","hidden":false},{"type":"style","section":"Miscellaneous","hidden":false},{"type":"refactor","section":"Miscellaneous","hidden":false},{"type":"perf","section":"Performance","hidden":false},{"type":"test","section":"Tests","hidden":false}]'
|
changelog-types: '[{"type":"feat","section":"Features","hidden":false},{"type":"fix","section":"Bug Fixes","hidden":false},{"type":"docs","section":"Documentation","hidden":false},{"type":"chore","section":"Miscellaneous","hidden":false},{"type":"src","section":"Miscellaneous","hidden":false},{"type":"style","section":"Miscellaneous","hidden":false},{"type":"refactor","section":"Miscellaneous","hidden":false},{"type":"perf","section":"Performance","hidden":false},{"type":"test","section":"Tests","hidden":false}]'
|
||||||
|
|
|
@ -13,6 +13,7 @@ index.js
|
||||||
/bundles
|
/bundles
|
||||||
/dist
|
/dist
|
||||||
/docs
|
/docs
|
||||||
|
src/schema/v1.js
|
||||||
|
|
||||||
# Runtime data
|
# Runtime data
|
||||||
pids
|
pids
|
||||||
|
@ -90,3 +91,6 @@ typings/
|
||||||
|
|
||||||
# Package lock
|
# Package lock
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
|
# Jetbrains IDE directories
|
||||||
|
.idea
|
||||||
|
|
209
CHANGELOG.md
209
CHANGELOG.md
|
@ -2,6 +2,215 @@
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
## [9.0.0](https://github.com/cloudevents/sdk-javascript/compare/v8.0.3...v9.0.0) (2025-04-03)
|
||||||
|
|
||||||
|
|
||||||
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
|
* remove node 16 ([#610](https://github.com/cloudevents/sdk-javascript/issues/610))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* remove node 16 ([#610](https://github.com/cloudevents/sdk-javascript/issues/610)) ([3ff6fdd](https://github.com/cloudevents/sdk-javascript/commit/3ff6fdd3bf1a9d77be9cd1d1ed589de47a86f7c1))
|
||||||
|
|
||||||
|
## [8.0.3](https://github.com/cloudevents/sdk-javascript/compare/v8.0.2...v8.0.3) (2025-04-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add generics to `Binding` type ([#604](https://github.com/cloudevents/sdk-javascript/issues/604)) ([f475cdf](https://github.com/cloudevents/sdk-javascript/commit/f475cdfd7ee7e80a375b997ba2f41b1655a44a03))
|
||||||
|
|
||||||
|
## [8.0.2](https://github.com/cloudevents/sdk-javascript/compare/v8.0.1...v8.0.2) (2024-07-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* creating an event does not error when the event attribute name is too long ([#593](https://github.com/cloudevents/sdk-javascript/issues/593)) ([6977113](https://github.com/cloudevents/sdk-javascript/commit/6977113d7b49bd2b702632cc09e29cc0c003e2a1))
|
||||||
|
|
||||||
|
## [8.0.1](https://github.com/cloudevents/sdk-javascript/compare/v8.0.0...v8.0.1) (2024-06-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* allow Node 22 and use it by default ([#587](https://github.com/cloudevents/sdk-javascript/issues/587)) ([e762607](https://github.com/cloudevents/sdk-javascript/commit/e7626077ed22b2bcbfa71b0403a58ac187c57cba))
|
||||||
|
|
||||||
|
|
||||||
|
### Miscellaneous
|
||||||
|
|
||||||
|
* Update compatible node version ([#573](https://github.com/cloudevents/sdk-javascript/issues/573)) ([245bae9](https://github.com/cloudevents/sdk-javascript/commit/245bae92d1c84b4a44fe7aae2f82c5a90818f1c5))
|
||||||
|
* updated check mark symbol to show some green checkboxes ([#582](https://github.com/cloudevents/sdk-javascript/issues/582)) ([c65afe9](https://github.com/cloudevents/sdk-javascript/commit/c65afe94d2320eae9b8b74de9b1e1bd8793baa6a))
|
||||||
|
|
||||||
|
## [8.0.0](https://github.com/cloudevents/sdk-javascript/compare/v7.0.2...v8.0.0) (2023-07-24)
|
||||||
|
|
||||||
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
|
* use string instead of enum for Version ([#561](https://github.com/cloudevents/sdk-javascript/issues/561)) ([15f6505](https://github.com/cloudevents/sdk-javascript/commit/15f6505a580b2bbf8d6b2e89feea10cbd40ab827))
|
||||||
|
TypeScript does not consider enum values equivalent, even if the string
|
||||||
|
representation is the same. So, when a module imports `cloudevents` and
|
||||||
|
also has a dependency on `cloudevents` this can cause conflicts where
|
||||||
|
the `CloudEvent.version` attribute is not considered equal when, in
|
||||||
|
fact, it is.
|
||||||
|
|
||||||
|
### Miscellaneous
|
||||||
|
|
||||||
|
* add `npm run build:schema` to the doc generation action ([#557](https://github.com/cloudevents/sdk-javascript/issues/557)) ([fa388f7](https://github.com/cloudevents/sdk-javascript/commit/fa388f7dc65c1739864d7a885d6d28111ce07775))
|
||||||
|
* modify release-please to use Signed-Off-By on commits ([#559](https://github.com/cloudevents/sdk-javascript/issues/559)) ([089520a](https://github.com/cloudevents/sdk-javascript/commit/089520a4cc8304e39ac9bfccf0ed59c76ea8c11a))
|
||||||
|
* release 8.0.0 ([#563](https://github.com/cloudevents/sdk-javascript/issues/563)) ([1ed43c8](https://github.com/cloudevents/sdk-javascript/commit/1ed43c84868ccfd18531deaf6cc9d4e4fcb21a08))
|
||||||
|
|
||||||
|
## [7.0.2](https://github.com/cloudevents/sdk-javascript/compare/v7.0.1...v7.0.2) (2023-07-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Miscellaneous
|
||||||
|
|
||||||
|
* add the provenance flag when publishing to npm ([#556](https://github.com/cloudevents/sdk-javascript/issues/556)) ([a0d8682](https://github.com/cloudevents/sdk-javascript/commit/a0d86826138be31072c9a30edf26f4b91da576ed))
|
||||||
|
* fix the release-please automation script. ([#554](https://github.com/cloudevents/sdk-javascript/issues/554)) ([023171d](https://github.com/cloudevents/sdk-javascript/commit/023171d9a08484c32e24f8228602ef4d5173c749))
|
||||||
|
|
||||||
|
## [7.0.1](https://github.com/cloudevents/sdk-javascript/compare/v7.0.0...v7.0.1) (2023-05-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* handle big integers in incoming events ([#495](https://github.com/cloudevents/sdk-javascript/issues/495)) ([43c3584](https://github.com/cloudevents/sdk-javascript/commit/43c3584b984aa170b1c1c4dff7218d027cd28d02))
|
||||||
|
|
||||||
|
|
||||||
|
### Miscellaneous
|
||||||
|
|
||||||
|
* add publish automation ([#550](https://github.com/cloudevents/sdk-javascript/issues/550)) ([3931b22](https://github.com/cloudevents/sdk-javascript/commit/3931b224cb3140ad3ba759edcf564621a2e34542))
|
||||||
|
* remove old Node versions from the readme ([#549](https://github.com/cloudevents/sdk-javascript/issues/549)) ([11442d3](https://github.com/cloudevents/sdk-javascript/commit/11442d32d307a0e8416ed573ce34fc825d3b63c4))
|
||||||
|
* Update compatible node version ([#552](https://github.com/cloudevents/sdk-javascript/issues/552)) ([f3659eb](https://github.com/cloudevents/sdk-javascript/commit/f3659ebfc6251b4c57997f70709688916a061a2d))
|
||||||
|
|
||||||
|
## [7.0.0](https://github.com/cloudevents/sdk-javascript/compare/v6.0.4...v7.0.0) (2023-05-03)
|
||||||
|
|
||||||
|
|
||||||
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
|
* remove node 12 and node 14 ([#545](https://github.com/cloudevents/sdk-javascript/issues/545))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* remove node 12 and node 14 ([#545](https://github.com/cloudevents/sdk-javascript/issues/545)) ([2cb9364](https://github.com/cloudevents/sdk-javascript/commit/2cb9364a25a5e82f2a68504dbe19839a7fbfd9d4))
|
||||||
|
|
||||||
|
|
||||||
|
### Miscellaneous
|
||||||
|
|
||||||
|
* add the build script to the pretest script. ([#539](https://github.com/cloudevents/sdk-javascript/issues/539)) ([c06ffc1](https://github.com/cloudevents/sdk-javascript/commit/c06ffc196389fedd7d5141d69fac3f4d95156193))
|
||||||
|
* fix release-please-action ([#543](https://github.com/cloudevents/sdk-javascript/issues/543)) ([ec83abc](https://github.com/cloudevents/sdk-javascript/commit/ec83abc82799159aa1f64c791c92e035ef6f42b8))
|
||||||
|
* release 6.0.5 ([#542](https://github.com/cloudevents/sdk-javascript/issues/542)) ([343382e](https://github.com/cloudevents/sdk-javascript/commit/343382ebdedc9a2efbc5b3ba5cd36e4e069fd38f))
|
||||||
|
* release 7.0.0 ([#546](https://github.com/cloudevents/sdk-javascript/issues/546)) ([0120d22](https://github.com/cloudevents/sdk-javascript/commit/0120d224ab67e804e201625e0a9d59947a5a212d))
|
||||||
|
* Update CI action to node 18.x ([#533](https://github.com/cloudevents/sdk-javascript/issues/533)) ([7ff64f8](https://github.com/cloudevents/sdk-javascript/commit/7ff64f8b82e1c5a824bbe985df4948d79e919e8c))
|
||||||
|
|
||||||
|
### [6.0.3](https://www.github.com/cloudevents/sdk-javascript/compare/v6.0.2...v6.0.3) (2023-02-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* improve validation on extension attribute ([#502](https://www.github.com/cloudevents/sdk-javascript/issues/502)) ([ea94a4d](https://www.github.com/cloudevents/sdk-javascript/commit/ea94a4d779d0744ef40abc81d08ab8b7e93e9133))
|
||||||
|
* Make CloudEvent data field immutable and enumerable using Object.keys() ([#515](https://www.github.com/cloudevents/sdk-javascript/issues/515)) ([#516](https://www.github.com/cloudevents/sdk-javascript/issues/516)) ([2d5fab1](https://www.github.com/cloudevents/sdk-javascript/commit/2d5fab1b7133241493bb9327aa26e7de4117616d))
|
||||||
|
* This fixes bug [#525](https://www.github.com/cloudevents/sdk-javascript/issues/525) where the browser version was breaking becuase of process not being found. ([#526](https://www.github.com/cloudevents/sdk-javascript/issues/526)) ([e5ee836](https://www.github.com/cloudevents/sdk-javascript/commit/e5ee8369ba5838aa24c2d99efeb81788757b71d1))
|
||||||
|
|
||||||
|
|
||||||
|
### Miscellaneous
|
||||||
|
|
||||||
|
* added the engines property to the package.json ([bc3aaca](https://www.github.com/cloudevents/sdk-javascript/commit/bc3aaca2ef250e4acd72b909488b326233237c83))
|
||||||
|
* bump cucumber to full release version ([#514](https://www.github.com/cloudevents/sdk-javascript/issues/514)) ([c09a9cc](https://www.github.com/cloudevents/sdk-javascript/commit/c09a9cc20a601ddc36c5c1b56fb52dc9c2161e1b))
|
||||||
|
* bump mocha to 10.1.0 ([#512](https://www.github.com/cloudevents/sdk-javascript/issues/512)) ([4831e6a](https://www.github.com/cloudevents/sdk-javascript/commit/4831e6a1a5003c4c1c7bcbd5a3a2fc5c48e0ba4c))
|
||||||
|
* bump webpack to 5.74.0 ([#509](https://www.github.com/cloudevents/sdk-javascript/issues/509)) ([760a024](https://www.github.com/cloudevents/sdk-javascript/commit/760a0240674c79ca6be142ae9f9b242080c4d59d))
|
||||||
|
* release 6.0.3 ([#503](https://www.github.com/cloudevents/sdk-javascript/issues/503)) ([3619ef2](https://www.github.com/cloudevents/sdk-javascript/commit/3619ef2bbd6e2b3e9e6e5bb5ad904689d40f4b79))
|
||||||
|
* Typos ([953bc2a](https://www.github.com/cloudevents/sdk-javascript/commit/953bc2a143a66d04d850c727305a5a465e843bff))
|
||||||
|
* **examples:** add mqtt example ([#523](https://www.github.com/cloudevents/sdk-javascript/issues/523)) ([b374d9a](https://www.github.com/cloudevents/sdk-javascript/commit/b374d9ac3313023e4f8a59cb22785751bbb0f686))
|
||||||
|
|
||||||
|
### [6.0.3](https://www.github.com/cloudevents/sdk-javascript/compare/v6.0.2...v6.0.3) (2022-11-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* improve validation on extension attribute ([#502](https://www.github.com/cloudevents/sdk-javascript/issues/502)) ([ea94a4d](https://www.github.com/cloudevents/sdk-javascript/commit/ea94a4d779d0744ef40abc81d08ab8b7e93e9133))
|
||||||
|
* Make CloudEvent data field immutable and enumerable using Object.keys() ([#515](https://www.github.com/cloudevents/sdk-javascript/issues/515)) ([#516](https://www.github.com/cloudevents/sdk-javascript/issues/516)) ([2d5fab1](https://www.github.com/cloudevents/sdk-javascript/commit/2d5fab1b7133241493bb9327aa26e7de4117616d))
|
||||||
|
|
||||||
|
|
||||||
|
### Miscellaneous
|
||||||
|
|
||||||
|
* bump cucumber to full release version ([#514](https://www.github.com/cloudevents/sdk-javascript/issues/514)) ([c09a9cc](https://www.github.com/cloudevents/sdk-javascript/commit/c09a9cc20a601ddc36c5c1b56fb52dc9c2161e1b))
|
||||||
|
* bump mocha to 10.1.0 ([#512](https://www.github.com/cloudevents/sdk-javascript/issues/512)) ([4831e6a](https://www.github.com/cloudevents/sdk-javascript/commit/4831e6a1a5003c4c1c7bcbd5a3a2fc5c48e0ba4c))
|
||||||
|
* bump webpack to 5.74.0 ([#509](https://www.github.com/cloudevents/sdk-javascript/issues/509)) ([760a024](https://www.github.com/cloudevents/sdk-javascript/commit/760a0240674c79ca6be142ae9f9b242080c4d59d))
|
||||||
|
|
||||||
|
### [6.0.2](https://www.github.com/cloudevents/sdk-javascript/compare/v6.0.1...v6.0.2) (2022-06-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* allow `TypedArray` for binary data ([#494](https://www.github.com/cloudevents/sdk-javascript/issues/494)) ([921e273](https://www.github.com/cloudevents/sdk-javascript/commit/921e273ede100ab9a262fdfa1f3d6561d3fab0f9))
|
||||||
|
* HTTP headers for extensions with false values ([#493](https://www.github.com/cloudevents/sdk-javascript/issues/493)) ([d6f52ca](https://www.github.com/cloudevents/sdk-javascript/commit/d6f52ca65f893fdb581bf06b2ff97b3d6eeeb744))
|
||||||
|
* package.json & package-lock.json to reduce vulnerabilities ([ed63f14](https://www.github.com/cloudevents/sdk-javascript/commit/ed63f14339fb7774bff865726370fe72a49abca3))
|
||||||
|
|
||||||
|
|
||||||
|
### Miscellaneous
|
||||||
|
|
||||||
|
* bump ajv and remove old dep dependency ([#496](https://www.github.com/cloudevents/sdk-javascript/issues/496)) ([ce02e0a](https://www.github.com/cloudevents/sdk-javascript/commit/ce02e0a1f3b24624bd8ba443c744b4a6c0cfcb44))
|
||||||
|
* update owners ([#499](https://www.github.com/cloudevents/sdk-javascript/issues/499)) ([a62eb44](https://www.github.com/cloudevents/sdk-javascript/commit/a62eb4466985972cd3112e6f8e3e0b62cb01c1c1))
|
||||||
|
|
||||||
|
### [6.0.1](https://www.github.com/cloudevents/sdk-javascript/compare/v6.0.0...v6.0.1) (2022-03-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Miscellaneous
|
||||||
|
|
||||||
|
* update dependencies to inlude ajv-formats ([#484](https://www.github.com/cloudevents/sdk-javascript/issues/484)) ([c0b1f77](https://www.github.com/cloudevents/sdk-javascript/commit/c0b1f7705a448dda3e6292d872a5bf435d26fab4)), closes [/github.com/cloudevents/sdk-javascript/pull/471/files#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519R128](https://www.github.com/cloudevents//github.com/cloudevents/sdk-javascript/pull/471/files/issues/diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519R128)
|
||||||
|
|
||||||
|
## [6.0.0](https://www.github.com/cloudevents/sdk-javascript/compare/v5.3.2...v6.0.0) (2022-03-21)
|
||||||
|
|
||||||
|
|
||||||
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
|
* add http transport and remove axios (#481)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add http transport and remove axios ([#481](https://www.github.com/cloudevents/sdk-javascript/issues/481)) ([0362a4f](https://www.github.com/cloudevents/sdk-javascript/commit/0362a4f11c7bdc74a3a9a05b5bb4a94516b15a44))
|
||||||
|
* precompile cloudevent schema ([#471](https://www.github.com/cloudevents/sdk-javascript/issues/471)) ([b13bde9](https://www.github.com/cloudevents/sdk-javascript/commit/b13bde9b4967f5c8b02b788a40a89dd4cec5b78a))
|
||||||
|
|
||||||
|
|
||||||
|
### Miscellaneous
|
||||||
|
|
||||||
|
* add an npm test:once script ([#480](https://www.github.com/cloudevents/sdk-javascript/issues/480)) ([b4d7aa9](https://www.github.com/cloudevents/sdk-javascript/commit/b4d7aa9adbb92bb5d037c464dd3d4bcd1ba88fe6))
|
||||||
|
* update package.json format and deps ([#479](https://www.github.com/cloudevents/sdk-javascript/issues/479)) ([6204805](https://www.github.com/cloudevents/sdk-javascript/commit/6204805bfcebf68fd1b94777ecb3df6d7473e10e))
|
||||||
|
* update the release documentation ([#476](https://www.github.com/cloudevents/sdk-javascript/issues/476)) ([c420da4](https://www.github.com/cloudevents/sdk-javascript/commit/c420da479343bc71a5ba4d5ed41841280f4c989a))
|
||||||
|
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
* update readme to include http builtin transport ([#483](https://www.github.com/cloudevents/sdk-javascript/issues/483)) ([4ab6356](https://www.github.com/cloudevents/sdk-javascript/commit/4ab6356bd70434e55938ff89e940952f8b0105a3))
|
||||||
|
|
||||||
|
### [5.3.2](https://www.github.com/cloudevents/sdk-javascript/compare/v5.3.1...v5.3.2) (2022-02-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* use `isolatedModules: true` in tsconfig.json ([#469](https://www.github.com/cloudevents/sdk-javascript/issues/469)) ([b5c0b56](https://www.github.com/cloudevents/sdk-javascript/commit/b5c0b56f52dd6119949df1a583b76a48c6e3cec7))
|
||||||
|
|
||||||
|
|
||||||
|
### Miscellaneous
|
||||||
|
|
||||||
|
* bump typedoc to remove vuln ([#472](https://www.github.com/cloudevents/sdk-javascript/issues/472)) ([c3d9f39](https://www.github.com/cloudevents/sdk-javascript/commit/c3d9f39a53afaf411fa91aeb2323fef2eddb4d32))
|
||||||
|
|
||||||
|
### [5.3.1](https://www.github.com/cloudevents/sdk-javascript/compare/v5.3.0...v5.3.1) (2022-02-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* improve binary data detection in HTTP transport ([#468](https://www.github.com/cloudevents/sdk-javascript/issues/468)) ([cd4dea9](https://www.github.com/cloudevents/sdk-javascript/commit/cd4dea954b1797eb0e0fe2acd1b32ef75a3b7b65))
|
||||||
|
* package.json & package-lock.json to reduce vulnerabilities ([#462](https://www.github.com/cloudevents/sdk-javascript/issues/462)) ([ae8fa79](https://www.github.com/cloudevents/sdk-javascript/commit/ae8fa799afea279adfbd1f35103fb168621c8a24))
|
||||||
|
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
* add TS examples for CloudEvent usage ([#461](https://www.github.com/cloudevents/sdk-javascript/issues/461)) ([c603831](https://www.github.com/cloudevents/sdk-javascript/commit/c603831e934c68c1f430708b5bff4dad938093dd))
|
||||||
|
* fix ts example ([#467](https://www.github.com/cloudevents/sdk-javascript/issues/467)) ([349b84c](https://www.github.com/cloudevents/sdk-javascript/commit/349b84c3dad5d282d24780a884a0f94643871247))
|
||||||
|
|
||||||
|
|
||||||
|
### Miscellaneous
|
||||||
|
|
||||||
|
* update readme with current Node LTS versions and add Node 16 to the testing matrix([#465](https://www.github.com/cloudevents/sdk-javascript/issues/465)) ([8abbc11](https://www.github.com/cloudevents/sdk-javascript/commit/8abbc114af4b784c5061737f432f0af9ccb6c6f2))
|
||||||
|
|
||||||
## [5.3.0](https://www.github.com/cloudevents/sdk-javascript/compare/v5.2.0...v5.3.0) (2022-01-14)
|
## [5.3.0](https://www.github.com/cloudevents/sdk-javascript/compare/v5.2.0...v5.3.0) (2022-01-14)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Maintainers
|
||||||
|
|
||||||
|
Current active maintainers of this SDK:
|
||||||
|
|
||||||
|
- [Lance Ball](https://github.com/lance)
|
||||||
|
- [Daniel Bevenius](https://github.com/danbev)
|
||||||
|
- [Lucas Holmquist](https://github.com/lholmquist)
|
||||||
|
- [Fabio Jose](https://github.com/fabiojose)
|
||||||
|
- [Helio Frota](https://github.com/helio-frota)
|
138
README.md
138
README.md
|
@ -12,14 +12,14 @@ The CloudEvents SDK for JavaScript.
|
||||||
|
|
||||||
- Represent CloudEvents in memory
|
- Represent CloudEvents in memory
|
||||||
- Serialize and deserialize CloudEvents in different [event formats](https://github.com/cloudevents/spec/blob/v1.0/spec.md#event-format).
|
- Serialize and deserialize CloudEvents in different [event formats](https://github.com/cloudevents/spec/blob/v1.0/spec.md#event-format).
|
||||||
- Send and recieve CloudEvents with via different [protocol bindings](https://github.com/cloudevents/spec/blob/v1.0/spec.md#protocol-binding).
|
- Send and receive CloudEvents with via different [protocol bindings](https://github.com/cloudevents/spec/blob/v1.0/spec.md#protocol-binding).
|
||||||
|
|
||||||
_Note:_ Supports CloudEvent versions 0.3, 1.0
|
_Note:_ Supports CloudEvent version 1.0
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
The CloudEvents SDK requires a current LTS version of Node.js. At the moment
|
The CloudEvents SDK requires a current LTS version of Node.js. At the moment
|
||||||
those are Node.js 10.x and Node.js 12.x. To install in your Node.js project:
|
those are Node.js 16.x, and Node.js 18.x. To install in your Node.js project:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
npm install cloudevents
|
npm install cloudevents
|
||||||
|
@ -46,9 +46,26 @@ app.post("/", (req, res) => {
|
||||||
|
|
||||||
#### Emitting Events
|
#### Emitting Events
|
||||||
|
|
||||||
You can send events over HTTP in either binary or structured format
|
The easiest way to send events is to use the built-in HTTP emitter.
|
||||||
using the `HTTP` binding to create a `Message` which has properties
|
|
||||||
for `headers` and `body`.
|
```js
|
||||||
|
const { httpTransport, emitterFor, CloudEvent } = require("cloudevents");
|
||||||
|
|
||||||
|
// Create an emitter to send events to a receiver
|
||||||
|
const emit = emitterFor(httpTransport("https://my.receiver.com/endpoint"));
|
||||||
|
|
||||||
|
// Create a new CloudEvent
|
||||||
|
const ce = new CloudEvent({ type, source, data });
|
||||||
|
|
||||||
|
// Send it to the endpoint - encoded as HTTP binary by default
|
||||||
|
emit(ce);
|
||||||
|
```
|
||||||
|
|
||||||
|
If you prefer to use another transport mechanism for sending events
|
||||||
|
over HTTP, you can use the `HTTP` binding to create a `Message` which
|
||||||
|
has properties for `headers` and `body`, allowing greater flexibility
|
||||||
|
and customization. For example, the `axios` module is used here to send
|
||||||
|
a CloudEvent.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const axios = require("axios").default;
|
const axios = require("axios").default;
|
||||||
|
@ -87,30 +104,20 @@ const emit = emitterFor(sendWithAxios, { mode: Mode.BINARY });
|
||||||
emit(new CloudEvent({ type, source, data }));
|
emit(new CloudEvent({ type, source, data }));
|
||||||
```
|
```
|
||||||
|
|
||||||
You may also use the `Emitter` singleton
|
You may also use the `Emitter` singleton to send your `CloudEvents`.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const axios = require("axios").default;
|
const { emitterFor, httpTransport, Mode, CloudEvent, Emitter } = require("cloudevents");
|
||||||
const { emitterFor, Mode, CloudEvent, Emitter } = require("cloudevents");
|
|
||||||
|
|
||||||
function sendWithAxios(message) {
|
// Create a CloudEvent emitter function to send events to our receiver
|
||||||
// Do what you need with the message headers
|
const emit = emitterFor(httpTransport("https://example.com/receiver"));
|
||||||
// and body in this function, then send the
|
|
||||||
// event
|
|
||||||
axios({
|
|
||||||
method: "post",
|
|
||||||
url: "...",
|
|
||||||
data: message.body,
|
|
||||||
headers: message.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = emitterFor(sendWithAxios, { mode: Mode.BINARY });
|
// Use the emit() function to send a CloudEvent to its endpoint when a "cloudevent" event is emitted
|
||||||
// Set the emit
|
// (see: https://nodejs.org/api/events.html#class-eventemitter)
|
||||||
Emitter.on("cloudevent", emit);
|
Emitter.on("cloudevent", emit);
|
||||||
|
|
||||||
...
|
...
|
||||||
// In any part of the code will send the event
|
// In any part of the code, calling `emit()` on a `CloudEvent` instance will send the event
|
||||||
new CloudEvent({ type, source, data }).emit();
|
new CloudEvent({ type, source, data }).emit();
|
||||||
|
|
||||||
// You can also have several listeners to send the event to several endpoints
|
// You can also have several listeners to send the event to several endpoints
|
||||||
|
@ -132,6 +139,43 @@ const ce = new CloudEvent({...});
|
||||||
const ce2 = ce.cloneWith({extension: "Value"});
|
const ce2 = ce.cloneWith({extension: "Value"});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can create a `CloudEvent` object in many ways, for example, in TypeScript:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { CloudEvent, CloudEventV1, CloudEventV1Attributes } from "cloudevents";
|
||||||
|
const ce: CloudEventV1<string> = {
|
||||||
|
specversion: "1.0",
|
||||||
|
source: "/some/source",
|
||||||
|
type: "example",
|
||||||
|
id: "1234"
|
||||||
|
};
|
||||||
|
const event = new CloudEvent(ce);
|
||||||
|
const ce2: CloudEventV1Attributes<string> = {
|
||||||
|
specversion: "1.0",
|
||||||
|
source: "/some/source",
|
||||||
|
type: "example",
|
||||||
|
};
|
||||||
|
const event2 = new CloudEvent(ce2);
|
||||||
|
const event3 = new CloudEvent({
|
||||||
|
source: "/some/source",
|
||||||
|
type: "example",
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### A Note About Big Integers
|
||||||
|
|
||||||
|
When parsing JSON data, if a JSON field value is a number, and that number
|
||||||
|
is really big, JavaScript loses precision. For example, the Twitter API exposes
|
||||||
|
the Tweet ID. This is a large number that exceeds the integer space of `Number`.
|
||||||
|
|
||||||
|
In order to address this situation, you can set the environment variable
|
||||||
|
`CE_USE_BIG_INT` to the string value `"true"` to enable the use of the
|
||||||
|
[`json-bigint`](https://www.npmjs.com/package/json-bigint) package. This
|
||||||
|
package is not used by default due to the resulting slowdown in parse speed
|
||||||
|
by a factor of 7x.
|
||||||
|
|
||||||
|
See for more information: https://github.com/cloudevents/sdk-javascript/issues/489
|
||||||
|
|
||||||
### Example Applications
|
### Example Applications
|
||||||
|
|
||||||
There are a few trivial example applications in
|
There are a few trivial example applications in
|
||||||
|
@ -147,37 +191,37 @@ There you will find Express.js, TypeScript and Websocket examples.
|
||||||
|
|
||||||
| Core Specification | [v0.3](https://github.com/cloudevents/spec/blob/v0.3/spec.md) | [v1.0](https://github.com/cloudevents/spec/blob/v1.0/spec.md) |
|
| Core Specification | [v0.3](https://github.com/cloudevents/spec/blob/v0.3/spec.md) | [v1.0](https://github.com/cloudevents/spec/blob/v1.0/spec.md) |
|
||||||
| ------------------ | ------------------------------------------------------------- | ------------------------------------------------------------- |
|
| ------------------ | ------------------------------------------------------------- | ------------------------------------------------------------- |
|
||||||
| CloudEvents Core | :heavy_check_mark: | :heavy_check_mark: |
|
| CloudEvents Core | :white_check_mark: | :white_check_mark: |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
| Event Formats | [v0.3](https://github.com/cloudevents/spec/tree/v0.3) | [v1.0](https://github.com/cloudevents/spec/blob/v1.0/spec.md#event-format) |
|
| Event Formats | [v0.3](https://github.com/cloudevents/spec/tree/v0.3) | [v1.0](https://github.com/cloudevents/spec/blob/v1.0/spec.md#event-format) |
|
||||||
| ----------------- | ----------------------------------------------------- | ----------------------------------------------------- |
|
| ----------------- | ----------------------------------------------------- | ----------------------------------------------------- |
|
||||||
| AVRO Event Format | :x: | :x: |
|
| AVRO Event Format | :x: | :x: |
|
||||||
| JSON Event Format | :heavy_check_mark: | :heavy_check_mark: |
|
| JSON Event Format | :white_check_mark: | :white_check_mark: |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
| Protocol Bindings | [v0.3](https://github.com/cloudevents/spec/tree/v0.3) | [v1.0](https://github.com/cloudevents/spec/blob/v1.0/spec.md#protocol-binding) |
|
| Protocol Bindings | [v0.3](https://github.com/cloudevents/spec/tree/v0.3) | [v1.0](https://github.com/cloudevents/spec/blob/v1.0/spec.md#protocol-binding) |
|
||||||
| ---------------------- | ----------------------------------------------------- | ----------------------------------------------------- |
|
| ---------------------- | ----------------------------------------------------- | ----------------------------------------------------- |
|
||||||
| AMQP Protocol Binding | :x: | :x: |
|
| AMQP Protocol Binding | :x: | :x: |
|
||||||
| HTTP Protocol Binding | :heavy_check_mark: | :heavy_check_mark: |
|
| HTTP Protocol Binding | :white_check_mark: | :white_check_mark: |
|
||||||
| Kafka Protocol Binding | :x: | :heavy_check_mark: |
|
| Kafka Protocol Binding | :x: | :white_check_mark: |
|
||||||
| MQTT Protocol Binding | :heavy_check_mark: | :x: |
|
| MQTT Protocol Binding | :white_check_mark: | :x: |
|
||||||
| NATS Protocol Binding | :x: | :x: |
|
| NATS Protocol Binding | :x: | :x: |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
| Content Modes | [v0.3](https://github.com/cloudevents/spec/tree/v0.3) | [v1.0](https://github.com/cloudevents/spec/blob/v1.0/http-protocol-binding.md#13-content-modes) |
|
| Content Modes | [v0.3](https://github.com/cloudevents/spec/tree/v0.3) | [v1.0](https://github.com/cloudevents/spec/blob/v1.0/http-protocol-binding.md#13-content-modes) |
|
||||||
| ---------------------- | ----------------------------------------------------- | ----------------------------------------------------- |
|
| ---------------------- | ----------------------------------------------------- | ----------------------------------------------------- |
|
||||||
| HTTP Binary | :heavy_check_mark: | :heavy_check_mark: |
|
| HTTP Binary | :white_check_mark: | :white_check_mark: |
|
||||||
| HTTP Structured | :heavy_check_mark: | :heavy_check_mark: |
|
| HTTP Structured | :white_check_mark: | :white_check_mark: |
|
||||||
| HTTP Batch | :heavy_check_mark: | :heavy_check_mark: |
|
| HTTP Batch | :white_check_mark: | :white_check_mark: |
|
||||||
| Kafka Binary | :heavy_check_mark: | :heavy_check_mark: |
|
| Kafka Binary | :white_check_mark: | :white_check_mark: |
|
||||||
| Kafka Structured | :heavy_check_mark: | :heavy_check_mark: |
|
| Kafka Structured | :white_check_mark: | :white_check_mark: |
|
||||||
| Kafka Batch | :heavy_check_mark: | :heavy_check_mark:
|
| Kafka Batch | :white_check_mark: | :white_check_mark:
|
||||||
| MQTT Binary | :heavy_check_mark: | :heavy_check_mark: |
|
| MQTT Binary | :white_check_mark: | :white_check_mark: |
|
||||||
| MQTT Structured | :heavy_check_mark: | :heavy_check_mark: |
|
| MQTT Structured | :white_check_mark: | :white_check_mark: |
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
|
|
||||||
|
@ -189,12 +233,15 @@ There you will find Express.js, TypeScript and Websocket examples.
|
||||||
to determine which week will have the call.
|
to determine which week will have the call.
|
||||||
- Slack: #cloudeventssdk channel under
|
- Slack: #cloudeventssdk channel under
|
||||||
[CNCF's Slack workspace](https://slack.cncf.io/).
|
[CNCF's Slack workspace](https://slack.cncf.io/).
|
||||||
- Maintainers typically available on Slack
|
|
||||||
- Lance Ball
|
|
||||||
- Lucas Holmquist
|
|
||||||
- Grant Timmerman
|
|
||||||
- Email: https://lists.cncf.io/g/cncf-cloudevents-sdk
|
- Email: https://lists.cncf.io/g/cncf-cloudevents-sdk
|
||||||
|
|
||||||
|
## Maintainers
|
||||||
|
|
||||||
|
Currently active maintainers who may be found in the CNCF Slack.
|
||||||
|
|
||||||
|
- Lance Ball (@lance)
|
||||||
|
- Lucas Holmquist (@lholmquist)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
We love contributions from the community! Please check the
|
We love contributions from the community! Please check the
|
||||||
|
@ -211,3 +258,14 @@ how SDK projects are
|
||||||
for how PR reviews and approval, and our
|
for how PR reviews and approval, and our
|
||||||
[Code of Conduct](https://github.com/cloudevents/spec/blob/master/community/GOVERNANCE.md#additional-information)
|
[Code of Conduct](https://github.com/cloudevents/spec/blob/master/community/GOVERNANCE.md#additional-information)
|
||||||
information.
|
information.
|
||||||
|
|
||||||
|
If there is a security concern with one of the CloudEvents specifications, or
|
||||||
|
with one of the project's SDKs, please send an email to
|
||||||
|
[cncf-cloudevents-security@lists.cncf.io](mailto:cncf-cloudevents-security@lists.cncf.io).
|
||||||
|
|
||||||
|
## Additional SDK Resources
|
||||||
|
|
||||||
|
- [List of current active maintainers](MAINTAINERS.md)
|
||||||
|
- [How to contribute to the project](CONTRIBUTING.md)
|
||||||
|
- [SDK's License](LICENSE)
|
||||||
|
- [SDK's Release process](RELEASING.md)
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
# Module Release Guidelines
|
|
||||||
|
|
||||||
## Create a Proposal Issue
|
|
||||||
|
|
||||||
To prepare for a new release, create a [new issue](https://github.com/cloudevents/sdk-javascript/issues/new?assignees=&labels=&template=feature-request.md&title=) where the title of the issue cleary reflects the version to be released.
|
|
||||||
|
|
||||||
For example: "Proposal for 3.2.0 release", or something similar. If you are not sure which version is the next version to be released, you can run `npm run release -- --dry-run` to find out what the next version will be.
|
|
||||||
|
|
||||||
The body of the issue should be the commits that will be part of the release. This can be easily accomplished by running a git log command with a defined **range**. This range should start at the most recent version tag and end at the latest commit in the main branch.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```
|
|
||||||
git log v3.0.1..upstream/main --oneline
|
|
||||||
```
|
|
||||||
|
|
||||||
This will output all the commits from the 3.0.1 tag to the latest commits in the remote upstream/main branch.
|
|
||||||
|
|
||||||
This output should be pasted into the issue as normal text. This will allow Github to magically turn all commit hashes and PR/Issues numbers to links.
|
|
||||||
|
|
||||||
### Get Consensus
|
|
||||||
|
|
||||||
Before a release can be finalized, other maintainers should give a +1 or a thumbs up or some other identifying mark that they are good with the changes.
|
|
||||||
|
|
||||||
## Create and Publish the release
|
|
||||||
|
|
||||||
Once consensus has been reached on the proposal it is time to create the release and publish it to npm.
|
|
||||||
|
|
||||||
### Create the Release
|
|
||||||
|
|
||||||
Creating the release is as simple as running the release script:
|
|
||||||
|
|
||||||
```
|
|
||||||
npm run release
|
|
||||||
```
|
|
||||||
|
|
||||||
This will update the CHANGELOG.md and create a new tag based on the version. This can then be pushed upstream by doing:
|
|
||||||
|
|
||||||
```
|
|
||||||
git push upstream main --follow-tags
|
|
||||||
```
|
|
||||||
|
|
||||||
### Create the release on GitHub
|
|
||||||
|
|
||||||
Once the release tag has been created and pushed up to Github, we should draft a new release using the Github UI, which is [located here](https://github.com/cloudevents/sdk-javascript/releases/new)
|
|
||||||
|
|
||||||
* Tag Version should be the tag that was just created
|
|
||||||
* The release title should be something like "VERSION Release"
|
|
||||||
* And the Changelog entries for the current release should be copied/pasted into the comments
|
|
||||||
|
|
||||||
|
|
||||||
### Publish to npm
|
|
||||||
|
|
||||||
Once the new version has been created, we need to push it to npm. Assuming you have all the rights to do so, just run:
|
|
||||||
|
|
||||||
```
|
|
||||||
npm publish
|
|
||||||
```
|
|
||||||
|
|
||||||
## Close the Issue
|
|
||||||
|
|
||||||
Once the release has been completed, the issue can be closed.
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Module Release Guidelines
|
||||||
|
|
||||||
|
## `release-please`
|
||||||
|
|
||||||
|
This project uses [`release-please-action`](https://github.com/google-github-actions/release-please-action)
|
||||||
|
to manage CHANGELOG.md and automate our releases. It does so by parsing the git history, looking for
|
||||||
|
[Conventional Commit](https://www.conventionalcommits.org/en/v1.0.0/) messages, and creating release PRs.
|
||||||
|
|
||||||
|
For example: https://github.com/cloudevents/sdk-javascript/pull/475
|
||||||
|
|
||||||
|
Each time a commit lands on `main`, the workflow updates the pull request to include the commit message
|
||||||
|
in CHANGELOG.md, and bump the version in package.json. When you are ready to create a new release, simply
|
||||||
|
land the pull request. This will result in a release commit, updating CHANGELOG.md and package.json, a version
|
||||||
|
tag is created on that commit SHA, and a release is drafted in github.com.
|
||||||
|
|
||||||
|
### Publish to npm
|
||||||
|
|
||||||
|
Once the new version has been created, we need to push it to npm. Assuming you have all the rights to do so, just run:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm publish
|
||||||
|
```
|
|
@ -28,11 +28,11 @@ app.post("/", (req, res) => {
|
||||||
const responseEventMessage = new CloudEvent({
|
const responseEventMessage = new CloudEvent({
|
||||||
source: '/',
|
source: '/',
|
||||||
type: 'event:response',
|
type: 'event:response',
|
||||||
...event
|
...event,
|
||||||
|
data: {
|
||||||
|
hello: 'world'
|
||||||
|
}
|
||||||
});
|
});
|
||||||
responseEventMessage.data = {
|
|
||||||
hello: 'world'
|
|
||||||
};
|
|
||||||
|
|
||||||
// const message = HTTP.binary(responseEventMessage)
|
// const message = HTTP.binary(responseEventMessage)
|
||||||
const message = HTTP.structured(responseEventMessage)
|
const message = HTTP.structured(responseEventMessage)
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
# MQTT Example
|
||||||
|
|
||||||
|
The MQTT message protocol are available since v5.3.0
|
||||||
|
|
||||||
|
## How To Start
|
||||||
|
|
||||||
|
Install and compile:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run compile
|
||||||
|
```
|
||||||
|
|
||||||
|
Start a MQTT broker using Docker:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -it -d -p 1883:1883 eclipse-mosquitto:2.0 mosquitto -c /mosquitto-no-auth.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm start
|
||||||
|
```
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"name": "mqtt-ex",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Simple mqtt example using CloudEvents types",
|
||||||
|
"repository": "https://github.com/cloudevents/sdk-javascript.git",
|
||||||
|
"main": "build/src/index.js",
|
||||||
|
"types": "build/src/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"build/src"
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"keywords": [],
|
||||||
|
"scripts": {
|
||||||
|
"start": "node build/index.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"check": "gts check",
|
||||||
|
"clean": "gts clean",
|
||||||
|
"compile": "tsc -p .",
|
||||||
|
"watch": "tsc -p . --watch",
|
||||||
|
"fix": "gts fix",
|
||||||
|
"prepare": "npm run compile",
|
||||||
|
"pretest": "npm run compile",
|
||||||
|
"posttest": "npm run check"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^14.14.10",
|
||||||
|
"@types/ws": "^8.5.4",
|
||||||
|
"gts": "^3.0.3",
|
||||||
|
"typescript": "~4.1.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cloudevents": "^6.0.3",
|
||||||
|
"mqtt": "^4.3.7"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
import { CloudEvent, MQTT } from "cloudevents";
|
||||||
|
import * as mqtt from "mqtt";
|
||||||
|
|
||||||
|
const client = mqtt.connect("mqtt://localhost:1883");
|
||||||
|
|
||||||
|
client.on("connect", function () {
|
||||||
|
client.subscribe("presence", function (err) {
|
||||||
|
if (err) return;
|
||||||
|
const event = new CloudEvent({
|
||||||
|
source: "presence",
|
||||||
|
type: "presence.event",
|
||||||
|
datacontenttype: "application/json",
|
||||||
|
data: {
|
||||||
|
hello: "world",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { body, headers } = MQTT.binary(event);
|
||||||
|
|
||||||
|
client.publish("presence", JSON.stringify(body), {
|
||||||
|
properties: {
|
||||||
|
userProperties: headers as mqtt.UserProperties,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("message", function (topic, message, packet) {
|
||||||
|
const event = MQTT.toEvent({
|
||||||
|
body: JSON.parse(message.toString()),
|
||||||
|
headers: packet.properties?.userProperties || {},
|
||||||
|
});
|
||||||
|
console.log(event);
|
||||||
|
client.end();
|
||||||
|
});
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"extends": "./node_modules/gts/tsconfig-google.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./build/",
|
||||||
|
"lib": [
|
||||||
|
"es6",
|
||||||
|
"dom"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"test/**/*.ts"
|
||||||
|
],
|
||||||
|
"allowJs": true
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
46
package.json
46
package.json
|
@ -1,17 +1,21 @@
|
||||||
{
|
{
|
||||||
"name": "cloudevents",
|
"name": "cloudevents",
|
||||||
"version": "5.3.0",
|
"version": "9.0.0",
|
||||||
"description": "CloudEvents SDK for JavaScript",
|
"description": "CloudEvents SDK for JavaScript",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch": "tsc --project tsconfig.json --watch",
|
"watch": "tsc --project tsconfig.json --watch",
|
||||||
"build": "tsc --project tsconfig.json && tsc --project tsconfig.browser.json && webpack",
|
"build:src": "tsc --project tsconfig.json",
|
||||||
|
"build:browser": "tsc --project tsconfig.browser.json && webpack",
|
||||||
|
"build:schema": "ajv compile -c ./src/schema/formats.js -s src/schema/cloudevent.json --strict-types false -o src/schema/v1.js",
|
||||||
|
"build": "npm run build:schema && npm run build:src && npm run build:browser",
|
||||||
"lint": "npm run lint:md && npm run lint:js",
|
"lint": "npm run lint:md && npm run lint:js",
|
||||||
"lint:js": "eslint 'src/**/*.{js,ts}' 'test/**/*.{js,ts}' cucumber.js",
|
"lint:js": "eslint 'src/**/*.{js,ts}' 'test/**/*.{js,ts}' cucumber.js",
|
||||||
"lint:md": "remark .",
|
"lint:md": "remark .",
|
||||||
"lint:fix": "eslint 'src/**/*.{js,ts}' 'test/**/*.{js,ts}' --fix",
|
"lint:fix": "eslint 'src/**/*.{js,ts}' 'test/**/*.{js,ts}' --fix",
|
||||||
"pretest": "npm run lint && npm run conformance",
|
"pretest": "npm run lint && npm run build && npm run conformance",
|
||||||
"test": "mocha --require ts-node/register ./test/integration/**/*.ts",
|
"test": "mocha --require ts-node/register ./test/integration/**/*.ts",
|
||||||
|
"test:one": "mocha --require ts-node/register",
|
||||||
"conformance": "cucumber-js ./conformance/features/*-protocol-binding.feature -p default",
|
"conformance": "cucumber-js ./conformance/features/*-protocol-binding.feature -p default",
|
||||||
"coverage": "nyc --reporter=lcov --reporter=text npm run test",
|
"coverage": "nyc --reporter=lcov --reporter=text npm run test",
|
||||||
"coverage-publish": "wget -qO - https://coverage.codacy.com/get.sh | bash -s report -l JavaScript -r coverage/lcov.info",
|
"coverage-publish": "wget -qO - https://coverage.codacy.com/get.sh | bash -s report -l JavaScript -r coverage/lcov.info",
|
||||||
|
@ -106,22 +110,27 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/cloudevents/sdk-javascript#readme",
|
"homepage": "https://github.com/cloudevents/sdk-javascript#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "~6.12.3",
|
"ajv": "^8.11.0",
|
||||||
"uuid": "~8.3.0"
|
"ajv-formats": "^2.1.1",
|
||||||
|
"process": "^0.11.10",
|
||||||
|
"json-bigint": "^1.0.0",
|
||||||
|
"util": "^0.12.4",
|
||||||
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cucumber/cucumber": "^8.0.0-rc.1",
|
"@cucumber/cucumber": "^8.0.0",
|
||||||
"@types/ajv": "^1.0.0",
|
|
||||||
"@types/chai": "^4.2.11",
|
"@types/chai": "^4.2.11",
|
||||||
"@types/cucumber": "^6.0.1",
|
"@types/cucumber": "^6.0.1",
|
||||||
"@types/got": "^9.6.11",
|
"@types/got": "^9.6.11",
|
||||||
|
"@types/json-bigint": "^1.0.1",
|
||||||
"@types/mocha": "^7.0.2",
|
"@types/mocha": "^7.0.2",
|
||||||
"@types/node": "^14.14.10",
|
"@types/node": "^14.14.10",
|
||||||
"@types/superagent": "^4.1.10",
|
"@types/superagent": "^4.1.10",
|
||||||
"@types/uuid": "^8.0.0",
|
"@types/uuid": "^8.3.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.29.0",
|
"@typescript-eslint/eslint-plugin": "^4.29.0",
|
||||||
"@typescript-eslint/parser": "^4.29.0",
|
"@typescript-eslint/parser": "^4.29.0",
|
||||||
"axios": "^0.21.3",
|
"ajv-cli": "^5.0.0",
|
||||||
|
"axios": "^0.26.1",
|
||||||
"chai": "~4.2.0",
|
"chai": "~4.2.0",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-config-standard": "^16.0.3",
|
"eslint-config-standard": "^16.0.3",
|
||||||
|
@ -129,9 +138,9 @@
|
||||||
"eslint-plugin-import": "^2.23.4",
|
"eslint-plugin-import": "^2.23.4",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^5.1.0",
|
"eslint-plugin-promise": "^5.1.0",
|
||||||
"got": "^11.7.0",
|
"got": "^11.8.5",
|
||||||
"http-parser-js": "^0.5.2",
|
"http-parser-js": "^0.5.2",
|
||||||
"mocha": "~9.1.2",
|
"mocha": "^10.1.0",
|
||||||
"nock": "~12.0.3",
|
"nock": "~12.0.3",
|
||||||
"nyc": "~15.0.0",
|
"nyc": "~15.0.0",
|
||||||
"prettier": "^2.0.5",
|
"prettier": "^2.0.5",
|
||||||
|
@ -139,15 +148,18 @@
|
||||||
"remark-lint": "^8.0.0",
|
"remark-lint": "^8.0.0",
|
||||||
"remark-lint-list-item-indent": "^2.0.1",
|
"remark-lint-list-item-indent": "^2.0.1",
|
||||||
"remark-preset-lint-recommended": "^5.0.0",
|
"remark-preset-lint-recommended": "^5.0.0",
|
||||||
"superagent": "^6.1.0",
|
"superagent": "^7.1.1",
|
||||||
"ts-node": "^8.10.2",
|
"ts-node": "^10.8.1",
|
||||||
"typedoc": "^0.21.5",
|
"typedoc": "^0.22.11",
|
||||||
"typescript": "^4.3.5",
|
"typescript": "^4.3.5",
|
||||||
"webpack": "^5.1.1",
|
"webpack": "^5.76.0",
|
||||||
"webpack-cli": "^4.0.0"
|
"webpack-cli": "^4.10.0"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"types": "./dist/index.d.ts"
|
"types": "./dist/index.d.ts",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20 <=24"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ const CONSTANTS = Object.freeze({
|
||||||
DATA_SCHEMA: "dataschema",
|
DATA_SCHEMA: "dataschema",
|
||||||
DATA_BASE64: "data_base64",
|
DATA_BASE64: "data_base64",
|
||||||
},
|
},
|
||||||
|
USE_BIG_INT_ENV: "CE_USE_BIG_INT"
|
||||||
} as const);
|
} as const);
|
||||||
|
|
||||||
export default CONSTANTS;
|
export default CONSTANTS;
|
||||||
|
|
|
@ -9,15 +9,13 @@ import { Emitter } from "..";
|
||||||
|
|
||||||
import { CloudEventV1 } from "./interfaces";
|
import { CloudEventV1 } from "./interfaces";
|
||||||
import { validateCloudEvent } from "./spec";
|
import { validateCloudEvent } from "./spec";
|
||||||
import { ValidationError, isBinary, asBase64, isValidType } from "./validation";
|
import { ValidationError, isBinary, asBase64, isValidType, base64AsBinary } from "./validation";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An enum representing the CloudEvent specification version
|
* Constants representing the CloudEvent specification version
|
||||||
*/
|
*/
|
||||||
export const enum Version {
|
export const V1 = "1.0";
|
||||||
V1 = "1.0",
|
export const V03 = "0.3";
|
||||||
V03 = "0.3",
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A CloudEvent describes event data in common formats to provide
|
* A CloudEvent describes event data in common formats to provide
|
||||||
|
@ -28,12 +26,12 @@ export class CloudEvent<T = undefined> implements CloudEventV1<T> {
|
||||||
id: string;
|
id: string;
|
||||||
type: string;
|
type: string;
|
||||||
source: string;
|
source: string;
|
||||||
specversion: Version;
|
specversion: string;
|
||||||
datacontenttype?: string;
|
datacontenttype?: string;
|
||||||
dataschema?: string;
|
dataschema?: string;
|
||||||
subject?: string;
|
subject?: string;
|
||||||
time?: string;
|
time?: string;
|
||||||
#_data?: T;
|
data?: T;
|
||||||
data_base64?: string;
|
data_base64?: string;
|
||||||
|
|
||||||
// Extensions should not exist as it's own object, but instead
|
// Extensions should not exist as it's own object, but instead
|
||||||
|
@ -69,7 +67,7 @@ export class CloudEvent<T = undefined> implements CloudEventV1<T> {
|
||||||
this.source = properties.source as string;
|
this.source = properties.source as string;
|
||||||
delete (properties as any).source;
|
delete (properties as any).source;
|
||||||
|
|
||||||
this.specversion = (properties.specversion as Version) || Version.V1;
|
this.specversion = (properties.specversion) || V1;
|
||||||
delete properties.specversion;
|
delete properties.specversion;
|
||||||
|
|
||||||
this.datacontenttype = properties.datacontenttype;
|
this.datacontenttype = properties.datacontenttype;
|
||||||
|
@ -85,26 +83,35 @@ export class CloudEvent<T = undefined> implements CloudEventV1<T> {
|
||||||
delete properties.dataschema;
|
delete properties.dataschema;
|
||||||
|
|
||||||
this.data_base64 = properties.data_base64 as string;
|
this.data_base64 = properties.data_base64 as string;
|
||||||
|
|
||||||
|
if (this.data_base64) {
|
||||||
|
this.data = base64AsBinary(this.data_base64) as unknown as T;
|
||||||
|
}
|
||||||
|
|
||||||
delete properties.data_base64;
|
delete properties.data_base64;
|
||||||
|
|
||||||
this.schemaurl = properties.schemaurl as string;
|
this.schemaurl = properties.schemaurl as string;
|
||||||
delete properties.schemaurl;
|
delete properties.schemaurl;
|
||||||
|
|
||||||
this.data = properties.data;
|
if (isBinary(properties.data)) {
|
||||||
|
this.data_base64 = asBase64(properties.data as unknown as Buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data = typeof properties.data !== "undefined" ? properties.data : this.data;
|
||||||
delete properties.data;
|
delete properties.data;
|
||||||
|
|
||||||
// sanity checking
|
// sanity checking
|
||||||
if (this.specversion === Version.V1 && this.schemaurl) {
|
if (this.specversion === V1 && this.schemaurl) {
|
||||||
throw new TypeError("cannot set schemaurl on version 1.0 event");
|
throw new TypeError("cannot set schemaurl on version 1.0 event");
|
||||||
} else if (this.specversion === Version.V03 && this.dataschema) {
|
} else if (this.specversion === V03 && this.dataschema) {
|
||||||
throw new TypeError("cannot set dataschema on version 0.3 event");
|
throw new TypeError("cannot set dataschema on version 0.3 event");
|
||||||
}
|
}
|
||||||
|
|
||||||
// finally process any remaining properties - these are extensions
|
// finally process any remaining properties - these are extensions
|
||||||
for (const [key, value] of Object.entries(properties)) {
|
for (const [key, value] of Object.entries(properties)) {
|
||||||
// Extension names should only allow lowercase a-z and 0-9 in the name
|
// Extension names must only allow lowercase a-z and 0-9 in the name
|
||||||
// names should not exceed 20 characters in length
|
// names should not exceed 20 characters in length
|
||||||
if (!key.match(/^[a-z0-9]{1,20}$/) && strict) {
|
if (!key.match(/^[a-z0-9]+$/) && strict) {
|
||||||
throw new ValidationError(`invalid extension name: ${key}
|
throw new ValidationError(`invalid extension name: ${key}
|
||||||
CloudEvents attribute names MUST consist of lower-case letters ('a' to 'z')
|
CloudEvents attribute names MUST consist of lower-case letters ('a' to 'z')
|
||||||
or digits ('0' to '9') from the ASCII character set. Attribute names SHOULD
|
or digits ('0' to '9') from the ASCII character set. Attribute names SHOULD
|
||||||
|
@ -127,17 +134,6 @@ See: https://github.com/cloudevents/spec/blob/v1.0/spec.md#type-system`);
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
get data(): T | undefined {
|
|
||||||
return this.#_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
set data(value: T | undefined) {
|
|
||||||
if (isBinary(value)) {
|
|
||||||
this.data_base64 = asBase64(value);
|
|
||||||
}
|
|
||||||
this.#_data = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by JSON.stringify(). The name is confusing, but this method is called by
|
* Used by JSON.stringify(). The name is confusing, but this method is called by
|
||||||
* JSON.stringify() when converting this object to JSON.
|
* JSON.stringify() when converting this object to JSON.
|
||||||
|
@ -147,7 +143,11 @@ See: https://github.com/cloudevents/spec/blob/v1.0/spec.md#type-system`);
|
||||||
toJSON(): Record<string, unknown> {
|
toJSON(): Record<string, unknown> {
|
||||||
const event = { ...this };
|
const event = { ...this };
|
||||||
event.time = new Date(this.time as string).toISOString();
|
event.time = new Date(this.time as string).toISOString();
|
||||||
event.data = this.#_data;
|
|
||||||
|
if (event.data_base64 && event.data) {
|
||||||
|
delete event.data;
|
||||||
|
}
|
||||||
|
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,9 +230,6 @@ See: https://github.com/cloudevents/spec/blob/v1.0/spec.md#type-system`);
|
||||||
event: CloudEventV1<any>,
|
event: CloudEventV1<any>,
|
||||||
options: Partial<CloudEventV1<any>>,
|
options: Partial<CloudEventV1<any>>,
|
||||||
strict = true): CloudEvent<any> {
|
strict = true): CloudEvent<any> {
|
||||||
if (event instanceof CloudEvent) {
|
|
||||||
event = event.toJSON() as CloudEventV1<any>;
|
|
||||||
}
|
|
||||||
return new CloudEvent(Object.assign({}, event, options), strict);
|
return new CloudEvent(Object.assign({}, event, options), strict);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2021 The CloudEvents Authors
|
|
||||||
SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const schemaV1 = {
|
|
||||||
$ref: "#/definitions/event",
|
|
||||||
definitions: {
|
|
||||||
specversion: {
|
|
||||||
type: "string",
|
|
||||||
minLength: 1,
|
|
||||||
const: "1.0",
|
|
||||||
},
|
|
||||||
datacontenttype: {
|
|
||||||
type: "string",
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
type: ["object", "string", "array", "number", "boolean", "null"],
|
|
||||||
},
|
|
||||||
data_base64: {
|
|
||||||
type: "string",
|
|
||||||
},
|
|
||||||
event: {
|
|
||||||
properties: {
|
|
||||||
specversion: {
|
|
||||||
$ref: "#/definitions/specversion",
|
|
||||||
},
|
|
||||||
datacontenttype: {
|
|
||||||
$ref: "#/definitions/datacontenttype",
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
$ref: "#/definitions/data",
|
|
||||||
},
|
|
||||||
data_base64: {
|
|
||||||
$ref: "#/definitions/data_base64",
|
|
||||||
},
|
|
||||||
id: {
|
|
||||||
$ref: "#/definitions/id",
|
|
||||||
},
|
|
||||||
time: {
|
|
||||||
$ref: "#/definitions/time",
|
|
||||||
},
|
|
||||||
dataschema: {
|
|
||||||
$ref: "#/definitions/dataschema",
|
|
||||||
},
|
|
||||||
subject: {
|
|
||||||
$ref: "#/definitions/subject",
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
$ref: "#/definitions/type",
|
|
||||||
},
|
|
||||||
source: {
|
|
||||||
$ref: "#/definitions/source",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
required: ["specversion", "id", "type", "source"],
|
|
||||||
type: "object",
|
|
||||||
},
|
|
||||||
id: {
|
|
||||||
type: "string",
|
|
||||||
minLength: 1,
|
|
||||||
},
|
|
||||||
time: {
|
|
||||||
format: "js-date-time",
|
|
||||||
type: "string",
|
|
||||||
},
|
|
||||||
dataschema: {
|
|
||||||
type: "string",
|
|
||||||
format: "uri",
|
|
||||||
},
|
|
||||||
subject: {
|
|
||||||
type: "string",
|
|
||||||
minLength: 1,
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
type: "string",
|
|
||||||
minLength: 1,
|
|
||||||
},
|
|
||||||
source: {
|
|
||||||
format: "uri-reference",
|
|
||||||
type: "string",
|
|
||||||
minLength: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
type: "object",
|
|
||||||
};
|
|
|
@ -3,36 +3,26 @@
|
||||||
SPDX-License-Identifier: Apache-2.0
|
SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Ajv, { Options } from "ajv";
|
|
||||||
import { ValidationError } from "./validation";
|
import { ValidationError } from "./validation";
|
||||||
|
|
||||||
import { CloudEventV1 } from "./interfaces";
|
import { CloudEventV1 } from "./interfaces";
|
||||||
import { schemaV1 } from "./schemas";
|
import { V1 } from "./cloudevent";
|
||||||
import { Version } from "./cloudevent";
|
import validate from "../schema/v1";
|
||||||
|
|
||||||
const ajv = new Ajv({ extendRefs: true } as Options);
|
|
||||||
|
|
||||||
// handle date-time format specially because a user could pass
|
|
||||||
// Date().toString(), which is not spec compliant date-time format
|
|
||||||
ajv.addFormat("js-date-time", function (dateTimeString) {
|
|
||||||
const date = new Date(Date.parse(dateTimeString));
|
|
||||||
return date.toString() !== "Invalid Date";
|
|
||||||
});
|
|
||||||
|
|
||||||
const isValidAgainstSchemaV1 = ajv.compile(schemaV1);
|
|
||||||
|
|
||||||
export function validateCloudEvent<T>(event: CloudEventV1<T>): boolean {
|
export function validateCloudEvent<T>(event: CloudEventV1<T>): boolean {
|
||||||
if (event.specversion === Version.V1) {
|
if (event.specversion === V1) {
|
||||||
if (!isValidAgainstSchemaV1(event)) {
|
if (!validate(event)) {
|
||||||
throw new ValidationError("invalid payload", isValidAgainstSchemaV1.errors);
|
throw new ValidationError("invalid payload", (validate as any).errors);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// attribute names must all be lowercase
|
// attribute names must all be [a-z|0-9]
|
||||||
|
const validation = /^[a-z0-9]+$/;
|
||||||
for (const key in event) {
|
for (const key in event) {
|
||||||
if (key !== key.toLowerCase()) {
|
if (validation.test(key) === false && key !== "data_base64") {
|
||||||
throw new ValidationError(`invalid attribute name: ${key}`);
|
throw new ValidationError(`invalid attribute name: "${key}"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -5,6 +5,23 @@
|
||||||
|
|
||||||
import { ErrorObject } from "ajv";
|
import { ErrorObject } from "ajv";
|
||||||
|
|
||||||
|
export type TypeArray = Int8Array | Uint8Array | Int16Array | Uint16Array |
|
||||||
|
Int32Array | Uint32Array | Uint8ClampedArray | Float32Array | Float64Array;
|
||||||
|
|
||||||
|
const globalThisPolyfill = (function() {
|
||||||
|
try {
|
||||||
|
return globalThis;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
try {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An Error class that will be thrown when a CloudEvent
|
* An Error class that will be thrown when a CloudEvent
|
||||||
* cannot be properly validated against a specification.
|
* cannot be properly validated against a specification.
|
||||||
|
@ -36,7 +53,7 @@ export const isDefined = (v: unknown): boolean => v !== null && typeof v !== "un
|
||||||
export const isBoolean = (v: unknown): boolean => typeof v === "boolean";
|
export const isBoolean = (v: unknown): boolean => typeof v === "boolean";
|
||||||
export const isInteger = (v: unknown): boolean => Number.isInteger(v as number);
|
export const isInteger = (v: unknown): boolean => Number.isInteger(v as number);
|
||||||
export const isDate = (v: unknown): v is Date => v instanceof Date;
|
export const isDate = (v: unknown): v is Date => v instanceof Date;
|
||||||
export const isBinary = (v: unknown): v is Uint32Array => v instanceof Uint32Array;
|
export const isBinary = (v: unknown): boolean => ArrayBuffer.isView(v);
|
||||||
|
|
||||||
export const isStringOrThrow = (v: unknown, t: Error): boolean =>
|
export const isStringOrThrow = (v: unknown, t: Error): boolean =>
|
||||||
isString(v)
|
isString(v)
|
||||||
|
@ -73,7 +90,7 @@ export const isBase64 = (value: unknown): boolean =>
|
||||||
|
|
||||||
export const isBuffer = (value: unknown): boolean => value instanceof Buffer;
|
export const isBuffer = (value: unknown): boolean => value instanceof Buffer;
|
||||||
|
|
||||||
export const asBuffer = (value: string | Buffer | Uint32Array): Buffer =>
|
export const asBuffer = (value: string | Buffer | TypeArray): Buffer =>
|
||||||
isBinary(value)
|
isBinary(value)
|
||||||
? Buffer.from((value as unknown) as string)
|
? Buffer.from((value as unknown) as string)
|
||||||
: isBuffer(value)
|
: isBuffer(value)
|
||||||
|
@ -82,7 +99,16 @@ export const asBuffer = (value: string | Buffer | Uint32Array): Buffer =>
|
||||||
throw new TypeError("is not buffer or a valid binary");
|
throw new TypeError("is not buffer or a valid binary");
|
||||||
})();
|
})();
|
||||||
|
|
||||||
export const asBase64 = (value: string | Buffer | Uint32Array): string => asBuffer(value).toString("base64");
|
export const base64AsBinary = (base64String: string): Uint8Array => {
|
||||||
|
const toBinaryString = (base64Str: string): string => globalThisPolyfill.atob
|
||||||
|
? globalThisPolyfill.atob(base64Str)
|
||||||
|
: Buffer.from(base64Str, "base64").toString("binary");
|
||||||
|
|
||||||
|
return Uint8Array.from(toBinaryString(base64String), (c) => c.charCodeAt(0));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const asBase64 =
|
||||||
|
(value: string | Buffer | TypeArray): string => asBuffer(value).toString("base64");
|
||||||
|
|
||||||
export const clone = (o: Record<string, unknown>): Record<string, unknown> => JSON.parse(JSON.stringify(o));
|
export const clone = (o: Record<string, unknown>): Record<string, unknown> => JSON.parse(JSON.stringify(o));
|
||||||
|
|
||||||
|
@ -97,5 +123,5 @@ export const asData = (data: unknown, contentType: string): string => {
|
||||||
return isBinary(maybeJson) ? asBase64(maybeJson) : maybeJson;
|
return isBinary(maybeJson) ? asBase64(maybeJson) : maybeJson;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isValidType = (v: boolean | number | string | Date | Uint32Array | unknown): boolean =>
|
export const isValidType = (v: boolean | number | string | Date | TypeArray | unknown): boolean =>
|
||||||
isBoolean(v) || isInteger(v) || isString(v) || isDate(v) || isBinary(v) || isObject(v);
|
isBoolean(v) || isInteger(v) || isString(v) || isDate(v) || isBinary(v) || isObject(v);
|
||||||
|
|
32
src/index.ts
32
src/index.ts
|
@ -3,11 +3,12 @@
|
||||||
SPDX-License-Identifier: Apache-2.0
|
SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CloudEvent, Version } from "./event/cloudevent";
|
import { CloudEvent, V1, V03 } from "./event/cloudevent";
|
||||||
import { ValidationError } from "./event/validation";
|
import { ValidationError } from "./event/validation";
|
||||||
import { CloudEventV1, CloudEventV1Attributes } from "./event/interfaces";
|
import { CloudEventV1, CloudEventV1Attributes } from "./event/interfaces";
|
||||||
|
|
||||||
import { Options, TransportFunction, EmitterFunction, emitterFor, Emitter } from "./transport/emitter";
|
import { Options, TransportFunction, EmitterFunction, emitterFor, Emitter } from "./transport/emitter";
|
||||||
|
import { httpTransport } from "./transport/http";
|
||||||
import {
|
import {
|
||||||
Headers, Mode, Binding, HTTP, Kafka, KafkaEvent, KafkaMessage, Message, MQTT, MQTTMessage, MQTTMessageFactory,
|
Headers, Mode, Binding, HTTP, Kafka, KafkaEvent, KafkaMessage, Message, MQTT, MQTTMessage, MQTTMessageFactory,
|
||||||
Serializer, Deserializer } from "./message";
|
Serializer, Deserializer } from "./message";
|
||||||
|
@ -17,30 +18,35 @@ import CONSTANTS from "./constants";
|
||||||
export {
|
export {
|
||||||
// From event
|
// From event
|
||||||
CloudEvent,
|
CloudEvent,
|
||||||
|
V1,
|
||||||
|
V03,
|
||||||
|
ValidationError,
|
||||||
|
Mode,
|
||||||
|
HTTP,
|
||||||
|
Kafka,
|
||||||
|
MQTT,
|
||||||
|
MQTTMessageFactory,
|
||||||
|
emitterFor,
|
||||||
|
httpTransport,
|
||||||
|
Emitter,
|
||||||
|
// From Constants
|
||||||
|
CONSTANTS
|
||||||
|
};
|
||||||
|
|
||||||
|
export type {
|
||||||
CloudEventV1,
|
CloudEventV1,
|
||||||
CloudEventV1Attributes,
|
CloudEventV1Attributes,
|
||||||
Version,
|
|
||||||
ValidationError,
|
|
||||||
// From message
|
// From message
|
||||||
Headers,
|
Headers,
|
||||||
Mode,
|
|
||||||
Binding,
|
Binding,
|
||||||
Message,
|
Message,
|
||||||
Deserializer,
|
Deserializer,
|
||||||
Serializer,
|
Serializer,
|
||||||
HTTP,
|
|
||||||
Kafka,
|
|
||||||
KafkaEvent,
|
KafkaEvent,
|
||||||
KafkaMessage,
|
KafkaMessage,
|
||||||
MQTT,
|
|
||||||
MQTTMessage,
|
MQTTMessage,
|
||||||
MQTTMessageFactory,
|
|
||||||
// From transport
|
// From transport
|
||||||
TransportFunction,
|
TransportFunction,
|
||||||
EmitterFunction,
|
EmitterFunction,
|
||||||
emitterFor,
|
Options
|
||||||
Emitter,
|
|
||||||
Options,
|
|
||||||
// From Constants
|
|
||||||
CONSTANTS,
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import { PassThroughParser, DateParser, MappedParser } from "../../parsers";
|
import { PassThroughParser, DateParser, MappedParser } from "../../parsers";
|
||||||
import { CloudEventV1 } from "../..";
|
import { CloudEventV1 } from "../..";
|
||||||
import { Headers } from "../";
|
import { Headers } from "../";
|
||||||
import { Version } from "../../event/cloudevent";
|
import { V1 } from "../../event/cloudevent";
|
||||||
import CONSTANTS from "../../constants";
|
import CONSTANTS from "../../constants";
|
||||||
|
|
||||||
export const allowedContentTypes = [CONSTANTS.DEFAULT_CONTENT_TYPE, CONSTANTS.MIME_JSON, CONSTANTS.MIME_OCTET_STREAM];
|
export const allowedContentTypes = [CONSTANTS.DEFAULT_CONTENT_TYPE, CONSTANTS.MIME_JSON, CONSTANTS.MIME_OCTET_STREAM];
|
||||||
|
@ -27,7 +27,7 @@ export const requiredHeaders = [
|
||||||
export function headersFor<T>(event: CloudEventV1<T>): Headers {
|
export function headersFor<T>(event: CloudEventV1<T>): Headers {
|
||||||
const headers: Headers = {};
|
const headers: Headers = {};
|
||||||
let headerMap: Readonly<{ [key: string]: MappedParser }>;
|
let headerMap: Readonly<{ [key: string]: MappedParser }>;
|
||||||
if (event.specversion === Version.V1) {
|
if (event.specversion === V1) {
|
||||||
headerMap = v1headerMap;
|
headerMap = v1headerMap;
|
||||||
} else {
|
} else {
|
||||||
headerMap = v03headerMap;
|
headerMap = v03headerMap;
|
||||||
|
@ -36,7 +36,7 @@ export function headersFor<T>(event: CloudEventV1<T>): Headers {
|
||||||
// iterate over the event properties - generate a header for each
|
// iterate over the event properties - generate a header for each
|
||||||
Object.getOwnPropertyNames(event).forEach((property) => {
|
Object.getOwnPropertyNames(event).forEach((property) => {
|
||||||
const value = event[property];
|
const value = event[property];
|
||||||
if (value) {
|
if (value !== undefined) {
|
||||||
const map: MappedParser | undefined = headerMap[property] as MappedParser;
|
const map: MappedParser | undefined = headerMap[property] as MappedParser;
|
||||||
if (map) {
|
if (map) {
|
||||||
headers[map.name] = map.parser.parse(value as string) as string;
|
headers[map.name] = map.parser.parse(value as string) as string;
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
SPDX-License-Identifier: Apache-2.0
|
SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CloudEvent, CloudEventV1, CONSTANTS, Mode, Version } from "../..";
|
import { types } from "util";
|
||||||
|
|
||||||
|
import { CloudEvent, CloudEventV1, CONSTANTS, Mode, V1, V03 } from "../..";
|
||||||
import { Message, Headers, Binding } from "..";
|
import { Message, Headers, Binding } from "..";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -29,7 +31,7 @@ function binary<T>(event: CloudEventV1<T>): Message {
|
||||||
const contentType: Headers = { [CONSTANTS.HEADER_CONTENT_TYPE]: CONSTANTS.DEFAULT_CONTENT_TYPE };
|
const contentType: Headers = { [CONSTANTS.HEADER_CONTENT_TYPE]: CONSTANTS.DEFAULT_CONTENT_TYPE };
|
||||||
const headers: Headers = { ...contentType, ...headersFor(event) };
|
const headers: Headers = { ...contentType, ...headersFor(event) };
|
||||||
let body = event.data;
|
let body = event.data;
|
||||||
if (typeof event.data === "object" && !(event.data instanceof Uint32Array)) {
|
if (typeof event.data === "object" && !types.isTypedArray(event.data)) {
|
||||||
// we'll stringify objects, but not binary data
|
// we'll stringify objects, but not binary data
|
||||||
body = (JSON.stringify(event.data) as unknown) as T;
|
body = (JSON.stringify(event.data) as unknown) as T;
|
||||||
}
|
}
|
||||||
|
@ -145,7 +147,7 @@ function getVersion(mode: Mode, headers: Headers, body: string | Record<string,
|
||||||
return (body as Record<string, string>).specversion;
|
return (body as Record<string, string>).specversion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Version.V1;
|
return V1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -153,11 +155,11 @@ function getVersion(mode: Mode, headers: Headers, body: string | Record<string,
|
||||||
* instance if it conforms to the Cloud Event specification for this receiver.
|
* instance if it conforms to the Cloud Event specification for this receiver.
|
||||||
*
|
*
|
||||||
* @param {Message} message the incoming HTTP Message
|
* @param {Message} message the incoming HTTP Message
|
||||||
* @param {Version} version the spec version of the incoming event
|
* @param {string} version the spec version of the incoming event
|
||||||
* @returns {CloudEvent} an instance of CloudEvent representing the incoming request
|
* @returns {CloudEvent} an instance of CloudEvent representing the incoming request
|
||||||
* @throws {ValidationError} of the event does not conform to the spec
|
* @throws {ValidationError} of the event does not conform to the spec
|
||||||
*/
|
*/
|
||||||
function parseBinary<T>(message: Message, version: Version): CloudEvent<T> {
|
function parseBinary<T>(message: Message, version: string): CloudEvent<T> {
|
||||||
const headers = { ...message.headers };
|
const headers = { ...message.headers };
|
||||||
let body = message.body;
|
let body = message.body;
|
||||||
|
|
||||||
|
@ -167,7 +169,7 @@ function parseBinary<T>(message: Message, version: Version): CloudEvent<T> {
|
||||||
const sanitizedHeaders = sanitize(headers);
|
const sanitizedHeaders = sanitize(headers);
|
||||||
|
|
||||||
const eventObj: { [key: string]: unknown | string | Record<string, unknown> } = {};
|
const eventObj: { [key: string]: unknown | string | Record<string, unknown> } = {};
|
||||||
const parserMap: Record<string, MappedParser> = version === Version.V03 ? v03binaryParsers : v1binaryParsers;
|
const parserMap: Record<string, MappedParser> = version === V03 ? v03binaryParsers : v1binaryParsers;
|
||||||
|
|
||||||
for (const header in parserMap) {
|
for (const header in parserMap) {
|
||||||
if (sanitizedHeaders[header]) {
|
if (sanitizedHeaders[header]) {
|
||||||
|
@ -204,11 +206,11 @@ function parseBinary<T>(message: Message, version: Version): CloudEvent<T> {
|
||||||
* Creates a new CloudEvent instance based on the provided payload and headers.
|
* Creates a new CloudEvent instance based on the provided payload and headers.
|
||||||
*
|
*
|
||||||
* @param {Message} message the incoming Message
|
* @param {Message} message the incoming Message
|
||||||
* @param {Version} version the spec version of this message (v1 or v03)
|
* @param {string} version the spec version of this message (v1 or v03)
|
||||||
* @returns {CloudEvent} a new CloudEvent instance for the provided headers and payload
|
* @returns {CloudEvent} a new CloudEvent instance for the provided headers and payload
|
||||||
* @throws {ValidationError} if the payload and header combination do not conform to the spec
|
* @throws {ValidationError} if the payload and header combination do not conform to the spec
|
||||||
*/
|
*/
|
||||||
function parseStructured<T>(message: Message, version: Version): CloudEvent<T> {
|
function parseStructured<T>(message: Message, version: string): CloudEvent<T> {
|
||||||
const payload = message.body;
|
const payload = message.body;
|
||||||
const headers = message.headers;
|
const headers = message.headers;
|
||||||
|
|
||||||
|
@ -225,7 +227,7 @@ function parseStructured<T>(message: Message, version: Version): CloudEvent<T> {
|
||||||
const incoming = { ...(parser.parse(payload as string) as Record<string, unknown>) };
|
const incoming = { ...(parser.parse(payload as string) as Record<string, unknown>) };
|
||||||
|
|
||||||
const eventObj: { [key: string]: unknown } = {};
|
const eventObj: { [key: string]: unknown } = {};
|
||||||
const parserMap: Record<string, MappedParser> = version === Version.V03 ? v03structuredParsers : v1structuredParsers;
|
const parserMap: Record<string, MappedParser> = version === V03 ? v03structuredParsers : v1structuredParsers;
|
||||||
|
|
||||||
for (const key in parserMap) {
|
for (const key in parserMap) {
|
||||||
const property = incoming[key];
|
const property = incoming[key];
|
||||||
|
|
|
@ -22,9 +22,9 @@ export * from "./mqtt";
|
||||||
* @property {@link Deserializer} `toEvent` - converts a Message into a CloudEvent
|
* @property {@link Deserializer} `toEvent` - converts a Message into a CloudEvent
|
||||||
* @property {@link Detector} `isEvent` - determines if a Message can be converted to a CloudEvent
|
* @property {@link Detector} `isEvent` - determines if a Message can be converted to a CloudEvent
|
||||||
*/
|
*/
|
||||||
export interface Binding {
|
export interface Binding<B extends Message = Message, S extends Message = Message> {
|
||||||
binary: Serializer;
|
binary: Serializer<B>;
|
||||||
structured: Serializer;
|
structured: Serializer<S>;
|
||||||
toEvent: Deserializer;
|
toEvent: Deserializer;
|
||||||
isEvent: Detector;
|
isEvent: Detector;
|
||||||
}
|
}
|
||||||
|
@ -65,8 +65,8 @@ export enum Mode {
|
||||||
* CloudEvent into a Message.
|
* CloudEvent into a Message.
|
||||||
* @interface
|
* @interface
|
||||||
*/
|
*/
|
||||||
export interface Serializer {
|
export interface Serializer<M extends Message> {
|
||||||
<T>(event: CloudEventV1<T>): Message;
|
<T>(event: CloudEventV1<T>): M;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -10,7 +10,10 @@ import { sanitize } from "../http/headers";
|
||||||
|
|
||||||
// Export the binding implementation and message interface
|
// Export the binding implementation and message interface
|
||||||
export {
|
export {
|
||||||
Kafka,
|
Kafka
|
||||||
|
};
|
||||||
|
|
||||||
|
export type {
|
||||||
KafkaMessage,
|
KafkaMessage,
|
||||||
KafkaEvent
|
KafkaEvent
|
||||||
};
|
};
|
||||||
|
@ -19,7 +22,7 @@ export {
|
||||||
* Bindings for Kafka transport
|
* Bindings for Kafka transport
|
||||||
* @implements {@linkcode Binding}
|
* @implements {@linkcode Binding}
|
||||||
*/
|
*/
|
||||||
const Kafka: Binding = {
|
const Kafka: Binding<KafkaMessage<unknown>, KafkaMessage<string>> = {
|
||||||
binary: toBinaryKafkaMessage,
|
binary: toBinaryKafkaMessage,
|
||||||
structured: toStructuredKafkaMessage,
|
structured: toStructuredKafkaMessage,
|
||||||
toEvent: deserializeKafkaMessage,
|
toEvent: deserializeKafkaMessage,
|
||||||
|
@ -32,9 +35,9 @@ type Key = string | Buffer;
|
||||||
* Extends the base Message type to include
|
* Extends the base Message type to include
|
||||||
* Kafka-specific fields
|
* Kafka-specific fields
|
||||||
*/
|
*/
|
||||||
interface KafkaMessage<T = string> extends Message {
|
interface KafkaMessage<T = string | Buffer | unknown> extends Message {
|
||||||
key: Key
|
key: Key
|
||||||
value: T | string | Buffer | unknown
|
value: T
|
||||||
timestamp?: string
|
timestamp?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +61,7 @@ interface KafkaEvent<T> extends CloudEventV1<T> {
|
||||||
* @param {KafkaEvent<T>} event The event to serialize
|
* @param {KafkaEvent<T>} event The event to serialize
|
||||||
* @returns {KafkaMessage<T>} a KafkaMessage instance
|
* @returns {KafkaMessage<T>} a KafkaMessage instance
|
||||||
*/
|
*/
|
||||||
function toBinaryKafkaMessage<T>(event: CloudEventV1<T>): KafkaMessage<T> {
|
function toBinaryKafkaMessage<T>(event: CloudEventV1<T>): KafkaMessage<T | undefined> {
|
||||||
// 3.2.1. Content Type
|
// 3.2.1. Content Type
|
||||||
// For the binary mode, the header content-type property MUST be mapped directly
|
// For the binary mode, the header content-type property MUST be mapped directly
|
||||||
// to the CloudEvents datacontenttype attribute.
|
// to the CloudEvents datacontenttype attribute.
|
||||||
|
@ -83,7 +86,7 @@ function toBinaryKafkaMessage<T>(event: CloudEventV1<T>): KafkaMessage<T> {
|
||||||
* @param {CloudEvent<T>} event the CloudEvent to be serialized
|
* @param {CloudEvent<T>} event the CloudEvent to be serialized
|
||||||
* @returns {KafkaMessage<T>} a KafkaMessage instance
|
* @returns {KafkaMessage<T>} a KafkaMessage instance
|
||||||
*/
|
*/
|
||||||
function toStructuredKafkaMessage<T>(event: CloudEventV1<T>): KafkaMessage<T> {
|
function toStructuredKafkaMessage<T>(event: CloudEventV1<T>): KafkaMessage<string> {
|
||||||
if ((event instanceof CloudEvent) && event.data_base64) {
|
if ((event instanceof CloudEvent) && event.data_base64) {
|
||||||
// The event's data is binary - delete it
|
// The event's data is binary - delete it
|
||||||
event = event.cloneWith({ data: undefined });
|
event = event.cloneWith({ data: undefined });
|
||||||
|
@ -127,9 +130,9 @@ function deserializeKafkaMessage<T>(message: Message): CloudEvent<T> | CloudEven
|
||||||
case Mode.BINARY:
|
case Mode.BINARY:
|
||||||
return parseBinary(m);
|
return parseBinary(m);
|
||||||
case Mode.STRUCTURED:
|
case Mode.STRUCTURED:
|
||||||
return parseStructured(m);
|
return parseStructured(m as unknown as KafkaMessage<string>);
|
||||||
case Mode.BATCH:
|
case Mode.BATCH:
|
||||||
return parseBatched(m);
|
return parseBatched(m as unknown as KafkaMessage<string>);
|
||||||
default:
|
default:
|
||||||
throw new ValidationError("Unknown Message mode");
|
throw new ValidationError("Unknown Message mode");
|
||||||
}
|
}
|
||||||
|
@ -209,14 +212,14 @@ function parseBinary<T>(message: KafkaMessage<T>): CloudEvent<T> {
|
||||||
* @param {KafkaMessage<T>} message the message
|
* @param {KafkaMessage<T>} message the message
|
||||||
* @returns {CloudEvent<T>} a KafkaEvent<T>
|
* @returns {CloudEvent<T>} a KafkaEvent<T>
|
||||||
*/
|
*/
|
||||||
function parseStructured<T>(message: KafkaMessage<T>): CloudEvent<T> {
|
function parseStructured<T>(message: KafkaMessage<string>): CloudEvent<T> {
|
||||||
// Although the format of a structured encoded event could be something
|
// Although the format of a structured encoded event could be something
|
||||||
// other than JSON, e.g. XML, we currently only support JSON
|
// other than JSON, e.g. XML, we currently only support JSON
|
||||||
// encoded structured events.
|
// encoded structured events.
|
||||||
if (!message.headers[CONSTANTS.HEADER_CONTENT_TYPE]?.startsWith(CONSTANTS.MIME_CE_JSON)) {
|
if (!message.headers[CONSTANTS.HEADER_CONTENT_TYPE]?.startsWith(CONSTANTS.MIME_CE_JSON)) {
|
||||||
throw new ValidationError(`Unsupported event encoding ${message.headers[CONSTANTS.HEADER_CONTENT_TYPE]}`);
|
throw new ValidationError(`Unsupported event encoding ${message.headers[CONSTANTS.HEADER_CONTENT_TYPE]}`);
|
||||||
}
|
}
|
||||||
const eventObj = JSON.parse(message.value as string);
|
const eventObj = JSON.parse(message.value);
|
||||||
eventObj.time = new Date(eventObj.time).toISOString();
|
eventObj.time = new Date(eventObj.time).toISOString();
|
||||||
return new CloudEvent({
|
return new CloudEvent({
|
||||||
...eventObj,
|
...eventObj,
|
||||||
|
@ -229,14 +232,14 @@ function parseStructured<T>(message: KafkaMessage<T>): CloudEvent<T> {
|
||||||
* @param {KafkaMessage<T>} message the message
|
* @param {KafkaMessage<T>} message the message
|
||||||
* @returns {CloudEvent<T>[]} an array of KafkaEvent<T>
|
* @returns {CloudEvent<T>[]} an array of KafkaEvent<T>
|
||||||
*/
|
*/
|
||||||
function parseBatched<T>(message: KafkaMessage<T>): CloudEvent<T>[] {
|
function parseBatched<T>(message: KafkaMessage<string>): CloudEvent<T>[] {
|
||||||
// Although the format of batch encoded events could be something
|
// Although the format of batch encoded events could be something
|
||||||
// other than JSON, e.g. XML, we currently only support JSON
|
// other than JSON, e.g. XML, we currently only support JSON
|
||||||
// encoded structured events.
|
// encoded structured events.
|
||||||
if (!message.headers[CONSTANTS.HEADER_CONTENT_TYPE]?.startsWith(CONSTANTS.MIME_CE_BATCH)) {
|
if (!message.headers[CONSTANTS.HEADER_CONTENT_TYPE]?.startsWith(CONSTANTS.MIME_CE_BATCH)) {
|
||||||
throw new ValidationError(`Unsupported event encoding ${message.headers[CONSTANTS.HEADER_CONTENT_TYPE]}`);
|
throw new ValidationError(`Unsupported event encoding ${message.headers[CONSTANTS.HEADER_CONTENT_TYPE]}`);
|
||||||
}
|
}
|
||||||
const events = JSON.parse(message.value as string) as Record<string, unknown>[];
|
const events = JSON.parse(message.value) as Record<string, unknown>[];
|
||||||
return events.map((e) => new CloudEvent({ ...e, partitionkey: message.key }, false));
|
return events.map((e) => new CloudEvent({ ...e, partitionkey: message.key }, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,18 +4,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Binding, Deserializer, CloudEvent, CloudEventV1, CONSTANTS, Message, ValidationError, Headers } from "../..";
|
import { Binding, Deserializer, CloudEvent, CloudEventV1, CONSTANTS, Message, ValidationError, Headers } from "../..";
|
||||||
|
import { base64AsBinary } from "../../event/validation";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
MQTT,
|
MQTT, MQTTMessageFactory
|
||||||
MQTTMessage,
|
|
||||||
MQTTMessageFactory
|
|
||||||
};
|
};
|
||||||
|
export type { MQTTMessage };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extends the base {@linkcode Message} interface to include MQTT attributes, some of which
|
* Extends the base {@linkcode Message} interface to include MQTT attributes, some of which
|
||||||
* are aliases of the {Message} attributes.
|
* are aliases of the {Message} attributes.
|
||||||
*/
|
*/
|
||||||
interface MQTTMessage<T> extends Message<T> {
|
interface MQTTMessage<T = unknown> extends Message<T> {
|
||||||
/**
|
/**
|
||||||
* Identifies this message as a PUBLISH packet. MQTTMessages created with
|
* Identifies this message as a PUBLISH packet. MQTTMessages created with
|
||||||
* the `binary` and `structured` Serializers will contain a "Content Type"
|
* the `binary` and `structured` Serializers will contain a "Content Type"
|
||||||
|
@ -37,7 +37,7 @@ interface MQTTMessage<T> extends Message<T> {
|
||||||
* Binding for MQTT transport support
|
* Binding for MQTT transport support
|
||||||
* @implements @linkcode Binding
|
* @implements @linkcode Binding
|
||||||
*/
|
*/
|
||||||
const MQTT: Binding = {
|
const MQTT: Binding<MQTTMessage, MQTTMessage> = {
|
||||||
binary,
|
binary,
|
||||||
structured,
|
structured,
|
||||||
toEvent: toEvent as Deserializer,
|
toEvent: toEvent as Deserializer,
|
||||||
|
@ -51,14 +51,16 @@ const MQTT: Binding = {
|
||||||
* @implements {Serializer}
|
* @implements {Serializer}
|
||||||
*/
|
*/
|
||||||
function binary<T>(event: CloudEventV1<T>): MQTTMessage<T> {
|
function binary<T>(event: CloudEventV1<T>): MQTTMessage<T> {
|
||||||
let properties;
|
const properties = { ...event };
|
||||||
if (event instanceof CloudEvent) {
|
|
||||||
properties = event.toJSON();
|
let body = properties.data as T;
|
||||||
} else {
|
|
||||||
properties = event;
|
if (!body && properties.data_base64) {
|
||||||
|
body = base64AsBinary(properties.data_base64) as unknown as T;
|
||||||
}
|
}
|
||||||
const body = properties.data as T;
|
|
||||||
delete properties.data;
|
delete properties.data;
|
||||||
|
delete properties.data_base64;
|
||||||
|
|
||||||
return MQTTMessageFactory(event.datacontenttype as string, properties, body);
|
return MQTTMessageFactory(event.datacontenttype as string, properties, body);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
SPDX-License-Identifier: Apache-2.0
|
SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import JSONbig from "json-bigint";
|
||||||
import CONSTANTS from "./constants";
|
import CONSTANTS from "./constants";
|
||||||
import { isString, isDefinedOrThrow, isStringOrObjectOrThrow, ValidationError } from "./event/validation";
|
import { isString, isDefinedOrThrow, isStringOrObjectOrThrow, ValidationError } from "./event/validation";
|
||||||
|
|
||||||
|
const __JSON = JSON;
|
||||||
export abstract class Parser {
|
export abstract class Parser {
|
||||||
abstract parse(payload: Record<string, unknown> | string | string[] | undefined): unknown;
|
abstract parse(payload: Record<string, unknown> | string | string[] | undefined): unknown;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +38,13 @@ export class JSONParser implements Parser {
|
||||||
|
|
||||||
isDefinedOrThrow(payload, new ValidationError("null or undefined payload"));
|
isDefinedOrThrow(payload, new ValidationError("null or undefined payload"));
|
||||||
isStringOrObjectOrThrow(payload, new ValidationError("invalid payload type, allowed are: string or object"));
|
isStringOrObjectOrThrow(payload, new ValidationError("invalid payload type, allowed are: string or object"));
|
||||||
|
|
||||||
|
if (process.env[CONSTANTS.USE_BIG_INT_ENV] === "true") {
|
||||||
|
JSON = JSONbig(({ useNativeBigInt: true })) as JSON;
|
||||||
|
} else {
|
||||||
|
JSON = __JSON;
|
||||||
|
}
|
||||||
|
|
||||||
const parseJSON = (v: Record<string, unknown> | string): string => (isString(v) ? JSON.parse(v as string) : v);
|
const parseJSON = (v: Record<string, unknown> | string): string => (isString(v) ? JSON.parse(v as string) : v);
|
||||||
return parseJSON(payload);
|
return parseJSON(payload);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"description": "CloudEvents Specification JSON Schema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"description": "Identifies the event.",
|
||||||
|
"$ref": "#/definitions/iddef",
|
||||||
|
"examples": [
|
||||||
|
"A234-1234-1234"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"description": "Identifies the context in which an event happened.",
|
||||||
|
"$ref": "#/definitions/sourcedef",
|
||||||
|
"examples" : [
|
||||||
|
"https://github.com/cloudevents",
|
||||||
|
"mailto:cncf-wg-serverless@lists.cncf.io",
|
||||||
|
"urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66",
|
||||||
|
"cloudevents/spec/pull/123",
|
||||||
|
"/sensors/tn-1234567/alerts",
|
||||||
|
"1-555-123-4567"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"specversion": {
|
||||||
|
"description": "The version of the CloudEvents specification which the event uses.",
|
||||||
|
"$ref": "#/definitions/specversiondef",
|
||||||
|
"examples": [
|
||||||
|
"1.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"description": "Describes the type of event related to the originating occurrence.",
|
||||||
|
"$ref": "#/definitions/typedef",
|
||||||
|
"examples" : [
|
||||||
|
"com.github.pull_request.opened",
|
||||||
|
"com.example.object.deleted.v2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"datacontenttype": {
|
||||||
|
"description": "Content type of the data value. Must adhere to RFC 2046 format.",
|
||||||
|
"$ref": "#/definitions/datacontenttypedef",
|
||||||
|
"examples": [
|
||||||
|
"text/xml",
|
||||||
|
"application/json",
|
||||||
|
"image/png",
|
||||||
|
"multipart/form-data"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dataschema": {
|
||||||
|
"description": "Identifies the schema that data adheres to.",
|
||||||
|
"$ref": "#/definitions/dataschemadef"
|
||||||
|
},
|
||||||
|
"subject": {
|
||||||
|
"description": "Describes the subject of the event in the context of the event producer (identified by source).",
|
||||||
|
"$ref": "#/definitions/subjectdef",
|
||||||
|
"examples": [
|
||||||
|
"mynewfile.jpg"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"description": "Timestamp of when the occurrence happened. Must adhere to RFC 3339.",
|
||||||
|
"$ref": "#/definitions/timedef",
|
||||||
|
"examples": [
|
||||||
|
"2018-04-05T17:31:00Z"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"description": "The event payload.",
|
||||||
|
"$ref": "#/definitions/datadef",
|
||||||
|
"examples": [
|
||||||
|
"<much wow=\"xml\"/>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"data_base64": {
|
||||||
|
"description": "Base64 encoded event payload. Must adhere to RFC4648.",
|
||||||
|
"$ref": "#/definitions/data_base64def",
|
||||||
|
"examples": [
|
||||||
|
"Zm9vYg=="
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["id", "source", "specversion", "type"],
|
||||||
|
"definitions": {
|
||||||
|
"iddef": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"sourcedef": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uri-reference",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"specversiondef": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"typedef": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"datacontenttypedef": {
|
||||||
|
"type": ["string", "null"],
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"dataschemadef": {
|
||||||
|
"type": ["string", "null"],
|
||||||
|
"format": "uri",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"subjectdef": {
|
||||||
|
"type": ["string", "null"],
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"timedef": {
|
||||||
|
"type": ["string", "null"],
|
||||||
|
"format": "date-time",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"datadef": {
|
||||||
|
"type": ["object", "string", "number", "array", "boolean", "null"]
|
||||||
|
},
|
||||||
|
"data_base64def": {
|
||||||
|
"type": ["string", "null"],
|
||||||
|
"contentEncoding": "base64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 The CloudEvents Authors
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
function formats(ajv) {
|
||||||
|
require("ajv-formats")(ajv);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = formats;
|
|
@ -3,20 +3,61 @@
|
||||||
SPDX-License-Identifier: Apache-2.0
|
SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Message, Options } from "../..";
|
import { Socket } from "net";
|
||||||
import axios from "axios";
|
import http, { OutgoingHttpHeaders } from "http";
|
||||||
|
import https, { RequestOptions } from "https";
|
||||||
|
|
||||||
export function axiosEmitter(sink: string) {
|
import { Message, Options } from "../..";
|
||||||
return function (message: Message, options?: Options): Promise<unknown> {
|
import { TransportFunction } from "../emitter";
|
||||||
options = { ...options };
|
|
||||||
const headers = {
|
/**
|
||||||
...message.headers,
|
* httpTransport provides a simple HTTP Transport function, which can send a CloudEvent,
|
||||||
...(options.headers as Record<string, string>),
|
* encoded as a Message to the endpoint. The returned function can be used with emitterFor()
|
||||||
};
|
* to provide an event emitter, for example:
|
||||||
delete options.headers;
|
*
|
||||||
return axios.post(sink, message.body, {
|
* const emitter = emitterFor(httpTransport("http://example.com"));
|
||||||
headers: headers,
|
* emitter.emit(myCloudEvent)
|
||||||
...options,
|
* .then(resp => console.log(resp));
|
||||||
|
*
|
||||||
|
* @param {string|URL} sink the destination endpoint for the event
|
||||||
|
* @returns {TransportFunction} a function which can be used to send CloudEvents to _sink_
|
||||||
|
*/
|
||||||
|
export function httpTransport(sink: string | URL): TransportFunction {
|
||||||
|
const url = new URL(sink);
|
||||||
|
let base: any;
|
||||||
|
if (url.protocol === "https:") {
|
||||||
|
base = https;
|
||||||
|
} else if (url.protocol === "http:") {
|
||||||
|
base = http;
|
||||||
|
} else {
|
||||||
|
throw new TypeError(`unsupported protocol ${url.protocol}`);
|
||||||
|
}
|
||||||
|
return function(message: Message, options?: Options): Promise<unknown> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
options = { ...options };
|
||||||
|
|
||||||
|
// TODO: Callers should be able to set any Node.js RequestOptions
|
||||||
|
const opts: RequestOptions = {
|
||||||
|
method: "POST",
|
||||||
|
headers: {...message.headers, ...options.headers as OutgoingHttpHeaders},
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const response = {
|
||||||
|
body: "",
|
||||||
|
headers: {},
|
||||||
|
};
|
||||||
|
const req = base.request(url, opts, (res: Socket) => {
|
||||||
|
res.setEncoding("utf-8");
|
||||||
|
response.headers = (res as any).headers;
|
||||||
|
res.on("data", (chunk) => response.body += chunk);
|
||||||
|
res.on("end", () => { resolve(response); });
|
||||||
|
});
|
||||||
|
req.on("error", reject);
|
||||||
|
req.write(message.body);
|
||||||
|
req.end();
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,25 +7,39 @@ import path from "path";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import { CloudEvent, ValidationError, Version } from "../../src";
|
import { CloudEvent, CloudEventV1, ValidationError, V1 } from "../../src";
|
||||||
import { asBase64 } from "../../src/event/validation";
|
import { asBase64 } from "../../src/event/validation";
|
||||||
import { ErrorObject } from "schema-utils/declarations/validate";
|
|
||||||
|
|
||||||
const type = "org.cncf.cloudevents.example";
|
const type = "org.cncf.cloudevents.example";
|
||||||
const source = "http://unit.test";
|
const source = "http://unit.test";
|
||||||
const id = "b46cf653-d48a-4b90-8dfa-355c01061361";
|
const id = "b46cf653-d48a-4b90-8dfa-355c01061361";
|
||||||
|
|
||||||
const fixture = {
|
const fixture = Object.freeze({
|
||||||
id,
|
id,
|
||||||
specversion: Version.V1,
|
specversion: V1,
|
||||||
source,
|
source,
|
||||||
type,
|
type,
|
||||||
data: `"some data"`,
|
data: `"some data"`
|
||||||
};
|
});
|
||||||
|
|
||||||
const imageData = new Uint32Array(fs.readFileSync(path.join(process.cwd(), "test", "integration", "ce.png")));
|
const imageData = new Uint32Array(fs.readFileSync(path.join(process.cwd(), "test", "integration", "ce.png")));
|
||||||
const image_base64 = asBase64(imageData);
|
const image_base64 = asBase64(imageData);
|
||||||
|
|
||||||
|
// Do not replace this with the assignment of a class instance
|
||||||
|
// as we just want to test if we can enumerate all explicitly defined fields!
|
||||||
|
const cloudEventV1InterfaceFields: (keyof CloudEventV1<unknown>)[] = Object.keys({
|
||||||
|
id: "",
|
||||||
|
type: "",
|
||||||
|
data: undefined,
|
||||||
|
data_base64: "",
|
||||||
|
source: "",
|
||||||
|
time: "",
|
||||||
|
datacontenttype: "",
|
||||||
|
dataschema: "",
|
||||||
|
specversion: "",
|
||||||
|
subject: ""
|
||||||
|
} as Required<CloudEventV1<unknown>>);
|
||||||
|
|
||||||
describe("A CloudEvent", () => {
|
describe("A CloudEvent", () => {
|
||||||
it("Can be constructed with a typed Message", () => {
|
it("Can be constructed with a typed Message", () => {
|
||||||
const ce = new CloudEvent(fixture);
|
const ce = new CloudEvent(fixture);
|
||||||
|
@ -68,10 +82,10 @@ describe("A CloudEvent", () => {
|
||||||
}).throw("invalid extension name");
|
}).throw("invalid extension name");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Throw a validation error for invalid extension names, more than 20 chars", () => {
|
it("Not throw a validation error for invalid extension names, more than 20 chars", () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
new CloudEvent({ "123456789012345678901": "extension1", ...fixture });
|
new CloudEvent({ "123456789012345678901": "extension1", ...fixture });
|
||||||
}).throw("invalid extension name");
|
}).not.throw("invalid extension name");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Throws a validation error for invalid uppercase extension names", () => {
|
it("Throws a validation error for invalid uppercase extension names", () => {
|
||||||
|
@ -79,6 +93,58 @@ describe("A CloudEvent", () => {
|
||||||
new CloudEvent({ ExtensionWithCaps: "extension value", ...fixture });
|
new CloudEvent({ ExtensionWithCaps: "extension value", ...fixture });
|
||||||
}).throw("invalid extension name");
|
}).throw("invalid extension name");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("CloudEventV1 interface fields should be enumerable", () => {
|
||||||
|
const classInstanceKeys = Object.keys(new CloudEvent({ ...fixture }));
|
||||||
|
|
||||||
|
for (const key of cloudEventV1InterfaceFields) {
|
||||||
|
expect(classInstanceKeys).to.contain(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws TypeError on trying to set any field value", () => {
|
||||||
|
const ce = new CloudEvent({
|
||||||
|
...fixture,
|
||||||
|
mycustomfield: "initialValue"
|
||||||
|
});
|
||||||
|
|
||||||
|
const keySet = new Set([...cloudEventV1InterfaceFields, ...Object.keys(ce)]);
|
||||||
|
|
||||||
|
expect(keySet).not.to.be.empty;
|
||||||
|
|
||||||
|
for (const cloudEventKey of keySet) {
|
||||||
|
let threw = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
ce[cloudEventKey] = "newValue";
|
||||||
|
} catch (err) {
|
||||||
|
threw = true;
|
||||||
|
expect(err).to.be.instanceOf(TypeError);
|
||||||
|
expect((err as TypeError).message).to.include("Cannot assign to read only property");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!threw) {
|
||||||
|
expect.fail(`Assigning a value to ${cloudEventKey} did not throw`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("toJSON()", () => {
|
||||||
|
it("does not return data field if data_base64 field is set to comply with JSON format spec 3.1.1", () => {
|
||||||
|
const binaryData = new Uint8Array([1,2,3]);
|
||||||
|
|
||||||
|
const ce = new CloudEvent({
|
||||||
|
...fixture,
|
||||||
|
data: binaryData
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(ce.data).to.be.equal(binaryData);
|
||||||
|
|
||||||
|
const json = ce.toJSON();
|
||||||
|
expect(json.data).to.not.exist;
|
||||||
|
expect(json.data_base64).to.be.equal("AQID");
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("A 1.0 CloudEvent", () => {
|
describe("A 1.0 CloudEvent", () => {
|
||||||
|
@ -99,7 +165,7 @@ describe("A 1.0 CloudEvent", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("can be constructed with an ID", () => {
|
it("can be constructed with an ID", () => {
|
||||||
const ce = new CloudEvent({ id: "1234", specversion: Version.V1, source, type });
|
const ce = new CloudEvent({ id: "1234", specversion: V1, source, type });
|
||||||
expect(ce.id).to.equal("1234");
|
expect(ce.id).to.equal("1234");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -214,7 +280,7 @@ describe("A 1.0 CloudEvent", () => {
|
||||||
const obj = JSON.parse(json as string);
|
const obj = JSON.parse(json as string);
|
||||||
expect(obj.type).to.equal(type);
|
expect(obj.type).to.equal(type);
|
||||||
expect(obj.source).to.equal(source);
|
expect(obj.source).to.equal(source);
|
||||||
expect(obj.specversion).to.equal(Version.V1);
|
expect(obj.specversion).to.equal(V1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("throws if the provded source is empty string", () => {
|
it("throws if the provded source is empty string", () => {
|
||||||
|
@ -225,13 +291,12 @@ describe("A 1.0 CloudEvent", () => {
|
||||||
type: "my.event.type",
|
type: "my.event.type",
|
||||||
source: "",
|
source: "",
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
expect(err).to.be.instanceOf(ValidationError);
|
expect(err).to.be.instanceOf(ValidationError);
|
||||||
const e = err as unknown as ValidationError;
|
const error = err.errors[0] as any;
|
||||||
const errors = e.errors as ErrorObject[];
|
expect(err.message).to.include("invalid payload");
|
||||||
expect(e.message).to.include("invalid payload");
|
expect(error.instancePath).to.equal("/source");
|
||||||
expect(errors[0].dataPath).to.equal(".source");
|
expect(error.keyword).to.equal("minLength");
|
||||||
expect(errors[0].keyword).to.equal("minLength");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,12 +6,13 @@
|
||||||
import "mocha";
|
import "mocha";
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import nock from "nock";
|
import nock from "nock";
|
||||||
import axios from "axios";
|
import axios, { AxiosRequestHeaders } from "axios";
|
||||||
import request from "superagent";
|
import request from "superagent";
|
||||||
import got from "got";
|
import got from "got";
|
||||||
|
|
||||||
import CONSTANTS from "../../src/constants";
|
import CONSTANTS from "../../src/constants";
|
||||||
import { CloudEvent, emitterFor, HTTP, Mode, Message, Options, TransportFunction } from "../../src";
|
import { CloudEvent, HTTP, Message, Mode, Options, TransportFunction, emitterFor, httpTransport }
|
||||||
|
from "../../src";
|
||||||
|
|
||||||
const DEFAULT_CE_CONTENT_TYPE = CONSTANTS.DEFAULT_CE_CONTENT_TYPE;
|
const DEFAULT_CE_CONTENT_TYPE = CONSTANTS.DEFAULT_CE_CONTENT_TYPE;
|
||||||
const sink = "https://cloudevents.io/";
|
const sink = "https://cloudevents.io/";
|
||||||
|
@ -38,7 +39,7 @@ export const fixture = new CloudEvent({
|
||||||
});
|
});
|
||||||
|
|
||||||
function axiosEmitter(message: Message, options?: Options): Promise<unknown> {
|
function axiosEmitter(message: Message, options?: Options): Promise<unknown> {
|
||||||
return axios.post(sink, message.body, { headers: message.headers, ...options });
|
return axios.post(sink, message.body, { headers: message.headers as AxiosRequestHeaders, ...options });
|
||||||
}
|
}
|
||||||
|
|
||||||
function superagentEmitter(message: Message, options?: Options): Promise<unknown> {
|
function superagentEmitter(message: Message, options?: Options): Promise<unknown> {
|
||||||
|
@ -83,7 +84,6 @@ describe("emitterFor() defaults", () => {
|
||||||
|
|
||||||
it("Supports HTTP binding, structured mode", () => {
|
it("Supports HTTP binding, structured mode", () => {
|
||||||
function transport(message: Message): Promise<unknown> {
|
function transport(message: Message): Promise<unknown> {
|
||||||
console.error(message);
|
|
||||||
// A structured message will have the application/cloudevents+json header
|
// A structured message will have the application/cloudevents+json header
|
||||||
expect(message.headers["content-type"]).to.equal(CONSTANTS.DEFAULT_CE_CONTENT_TYPE);
|
expect(message.headers["content-type"]).to.equal(CONSTANTS.DEFAULT_CE_CONTENT_TYPE);
|
||||||
const body = JSON.parse(message.body as string);
|
const body = JSON.parse(message.body as string);
|
||||||
|
@ -101,33 +101,50 @@ describe("emitterFor() defaults", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function setupMock(uri: string) {
|
||||||
|
nock(uri)
|
||||||
|
.post("/")
|
||||||
|
.reply(function (uri: string, body: nock.Body) {
|
||||||
|
// return the request body and the headers so they can be
|
||||||
|
// examined in the test
|
||||||
|
if (typeof body === "string") {
|
||||||
|
body = JSON.parse(body);
|
||||||
|
}
|
||||||
|
const returnBody = { ...(body as Record<string, unknown>), ...this.req.headers };
|
||||||
|
return [201, returnBody];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
describe("HTTP Transport Binding for emitterFactory", () => {
|
describe("HTTP Transport Binding for emitterFactory", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => { setupMock(sink); });
|
||||||
nock(sink)
|
|
||||||
.post("/")
|
describe("HTTPS builtin", () => {
|
||||||
.reply(function (uri: string, body: nock.Body) {
|
testEmitterBinary(httpTransport(sink), "body");
|
||||||
// return the request body and the headers so they can be
|
});
|
||||||
// examined in the test
|
|
||||||
if (typeof body === "string") {
|
describe("HTTP builtin", () => {
|
||||||
body = JSON.parse(body);
|
setupMock("http://cloudevents.io");
|
||||||
}
|
testEmitterBinary(httpTransport("http://cloudevents.io"), "body");
|
||||||
const returnBody = { ...(body as Record<string, unknown>), ...this.req.headers };
|
setupMock("http://cloudevents.io");
|
||||||
return [201, returnBody];
|
testEmitterStructured(httpTransport("http://cloudevents.io"), "body");
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Axios", () => {
|
describe("Axios", () => {
|
||||||
testEmitter(axiosEmitter, "data");
|
testEmitterBinary(axiosEmitter, "data");
|
||||||
|
testEmitterStructured(axiosEmitter, "data");
|
||||||
});
|
});
|
||||||
describe("SuperAgent", () => {
|
describe("SuperAgent", () => {
|
||||||
testEmitter(superagentEmitter, "body");
|
testEmitterBinary(superagentEmitter, "body");
|
||||||
|
testEmitterStructured(superagentEmitter, "body");
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Got", () => {
|
describe("Got", () => {
|
||||||
testEmitter(gotEmitter, "body");
|
testEmitterBinary(gotEmitter, "body");
|
||||||
|
testEmitterStructured(gotEmitter, "body");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function testEmitter(fn: TransportFunction, bodyAttr: string) {
|
function testEmitterBinary(fn: TransportFunction, bodyAttr: string) {
|
||||||
it("Works as a binary event emitter", async () => {
|
it("Works as a binary event emitter", async () => {
|
||||||
const emitter = emitterFor(fn);
|
const emitter = emitterFor(fn);
|
||||||
const response = (await emitter(fixture)) as Record<string, Record<string, string>>;
|
const response = (await emitter(fixture)) as Record<string, Record<string, string>>;
|
||||||
|
@ -137,7 +154,9 @@ function testEmitter(fn: TransportFunction, bodyAttr: string) {
|
||||||
}
|
}
|
||||||
assertBinary(body);
|
assertBinary(body);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function testEmitterStructured(fn: TransportFunction, bodyAttr: string) {
|
||||||
it("Works as a structured event emitter", async () => {
|
it("Works as a structured event emitter", async () => {
|
||||||
const emitter = emitterFor(fn, { binding: HTTP, mode: Mode.STRUCTURED });
|
const emitter = emitterFor(fn, { binding: HTTP, mode: Mode.STRUCTURED });
|
||||||
const response = (await emitter(fixture)) as Record<string, Record<string, Record<string, string>>>;
|
const response = (await emitter(fixture)) as Record<string, Record<string, Record<string, string>>>;
|
||||||
|
|
|
@ -26,7 +26,7 @@ describe("Emitter Singleton", () => {
|
||||||
if (typeof body === "string") {
|
if (typeof body === "string") {
|
||||||
body = JSON.parse(body);
|
body = JSON.parse(body);
|
||||||
}
|
}
|
||||||
assertStructured({ ...(<any>body), ...(msg as Message).headers });
|
assertStructured({ ...(body as any), ...(msg as Message).headers });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("emit a Node.js 'cloudevent' event as an EventEmitter with ensureDelivery", async () => {
|
it("emit a Node.js 'cloudevent' event as an EventEmitter with ensureDelivery", async () => {
|
||||||
|
@ -41,7 +41,7 @@ describe("Emitter Singleton", () => {
|
||||||
if (typeof body === "string") {
|
if (typeof body === "string") {
|
||||||
body = JSON.parse(body);
|
body = JSON.parse(body);
|
||||||
}
|
}
|
||||||
assertStructured({ ...(<any>body), ...(msg as Message).headers });
|
assertStructured({ ...(body as any), ...(msg as Message).headers });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("emit a Node.js 'cloudevent' event as an EventEmitter with ensureDelivery Error", async () => {
|
it("emit a Node.js 'cloudevent' event as an EventEmitter with ensureDelivery Error", async () => {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import path from "path";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import { CloudEvent, CONSTANTS, Version } from "../../src";
|
import { CloudEvent, CONSTANTS, V1 } from "../../src";
|
||||||
import { asBase64 } from "../../src/event/validation";
|
import { asBase64 } from "../../src/event/validation";
|
||||||
import { Message, Kafka, KafkaMessage, KafkaEvent } from "../../src/message";
|
import { Message, Kafka, KafkaMessage, KafkaEvent } from "../../src/message";
|
||||||
import { KAFKA_CE_HEADERS } from "../../src/message/kafka/headers";
|
import { KAFKA_CE_HEADERS } from "../../src/message/kafka/headers";
|
||||||
|
@ -43,7 +43,7 @@ const imageData = new Uint32Array(fs.readFileSync(path.join(process.cwd(), "test
|
||||||
const image_base64 = asBase64(imageData);
|
const image_base64 = asBase64(imageData);
|
||||||
|
|
||||||
const fixture = new CloudEvent({
|
const fixture = new CloudEvent({
|
||||||
specversion: Version.V1,
|
specversion: V1,
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
source,
|
source,
|
||||||
|
@ -131,7 +131,7 @@ describe("Kafka transport", () => {
|
||||||
expect(event.LUNCH).to.equal("tacos");
|
expect(event.LUNCH).to.equal("tacos");
|
||||||
expect(function () {
|
expect(function () {
|
||||||
event.validate();
|
event.validate();
|
||||||
}).to.throw("invalid attribute name: LUNCH");
|
}).to.throw("invalid attribute name: \"LUNCH\"");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Can detect CloudEvent binary Messages with weird versions", () => {
|
it("Can detect CloudEvent binary Messages with weird versions", () => {
|
||||||
|
@ -233,7 +233,7 @@ describe("Kafka transport", () => {
|
||||||
expect(message.body).to.equal(data);
|
expect(message.body).to.equal(data);
|
||||||
// validate all headers
|
// validate all headers
|
||||||
expect(message.headers[CONSTANTS.HEADER_CONTENT_TYPE]).to.equal(datacontenttype);
|
expect(message.headers[CONSTANTS.HEADER_CONTENT_TYPE]).to.equal(datacontenttype);
|
||||||
expect(message.headers[KAFKA_CE_HEADERS.SPEC_VERSION]).to.equal(Version.V1);
|
expect(message.headers[KAFKA_CE_HEADERS.SPEC_VERSION]).to.equal(V1);
|
||||||
expect(message.headers[KAFKA_CE_HEADERS.ID]).to.equal(id);
|
expect(message.headers[KAFKA_CE_HEADERS.ID]).to.equal(id);
|
||||||
expect(message.headers[KAFKA_CE_HEADERS.TYPE]).to.equal(type);
|
expect(message.headers[KAFKA_CE_HEADERS.TYPE]).to.equal(type);
|
||||||
expect(message.headers[KAFKA_CE_HEADERS.SOURCE]).to.equal(source);
|
expect(message.headers[KAFKA_CE_HEADERS.SOURCE]).to.equal(source);
|
||||||
|
@ -249,7 +249,7 @@ describe("Kafka transport", () => {
|
||||||
expect(message.headers[CONSTANTS.HEADER_CONTENT_TYPE]).to.equal(CONSTANTS.DEFAULT_CE_CONTENT_TYPE);
|
expect(message.headers[CONSTANTS.HEADER_CONTENT_TYPE]).to.equal(CONSTANTS.DEFAULT_CE_CONTENT_TYPE);
|
||||||
// Parse the message body as JSON, then validate the attributes
|
// Parse the message body as JSON, then validate the attributes
|
||||||
const body = JSON.parse(message.body as string);
|
const body = JSON.parse(message.body as string);
|
||||||
expect(body[CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION]).to.equal(Version.V1);
|
expect(body[CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION]).to.equal(V1);
|
||||||
expect(body[CONSTANTS.CE_ATTRIBUTES.ID]).to.equal(id);
|
expect(body[CONSTANTS.CE_ATTRIBUTES.ID]).to.equal(id);
|
||||||
expect(body[CONSTANTS.CE_ATTRIBUTES.TYPE]).to.equal(type);
|
expect(body[CONSTANTS.CE_ATTRIBUTES.TYPE]).to.equal(type);
|
||||||
expect(body[CONSTANTS.CE_ATTRIBUTES.SOURCE]).to.equal(source);
|
expect(body[CONSTANTS.CE_ATTRIBUTES.SOURCE]).to.equal(source);
|
||||||
|
|
|
@ -8,7 +8,7 @@ import fs from "fs";
|
||||||
|
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import { IncomingHttpHeaders } from "http";
|
import { IncomingHttpHeaders } from "http";
|
||||||
import { CloudEvent, CONSTANTS, Version } from "../../src";
|
import { CloudEvent, CONSTANTS, V1, V03 } from "../../src";
|
||||||
import { asBase64 } from "../../src/event/validation";
|
import { asBase64 } from "../../src/event/validation";
|
||||||
import { Message, HTTP } from "../../src/message";
|
import { Message, HTTP } from "../../src/message";
|
||||||
|
|
||||||
|
@ -41,6 +41,60 @@ const imageData = new Uint32Array(fs.readFileSync(path.join(process.cwd(), "test
|
||||||
const image_base64 = asBase64(imageData);
|
const image_base64 = asBase64(imageData);
|
||||||
|
|
||||||
describe("HTTP transport", () => {
|
describe("HTTP transport", () => {
|
||||||
|
it("validates extension attribute names for incoming messages", () => {
|
||||||
|
// create a new Message
|
||||||
|
const msg: Message = {
|
||||||
|
headers: {
|
||||||
|
"ce-id": "213",
|
||||||
|
"ce-source": "test",
|
||||||
|
"ce-type": "test",
|
||||||
|
"ce-bad-extension": "value"
|
||||||
|
},
|
||||||
|
body: undefined
|
||||||
|
};
|
||||||
|
const evt = HTTP.toEvent(msg) as CloudEvent;
|
||||||
|
expect(() => evt.validate()).to.throw(TypeError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Includes extensions in binary mode when type is 'boolean' with a false value", () => {
|
||||||
|
const evt = new CloudEvent({ source: "test", type: "test", extboolean: false });
|
||||||
|
expect(evt.hasOwnProperty("extboolean")).to.equal(true);
|
||||||
|
expect(evt["extboolean"]).to.equal(false);
|
||||||
|
const message = HTTP.binary(evt);
|
||||||
|
expect(message.headers.hasOwnProperty("ce-extboolean")).to.equal(true);
|
||||||
|
expect(message.headers["ce-extboolean"]).to.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Includes extensions in structured when type is 'boolean' with a false value", () => {
|
||||||
|
const evt = new CloudEvent({ source: "test", type: "test", extboolean: false });
|
||||||
|
expect(evt.hasOwnProperty("extboolean")).to.equal(true);
|
||||||
|
expect(evt["extboolean"]).to.equal(false);
|
||||||
|
const message = HTTP.structured(evt);
|
||||||
|
const body = JSON.parse(message.body as string);
|
||||||
|
expect(body.hasOwnProperty("extboolean")).to.equal(true);
|
||||||
|
expect(body.extboolean).to.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Handles big integers in structured mode", () => {
|
||||||
|
process.env[CONSTANTS.USE_BIG_INT_ENV] = "true";
|
||||||
|
const ce = HTTP.toEvent({
|
||||||
|
headers: { "content-type": "application/cloudevents+json" },
|
||||||
|
body: `{"data": 1524831183200260097}`
|
||||||
|
}) as CloudEvent;
|
||||||
|
expect(ce.data).to.equal(1524831183200260097n);
|
||||||
|
process.env[CONSTANTS.USE_BIG_INT_ENV] = undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Handles big integers in binary mode", () => {
|
||||||
|
process.env[CONSTANTS.USE_BIG_INT_ENV] = "true";
|
||||||
|
const ce = HTTP.toEvent({
|
||||||
|
headers: { "content-type": "application/json", "ce-id": "1234" },
|
||||||
|
body: `{"data": 1524831183200260097}`
|
||||||
|
}) as CloudEvent<Record<string, never>>;
|
||||||
|
expect((ce.data as Record<string, never>).data).to.equal(1524831183200260097n);
|
||||||
|
process.env[CONSTANTS.USE_BIG_INT_ENV] = undefined;
|
||||||
|
});
|
||||||
|
|
||||||
it("Handles events with no content-type and no datacontenttype", () => {
|
it("Handles events with no content-type and no datacontenttype", () => {
|
||||||
const body = "{Something[Not:valid}JSON";
|
const body = "{Something[Not:valid}JSON";
|
||||||
const message: Message<undefined> = {
|
const message: Message<undefined> = {
|
||||||
|
@ -100,7 +154,7 @@ describe("HTTP transport", () => {
|
||||||
[CONSTANTS.CE_HEADERS.ID]: "1234",
|
[CONSTANTS.CE_HEADERS.ID]: "1234",
|
||||||
[CONSTANTS.CE_HEADERS.SOURCE]: "test",
|
[CONSTANTS.CE_HEADERS.SOURCE]: "test",
|
||||||
[CONSTANTS.CE_HEADERS.TYPE]: "test.event",
|
[CONSTANTS.CE_HEADERS.TYPE]: "test.event",
|
||||||
[CONSTANTS.CE_HEADERS.SPEC_VERSION]: Version.V1,
|
[CONSTANTS.CE_HEADERS.SPEC_VERSION]: V1,
|
||||||
"ce-LUNCH": "tacos",
|
"ce-LUNCH": "tacos",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -109,7 +163,7 @@ describe("HTTP transport", () => {
|
||||||
expect(event.LUNCH).to.equal("tacos");
|
expect(event.LUNCH).to.equal("tacos");
|
||||||
expect(function () {
|
expect(function () {
|
||||||
event.validate();
|
event.validate();
|
||||||
}).to.throw("invalid attribute name: LUNCH");
|
}).to.throw("invalid attribute name: \"LUNCH\"");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Can detect CloudEvent binary Messages with weird versions", () => {
|
it("Can detect CloudEvent binary Messages with weird versions", () => {
|
||||||
|
@ -183,7 +237,7 @@ describe("HTTP transport", () => {
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
source,
|
source,
|
||||||
specversion: Version.V1,
|
specversion: V1,
|
||||||
data: { lunch: "tacos" },
|
data: { lunch: "tacos" },
|
||||||
});
|
});
|
||||||
const message: Message<undefined> = {
|
const message: Message<undefined> = {
|
||||||
|
@ -196,7 +250,7 @@ describe("HTTP transport", () => {
|
||||||
|
|
||||||
describe("Specification version V1", () => {
|
describe("Specification version V1", () => {
|
||||||
const fixture = new CloudEvent({
|
const fixture = new CloudEvent({
|
||||||
specversion: Version.V1,
|
specversion: V1,
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
source,
|
source,
|
||||||
|
@ -214,7 +268,7 @@ describe("HTTP transport", () => {
|
||||||
expect(message.body).to.equal(JSON.stringify(data));
|
expect(message.body).to.equal(JSON.stringify(data));
|
||||||
// validate all headers
|
// validate all headers
|
||||||
expect(message.headers[CONSTANTS.HEADER_CONTENT_TYPE]).to.equal(datacontenttype);
|
expect(message.headers[CONSTANTS.HEADER_CONTENT_TYPE]).to.equal(datacontenttype);
|
||||||
expect(message.headers[CONSTANTS.CE_HEADERS.SPEC_VERSION]).to.equal(Version.V1);
|
expect(message.headers[CONSTANTS.CE_HEADERS.SPEC_VERSION]).to.equal(V1);
|
||||||
expect(message.headers[CONSTANTS.CE_HEADERS.ID]).to.equal(id);
|
expect(message.headers[CONSTANTS.CE_HEADERS.ID]).to.equal(id);
|
||||||
expect(message.headers[CONSTANTS.CE_HEADERS.TYPE]).to.equal(type);
|
expect(message.headers[CONSTANTS.CE_HEADERS.TYPE]).to.equal(type);
|
||||||
expect(message.headers[CONSTANTS.CE_HEADERS.SOURCE]).to.equal(source);
|
expect(message.headers[CONSTANTS.CE_HEADERS.SOURCE]).to.equal(source);
|
||||||
|
@ -230,7 +284,7 @@ describe("HTTP transport", () => {
|
||||||
expect(message.headers[CONSTANTS.HEADER_CONTENT_TYPE]).to.equal(CONSTANTS.DEFAULT_CE_CONTENT_TYPE);
|
expect(message.headers[CONSTANTS.HEADER_CONTENT_TYPE]).to.equal(CONSTANTS.DEFAULT_CE_CONTENT_TYPE);
|
||||||
// Parse the message body as JSON, then validate the attributes
|
// Parse the message body as JSON, then validate the attributes
|
||||||
const body = JSON.parse(message.body as string);
|
const body = JSON.parse(message.body as string);
|
||||||
expect(body[CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION]).to.equal(Version.V1);
|
expect(body[CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION]).to.equal(V1);
|
||||||
expect(body[CONSTANTS.CE_ATTRIBUTES.ID]).to.equal(id);
|
expect(body[CONSTANTS.CE_ATTRIBUTES.ID]).to.equal(id);
|
||||||
expect(body[CONSTANTS.CE_ATTRIBUTES.TYPE]).to.equal(type);
|
expect(body[CONSTANTS.CE_ATTRIBUTES.TYPE]).to.equal(type);
|
||||||
expect(body[CONSTANTS.CE_ATTRIBUTES.SOURCE]).to.equal(source);
|
expect(body[CONSTANTS.CE_ATTRIBUTES.SOURCE]).to.equal(source);
|
||||||
|
@ -299,7 +353,7 @@ describe("HTTP transport", () => {
|
||||||
|
|
||||||
describe("Specification version V03", () => {
|
describe("Specification version V03", () => {
|
||||||
const fixture = new CloudEvent({
|
const fixture = new CloudEvent({
|
||||||
specversion: Version.V03,
|
specversion: V03,
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
source,
|
source,
|
||||||
|
@ -317,7 +371,7 @@ describe("HTTP transport", () => {
|
||||||
expect(message.body).to.equal(JSON.stringify(data));
|
expect(message.body).to.equal(JSON.stringify(data));
|
||||||
// validate all headers
|
// validate all headers
|
||||||
expect(message.headers[CONSTANTS.HEADER_CONTENT_TYPE]).to.equal(datacontenttype);
|
expect(message.headers[CONSTANTS.HEADER_CONTENT_TYPE]).to.equal(datacontenttype);
|
||||||
expect(message.headers[CONSTANTS.CE_HEADERS.SPEC_VERSION]).to.equal(Version.V03);
|
expect(message.headers[CONSTANTS.CE_HEADERS.SPEC_VERSION]).to.equal(V03);
|
||||||
expect(message.headers[CONSTANTS.CE_HEADERS.ID]).to.equal(id);
|
expect(message.headers[CONSTANTS.CE_HEADERS.ID]).to.equal(id);
|
||||||
expect(message.headers[CONSTANTS.CE_HEADERS.TYPE]).to.equal(type);
|
expect(message.headers[CONSTANTS.CE_HEADERS.TYPE]).to.equal(type);
|
||||||
expect(message.headers[CONSTANTS.CE_HEADERS.SOURCE]).to.equal(source);
|
expect(message.headers[CONSTANTS.CE_HEADERS.SOURCE]).to.equal(source);
|
||||||
|
@ -333,7 +387,7 @@ describe("HTTP transport", () => {
|
||||||
expect(message.headers[CONSTANTS.HEADER_CONTENT_TYPE]).to.equal(CONSTANTS.DEFAULT_CE_CONTENT_TYPE);
|
expect(message.headers[CONSTANTS.HEADER_CONTENT_TYPE]).to.equal(CONSTANTS.DEFAULT_CE_CONTENT_TYPE);
|
||||||
// Parse the message body as JSON, then validate the attributes
|
// Parse the message body as JSON, then validate the attributes
|
||||||
const body = JSON.parse(message.body as string);
|
const body = JSON.parse(message.body as string);
|
||||||
expect(body[CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION]).to.equal(Version.V03);
|
expect(body[CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION]).to.equal(V03);
|
||||||
expect(body[CONSTANTS.CE_ATTRIBUTES.ID]).to.equal(id);
|
expect(body[CONSTANTS.CE_ATTRIBUTES.ID]).to.equal(id);
|
||||||
expect(body[CONSTANTS.CE_ATTRIBUTES.TYPE]).to.equal(type);
|
expect(body[CONSTANTS.CE_ATTRIBUTES.TYPE]).to.equal(type);
|
||||||
expect(body[CONSTANTS.CE_ATTRIBUTES.SOURCE]).to.equal(source);
|
expect(body[CONSTANTS.CE_ATTRIBUTES.SOURCE]).to.equal(source);
|
||||||
|
|
|
@ -7,7 +7,7 @@ import path from "path";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import { CloudEvent, CONSTANTS, Version, Headers } from "../../src";
|
import { CloudEvent, CONSTANTS, V1, Headers } from "../../src";
|
||||||
import { asBase64 } from "../../src/event/validation";
|
import { asBase64 } from "../../src/event/validation";
|
||||||
import { Message, MQTT, MQTTMessage } from "../../src/message";
|
import { Message, MQTT, MQTTMessage } from "../../src/message";
|
||||||
|
|
||||||
|
@ -32,18 +32,18 @@ const ext2Name = "extension2";
|
||||||
const ext2Value = "acme";
|
const ext2Value = "acme";
|
||||||
|
|
||||||
// Binary data as base64
|
// Binary data as base64
|
||||||
const dataBinary = Uint32Array.from(JSON.stringify(data), (c) => c.codePointAt(0) as number);
|
const dataBinary = Uint8Array.from(JSON.stringify(data), (c) => c.codePointAt(0) as number);
|
||||||
const data_base64 = asBase64(dataBinary);
|
const data_base64 = asBase64(dataBinary);
|
||||||
|
|
||||||
// Since the above is a special case (string as binary), let's test
|
// Since the above is a special case (string as binary), let's test
|
||||||
// with a real binary file one is likely to encounter in the wild
|
// with a real binary file one is likely to encounter in the wild
|
||||||
const imageData = new Uint32Array(fs.readFileSync(path.join(process.cwd(), "test", "integration", "ce.png")));
|
const imageData = new Uint8Array(fs.readFileSync(path.join(process.cwd(), "test", "integration", "ce.png")));
|
||||||
const image_base64 = asBase64(imageData);
|
const image_base64 = asBase64(imageData);
|
||||||
|
|
||||||
const PUBLISH = {"Content Type": "application/json; charset=utf-8"};
|
const PUBLISH = {"Content Type": "application/json; charset=utf-8"};
|
||||||
|
|
||||||
const fixture = new CloudEvent({
|
const fixture = new CloudEvent({
|
||||||
specversion: Version.V1,
|
specversion: V1,
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
source,
|
source,
|
||||||
|
@ -134,7 +134,7 @@ describe("MQTT transport", () => {
|
||||||
expect(event.LUNCH).to.equal("tacos");
|
expect(event.LUNCH).to.equal("tacos");
|
||||||
expect(function () {
|
expect(function () {
|
||||||
event.validate();
|
event.validate();
|
||||||
}).to.throw("invalid attribute name: LUNCH");
|
}).to.throw("invalid attribute name: \"LUNCH\"");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Can detect CloudEvent binary Messages with weird versions", () => {
|
it("Can detect CloudEvent binary Messages with weird versions", () => {
|
||||||
|
@ -216,7 +216,7 @@ describe("MQTT transport", () => {
|
||||||
expect(message.body).to.equal(data);
|
expect(message.body).to.equal(data);
|
||||||
// validate all headers
|
// validate all headers
|
||||||
expect(message.headers.datacontenttype).to.equal(datacontenttype);
|
expect(message.headers.datacontenttype).to.equal(datacontenttype);
|
||||||
expect(message.headers.specversion).to.equal(Version.V1);
|
expect(message.headers.specversion).to.equal(V1);
|
||||||
expect(message.headers.id).to.equal(id);
|
expect(message.headers.id).to.equal(id);
|
||||||
expect(message.headers.type).to.equal(type);
|
expect(message.headers.type).to.equal(type);
|
||||||
expect(message.headers.source).to.equal(source);
|
expect(message.headers.source).to.equal(source);
|
||||||
|
@ -232,7 +232,7 @@ describe("MQTT transport", () => {
|
||||||
expect(message.body).to.equal(data);
|
expect(message.body).to.equal(data);
|
||||||
// validate all headers
|
// validate all headers
|
||||||
expect(message["User Properties"]?.datacontenttype).to.equal(datacontenttype);
|
expect(message["User Properties"]?.datacontenttype).to.equal(datacontenttype);
|
||||||
expect(message["User Properties"]?.specversion).to.equal(Version.V1);
|
expect(message["User Properties"]?.specversion).to.equal(V1);
|
||||||
expect(message["User Properties"]?.id).to.equal(id);
|
expect(message["User Properties"]?.id).to.equal(id);
|
||||||
expect(message["User Properties"]?.type).to.equal(type);
|
expect(message["User Properties"]?.type).to.equal(type);
|
||||||
expect(message["User Properties"]?.source).to.equal(source);
|
expect(message["User Properties"]?.source).to.equal(source);
|
||||||
|
@ -249,7 +249,7 @@ describe("MQTT transport", () => {
|
||||||
expect(message.body).to.deep.equal(message.payload);
|
expect(message.body).to.deep.equal(message.payload);
|
||||||
expect(message.payload).to.deep.equal(fixture.toJSON());
|
expect(message.payload).to.deep.equal(fixture.toJSON());
|
||||||
const body = message.body as Record<string, string>;
|
const body = message.body as Record<string, string>;
|
||||||
expect(body[CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION]).to.equal(Version.V1);
|
expect(body[CONSTANTS.CE_ATTRIBUTES.SPEC_VERSION]).to.equal(V1);
|
||||||
expect(body[CONSTANTS.CE_ATTRIBUTES.ID]).to.equal(id);
|
expect(body[CONSTANTS.CE_ATTRIBUTES.ID]).to.equal(id);
|
||||||
expect(body[CONSTANTS.CE_ATTRIBUTES.TYPE]).to.equal(type);
|
expect(body[CONSTANTS.CE_ATTRIBUTES.TYPE]).to.equal(type);
|
||||||
expect(body[CONSTANTS.CE_ATTRIBUTES.SOURCE]).to.equal(source);
|
expect(body[CONSTANTS.CE_ATTRIBUTES.SOURCE]).to.equal(source);
|
||||||
|
@ -281,14 +281,14 @@ describe("MQTT transport", () => {
|
||||||
|
|
||||||
it("Converts base64 encoded data to binary when deserializing structured messages", () => {
|
it("Converts base64 encoded data to binary when deserializing structured messages", () => {
|
||||||
const message = MQTT.structured(fixture.cloneWith({ data: imageData, datacontenttype: "image/png" }));
|
const message = MQTT.structured(fixture.cloneWith({ data: imageData, datacontenttype: "image/png" }));
|
||||||
const eventDeserialized = MQTT.toEvent(message) as CloudEvent<Uint32Array>;
|
const eventDeserialized = MQTT.toEvent(message) as CloudEvent<Uint8Array>;
|
||||||
expect(eventDeserialized.data).to.deep.equal(imageData);
|
expect(eventDeserialized.data).to.deep.equal(imageData);
|
||||||
expect(eventDeserialized.data_base64).to.equal(image_base64);
|
expect(eventDeserialized.data_base64).to.equal(image_base64);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Converts base64 encoded data to binary when deserializing binary messages", () => {
|
it("Converts base64 encoded data to binary when deserializing binary messages", () => {
|
||||||
const message = MQTT.binary(fixture.cloneWith({ data: imageData, datacontenttype: "image/png" }));
|
const message = MQTT.binary(fixture.cloneWith({ data: imageData, datacontenttype: "image/png" }));
|
||||||
const eventDeserialized = MQTT.toEvent(message) as CloudEvent<Uint32Array>;
|
const eventDeserialized = MQTT.toEvent(message) as CloudEvent<Uint8Array>;
|
||||||
expect(eventDeserialized.data).to.deep.equal(imageData);
|
expect(eventDeserialized.data).to.deep.equal(imageData);
|
||||||
expect(eventDeserialized.data_base64).to.equal(image_base64);
|
expect(eventDeserialized.data_base64).to.equal(image_base64);
|
||||||
});
|
});
|
||||||
|
@ -302,7 +302,7 @@ describe("MQTT transport", () => {
|
||||||
|
|
||||||
it("Does not parse binary data from binary messages with content type application/json", () => {
|
it("Does not parse binary data from binary messages with content type application/json", () => {
|
||||||
const message = MQTT.binary(fixture.cloneWith({ data: dataBinary }));
|
const message = MQTT.binary(fixture.cloneWith({ data: dataBinary }));
|
||||||
const eventDeserialized = MQTT.toEvent(message) as CloudEvent<Uint32Array>;
|
const eventDeserialized = MQTT.toEvent(message) as CloudEvent<Uint8Array>;
|
||||||
expect(eventDeserialized.data).to.deep.equal(dataBinary);
|
expect(eventDeserialized.data).to.deep.equal(dataBinary);
|
||||||
expect(eventDeserialized.data_base64).to.equal(data_base64);
|
expect(eventDeserialized.data_base64).to.equal(data_base64);
|
||||||
});
|
});
|
||||||
|
|
|
@ -56,7 +56,7 @@ describe("JSON Event Format Parser", () => {
|
||||||
const parser = new Parser();
|
const parser = new Parser();
|
||||||
|
|
||||||
// TODO: Should the parser catch the SyntaxError and re-throw a ValidationError?
|
// TODO: Should the parser catch the SyntaxError and re-throw a ValidationError?
|
||||||
expect(parser.parse.bind(parser, payload)).to.throw(SyntaxError, "Unexpected token g in JSON at position 1");
|
expect(parser.parse.bind(parser, payload)).to.throw(SyntaxError);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Accepts a string as valid JSON", () => {
|
it("Accepts a string as valid JSON", () => {
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
|
|
||||||
import "mocha";
|
import "mocha";
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import { CloudEvent, CloudEventV1, Version } from "../../src";
|
import { CloudEvent, CloudEventV1, V1, V03 } from "../../src";
|
||||||
|
|
||||||
const fixture: CloudEventV1<undefined> = {
|
const fixture: CloudEventV1<undefined> = {
|
||||||
id: "123",
|
id: "123",
|
||||||
type: "org.cloudevents.test",
|
type: "org.cloudevents.test",
|
||||||
source: "http://cloudevents.io",
|
source: "http://cloudevents.io",
|
||||||
specversion: Version.V1,
|
specversion: V1,
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("The SDK Requirements", () => {
|
describe("The SDK Requirements", () => {
|
||||||
|
@ -21,19 +21,19 @@ describe("The SDK Requirements", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("v0.3", () => {
|
describe("v0.3", () => {
|
||||||
it("should create an event using the right spec version", () => {
|
it("should create an (invalid) event using the right spec version", () => {
|
||||||
expect(
|
expect(
|
||||||
new CloudEvent({
|
new CloudEvent({
|
||||||
...fixture,
|
...fixture,
|
||||||
specversion: Version.V03,
|
specversion: V03,
|
||||||
}).specversion,
|
}, false).specversion,
|
||||||
).to.equal(Version.V03);
|
).to.equal(V03);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("v1.0", () => {
|
describe("v1.0", () => {
|
||||||
it("should create an event using the right spec version", () => {
|
it("should create an event using the right spec version", () => {
|
||||||
expect(new CloudEvent(fixture).specversion).to.equal(Version.V1);
|
expect(new CloudEvent(fixture).specversion).to.equal(V1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
import "mocha";
|
import "mocha";
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import { CloudEvent, Version, ValidationError } from "../../src";
|
import { CloudEvent, V1, ValidationError } from "../../src";
|
||||||
import { asBase64 } from "../../src/event/validation";
|
import { asBase64 } from "../../src/event/validation";
|
||||||
import Constants from "../../src/constants";
|
import Constants from "../../src/constants";
|
||||||
|
|
||||||
|
@ -19,8 +19,8 @@ const data = {
|
||||||
};
|
};
|
||||||
const subject = "subject-x0";
|
const subject = "subject-x0";
|
||||||
|
|
||||||
let cloudevent = new CloudEvent({
|
const cloudevent = new CloudEvent({
|
||||||
specversion: Version.V1,
|
specversion: V1,
|
||||||
id,
|
id,
|
||||||
source,
|
source,
|
||||||
type,
|
type,
|
||||||
|
@ -99,6 +99,16 @@ describe("CloudEvents Spec v1.0", () => {
|
||||||
it("should be ok when the type is an string converted from an object", () => {
|
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);
|
expect(cloudevent.cloneWith({ objectextension: JSON.stringify({ some: "object" }) }).validate()).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should only allow a-z|0-9 in the attribute names", () => {
|
||||||
|
const testCases = [
|
||||||
|
"an extension", "an_extension", "an-extension", "an.extension", "an+extension"
|
||||||
|
];
|
||||||
|
testCases.forEach((testCase) => {
|
||||||
|
const evt = cloudevent.cloneWith({ [testCase]: "a value"}, false);
|
||||||
|
expect(() => evt.validate()).to.throw(ValidationError);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("The Constraints check", () => {
|
describe("The Constraints check", () => {
|
||||||
|
@ -110,8 +120,8 @@ describe("CloudEvents Spec v1.0", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("defaut ID create when an empty string", () => {
|
it("defaut ID create when an empty string", () => {
|
||||||
cloudevent = cloudevent.cloneWith({ id: "" });
|
const testEvent = cloudevent.cloneWith({ id: "" });
|
||||||
expect(cloudevent.id.length).to.be.greaterThan(0);
|
expect(testEvent.id.length).to.be.greaterThan(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -150,11 +160,11 @@ describe("CloudEvents Spec v1.0", () => {
|
||||||
describe("'time'", () => {
|
describe("'time'", () => {
|
||||||
it("must adhere to the format specified in RFC 3339", () => {
|
it("must adhere to the format specified in RFC 3339", () => {
|
||||||
const d = new Date();
|
const d = new Date();
|
||||||
cloudevent = cloudevent.cloneWith({ time: d.toString() });
|
const testEvent = cloudevent.cloneWith({ time: d.toString() }, false);
|
||||||
// ensure that we always get back the same thing we passed in
|
// ensure that we always get back the same thing we passed in
|
||||||
expect(cloudevent.time).to.equal(d.toString());
|
expect(testEvent.time).to.equal(d.toString());
|
||||||
// ensure that when stringified, the timestamp is in RFC3339 format
|
// ensure that when stringified, the timestamp is in RFC3339 format
|
||||||
expect(JSON.parse(JSON.stringify(cloudevent)).time).to.equal(new Date(d.toString()).toISOString());
|
expect(JSON.parse(JSON.stringify(testEvent)).time).to.equal(new Date(d.toString()).toISOString());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -173,14 +183,60 @@ describe("CloudEvents Spec v1.0", () => {
|
||||||
expect(typeof ce.data).to.equal("string");
|
expect(typeof ce.data).to.equal("string");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be ok when type is 'Uint32Array' for 'Binary'", () => {
|
const dataString = ")(*~^my data for ce#@#$%";
|
||||||
const dataString = ")(*~^my data for ce#@#$%";
|
const testCases = [
|
||||||
|
{
|
||||||
|
type: Int8Array,
|
||||||
|
data: Int8Array.from(dataString, (c) => c.codePointAt(0) as number),
|
||||||
|
expected: asBase64(Int8Array.from(dataString, (c) => c.codePointAt(0) as number))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: Uint8Array,
|
||||||
|
data: Uint8Array.from(dataString, (c) => c.codePointAt(0) as number),
|
||||||
|
expected: asBase64(Uint8Array.from(dataString, (c) => c.codePointAt(0) as number))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: Int16Array,
|
||||||
|
data: Int16Array.from(dataString, (c) => c.codePointAt(0) as number),
|
||||||
|
expected: asBase64(Int16Array.from(dataString, (c) => c.codePointAt(0) as number))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: Uint16Array,
|
||||||
|
data: Uint16Array.from(dataString, (c) => c.codePointAt(0) as number),
|
||||||
|
expected: asBase64(Uint16Array.from(dataString, (c) => c.codePointAt(0) as number))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: Int32Array,
|
||||||
|
data: Int32Array.from(dataString, (c) => c.codePointAt(0) as number),
|
||||||
|
expected: asBase64(Int32Array.from(dataString, (c) => c.codePointAt(0) as number))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: Uint32Array,
|
||||||
|
data: Uint32Array.from(dataString, (c) => c.codePointAt(0) as number),
|
||||||
|
expected: asBase64(Uint32Array.from(dataString, (c) => c.codePointAt(0) as number))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: Uint8ClampedArray,
|
||||||
|
data: Uint8ClampedArray.from(dataString, (c) => c.codePointAt(0) as number),
|
||||||
|
expected: asBase64(Uint8ClampedArray.from(dataString, (c) => c.codePointAt(0) as number))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: Float32Array,
|
||||||
|
data: Float32Array.from(dataString, (c) => c.codePointAt(0) as number),
|
||||||
|
expected: asBase64(Float32Array.from(dataString, (c) => c.codePointAt(0) as number))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: Float64Array,
|
||||||
|
data: Float64Array.from(dataString, (c) => c.codePointAt(0) as number),
|
||||||
|
expected: asBase64(Float64Array.from(dataString, (c) => c.codePointAt(0) as number))
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const dataBinary = Uint32Array.from(dataString, (c) => c.codePointAt(0) as number);
|
testCases.forEach((test) => {
|
||||||
const expected = asBase64(dataBinary);
|
it(`should be ok when type is '${test.type.name}' for 'Binary'`, () => {
|
||||||
|
const ce = cloudevent.cloneWith({ datacontenttype: "text/plain", data: test.data });
|
||||||
const ce = cloudevent.cloneWith({ datacontenttype: "text/plain", data: dataBinary });
|
expect(ce.data_base64).to.equal(test.expected);
|
||||||
expect(ce.data_base64).to.equal(expected);
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2016", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
|
"target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
|
||||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||||
"allowJs": true, /* Allow javascript files to be compiled. */
|
"allowJs": true, /* Allow javascript files to be compiled. */
|
||||||
"checkJs": false, /* Report errors in .js files. */
|
"checkJs": false, /* Report errors in .js files. */
|
||||||
|
@ -11,7 +11,8 @@
|
||||||
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
|
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"experimentalDecorators": true
|
"experimentalDecorators": true,
|
||||||
|
"isolatedModules": true,
|
||||||
},
|
},
|
||||||
"compileOnSave": true,
|
"compileOnSave": true,
|
||||||
"include": [
|
"include": [
|
||||||
|
|
|
@ -1,9 +1,22 @@
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
const webpack = require("webpack");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: {
|
entry: {
|
||||||
"cloudevents": "./browser/index.js"
|
"cloudevents": "./browser/index.js"
|
||||||
},
|
},
|
||||||
|
resolve: {
|
||||||
|
fallback: {
|
||||||
|
util: require.resolve("util/"),
|
||||||
|
http: false,
|
||||||
|
https: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.ProvidePlugin({
|
||||||
|
process: 'process/browser'
|
||||||
|
})
|
||||||
|
],
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, "bundles"),
|
path: path.resolve(__dirname, "bundles"),
|
||||||
filename: "[name].js",
|
filename: "[name].js",
|
||||||
|
|
Loading…
Reference in New Issue