From e219a30708f061ef5fdb576d18d2e087f4ba5a25 Mon Sep 17 00:00:00 2001 From: Lucas Holmquist Date: Wed, 29 Jul 2020 18:48:14 -0400 Subject: [PATCH 01/14] chore: Update readme with correct Receiver usage (#287) Signed-off-by: Lucas Holmquist --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e8fb4107..c128711f 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,8 @@ const { Receiver } = require("cloudevents"); -// Create a receiver to accept events over HTTP -const receiver = new Receiver(); - // body and headers come from an incoming HTTP request, e.g. express.js -const receivedEvent = receiver.accept(req.headers, req.body); +const receivedEvent = Receiver.accept(req.headers, req.body); console.log(receivedEvent); ``` @@ -157,4 +154,4 @@ We love contributions from the community! Please check the for information on how to get involved. [v1spec]: https://github.com/cloudevents/spec/tree/v1.0 -[v103pec]: https://github.com/cloudevents/spec/tree/v0.3 \ No newline at end of file +[v103pec]: https://github.com/cloudevents/spec/tree/v0.3 From ed9ea956d73491cc890751bf1baf4f0be828a9cb Mon Sep 17 00:00:00 2001 From: Lance Ball Date: Thu, 30 Jul 2020 08:39:47 -0400 Subject: [PATCH 02/14] fix: ensure that data encoded as base64 is parsed as an object (#285) The 0.3 specification states that `datacontentencoding` may be set to base64. If an incoming event arrives over HTTP with this value set, and the content type is either application/json or application/cloudevents+json, then ensure that the data is decoded and parsed. Fixes: https://github.com/cloudevents/sdk-javascript/issues/284 See: https://github.com/cloudevents/sdk-javascript/pull/282 Signed-off-by: Lance Ball --- src/transport/http/binary_receiver.ts | 9 ++++++++ src/transport/http/structured_receiver.ts | 6 ++++- test/integration/receiver_binary_03_tests.ts | 21 ++++++++++++++++++ .../receiver_structured_0_3_test.ts | 22 +++++++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/transport/http/binary_receiver.ts b/src/transport/http/binary_receiver.ts index 3c6fb2c3..9be2def0 100644 --- a/src/transport/http/binary_receiver.ts +++ b/src/transport/http/binary_receiver.ts @@ -77,6 +77,15 @@ export class BinaryHTTPReceiver { eventObj[header.substring(CONSTANTS.EXTENSIONS_PREFIX.length)] = headers[header]; } } + // At this point, if the datacontenttype is application/json and the datacontentencoding is base64 + // then the data has already been decoded as a string, then parsed as JSON. We don't need to have + // the datacontentencoding property set - in fact, it's incorrect to do so. + if ( + eventObj.datacontenttype === CONSTANTS.MIME_JSON && + eventObj.datacontentencoding === CONSTANTS.ENCODING_BASE64 + ) { + delete eventObj.datacontentencoding; + } const cloudevent = new CloudEvent({ ...eventObj, data: parsedPayload } as CloudEventV1 | CloudEventV03); this.version === Version.V1 ? validateV1(cloudevent) : validateV03(cloudevent); diff --git a/src/transport/http/structured_receiver.ts b/src/transport/http/structured_receiver.ts index 383c5a75..2450371e 100644 --- a/src/transport/http/structured_receiver.ts +++ b/src/transport/http/structured_receiver.ts @@ -1,6 +1,6 @@ import { CloudEvent, Version } from "../.."; import { Headers, sanitize } from "./headers"; -import { Parser, JSONParser, MappedParser } from "../../parsers"; +import { Parser, JSONParser, MappedParser, Base64Parser } from "../../parsers"; import { parserByContentType } from "../../parsers"; import { v1structuredParsers, v03structuredParsers } from "./versions"; import { isString, isBase64, ValidationError, isStringOrObjectOrThrow } from "../../event/validation"; @@ -75,6 +75,10 @@ export class StructuredHTTPReceiver { if (eventObj.data && eventObj.datacontentencoding) { if (eventObj.datacontentencoding === CONSTANTS.ENCODING_BASE64 && !isBase64(eventObj.data)) { throw new ValidationError("invalid payload"); + } else if (eventObj.datacontentencoding === CONSTANTS.ENCODING_BASE64) { + const dataParser = new Base64Parser(); + eventObj.data = JSON.parse(dataParser.parse(eventObj.data as string)); + delete eventObj.datacontentencoding; } } diff --git a/test/integration/receiver_binary_03_tests.ts b/test/integration/receiver_binary_03_tests.ts index 9ac4c6d0..dfdfd736 100644 --- a/test/integration/receiver_binary_03_tests.ts +++ b/test/integration/receiver_binary_03_tests.ts @@ -4,6 +4,7 @@ import { expect } from "chai"; import { CloudEvent, ValidationError, Version } from "../../src"; import { BinaryHTTPReceiver } from "../../src/transport/http/binary_receiver"; import CONSTANTS from "../../src/constants"; +import { asBase64 } from "../../src/event/validation"; const receiver = new BinaryHTTPReceiver(Version.V03); @@ -154,6 +155,26 @@ describe("HTTP Transport Binding Binary Receiver for CloudEvents v0.3", () => { // act and assert expect(receiver.parse.bind(receiver, payload, attributes)).to.not.throw(); }); + + it("Succeeds when content-type is application/json and datacontentencoding is base64", () => { + const expected = { + whose: "ours", + }; + const bindata = Uint32Array.from(JSON.stringify(expected) as string, (c) => c.codePointAt(0) as number); + const payload = asBase64(bindata); + + const attributes = { + [CONSTANTS.CE_HEADERS.TYPE]: "test", + [CONSTANTS.CE_HEADERS.SPEC_VERSION]: Version.V03, + [CONSTANTS.CE_HEADERS.SOURCE]: "/test-source", + [CONSTANTS.CE_HEADERS.ID]: "123456", + [CONSTANTS.CE_HEADERS.TIME]: "2019-06-16T11:42:00Z", + [CONSTANTS.HEADER_CONTENT_TYPE]: "application/json", + [CONSTANTS.BINARY_HEADERS_03.CONTENT_ENCODING]: "base64", + }; + const event = receiver.parse(payload, attributes); + expect(event.data).to.deep.equal(expected); + }); }); describe("Parse", () => { diff --git a/test/integration/receiver_structured_0_3_test.ts b/test/integration/receiver_structured_0_3_test.ts index f83e11f9..d47220bf 100644 --- a/test/integration/receiver_structured_0_3_test.ts +++ b/test/integration/receiver_structured_0_3_test.ts @@ -3,6 +3,8 @@ import { expect } from "chai"; import { CloudEvent, ValidationError, Version } from "../../src"; import { StructuredHTTPReceiver } from "../../src/transport/http/structured_receiver"; +import { asBase64 } from "../../src/event/validation"; +import CONSTANTS from "../../src/constants"; const receiver = new StructuredHTTPReceiver(Version.V03); const type = "com.github.pull.create"; @@ -85,6 +87,26 @@ describe("HTTP Transport Binding Structured Receiver CloudEvents v0.3", () => { expect(receiver.parse.bind(receiver, event, attributes)).to.throw(ValidationError, "invalid payload"); }); + it("Succeeds when content-type is application/cloudevents+json and datacontentencoding is base64", () => { + const expected = { + whose: "ours", + }; + const bindata = Uint32Array.from(JSON.stringify(expected) as string, (c) => c.codePointAt(0) as number); + const payload = { + data: asBase64(bindata), + specversion: Version.V03, + source, + type, + datacontentencoding: CONSTANTS.ENCODING_BASE64, + }; + const attributes = { + "Content-Type": "application/cloudevents+json", + }; + + const event = receiver.parse(payload, attributes); + expect(event.data).to.deep.equal(expected); + }); + it("No error when all required stuff are in place", () => { // setup const payload = { From 3fab5f2c92859b22363b33024f8000dc3ceac1c3 Mon Sep 17 00:00:00 2001 From: Grant Timmerman Date: Thu, 30 Jul 2020 05:43:51 -0700 Subject: [PATCH 03/14] docs: update badge name (#289) Signed-off-by: Grant Timmerman --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c128711f..40eb73df 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/bd66e7c52002481993cd6d610534b0f7)](https://www.codacy.com/app/fabiojose/sdk-javascript?utm_source=github.com&utm_medium=referral&utm_content=cloudevents/sdk-javascript&utm_campaign=Badge_Grade) [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/bd66e7c52002481993cd6d610534b0f7)](https://www.codacy.com/app/fabiojose/sdk-javascript?utm_source=github.com&utm_medium=referral&utm_content=cloudevents/sdk-javascript&utm_campaign=Badge_Coverage) ![Node.js CI](https://github.com/cloudevents/sdk-javascript/workflows/Node.js%20CI/badge.svg) -[![npm version](https://img.shields.io/npm/v/cloudevents-sdk.svg)](https://www.npmjs.com/package/cloudevents-sdk) +[![npm version](https://img.shields.io/npm/v/cloudevents.svg)](https://www.npmjs.com/package/cloudevents) [![vulnerabilities](https://snyk.io/test/github/cloudevents/sdk-javascript/badge.svg)](https://snyk.io/test/github/cloudevents/sdk-javascript) The CloudEvents SDK for JavaScript. From 763838c89cc704397bf75c9cbde79ae0f5c731c1 Mon Sep 17 00:00:00 2001 From: Lucas Holmquist Date: Thu, 30 Jul 2020 09:24:02 -0400 Subject: [PATCH 04/14] chore: Update examples to use latest sdk changes (#282) * This updates all the examples to use the latest module version(3.0.0) Signed-off-by: Lucas Holmquist --- examples/express-ex/README.md | 15 +++++++++++++-- examples/express-ex/index.js | 4 ++-- examples/express-ex/package.json | 2 +- examples/payload/v03/structured-event-1.json | 2 +- examples/payload/v03/structured-event-2.json | 2 +- examples/payload/v1/structured-event-1.json | 2 +- examples/payload/v1/structured-event-2.json | 2 +- examples/typescript-ex/package.json | 4 +++- examples/typescript-ex/src/index.ts | 15 ++++++--------- examples/websocket/README.md | 2 +- examples/websocket/client.js | 2 +- examples/websocket/index.html | 4 ++-- examples/websocket/package.json | 2 +- examples/websocket/server.js | 6 +++--- 14 files changed, 37 insertions(+), 27 deletions(-) diff --git a/examples/express-ex/README.md b/examples/express-ex/README.md index 5a41f8fa..9342c515 100644 --- a/examples/express-ex/README.md +++ b/examples/express-ex/README.md @@ -67,7 +67,7 @@ curl -X POST \ -H'ce-source:https://github.com/cloudevents/spec/pull/123' \ -H'ce-id:45c83279-c8a1-4db6-a703-b3768db93887' \ -H'ce-time:2019-11-06T11:17:00Z' \ - -H'ce-my-extension:extension value' \ + -H'ce-myextension:extension value' \ http://localhost:3000/ ``` @@ -110,6 +110,17 @@ curl -X POST \ http://localhost:3000/ ``` +__A Structured One with Base64 Event Data__ + +> Payload [example](../payload/v03/structured-event-2.json) + +```bash +curl -X POST \ + -d'@../payload/v03/structured-event-2.json' \ + -H'Content-Type:application/cloudevents+json' \ + http://localhost:3000/ +``` + __A Binary One__ ```bash @@ -135,7 +146,7 @@ curl -X POST \ -H'ce-source:https://github.com/cloudevents/spec/pull/123' \ -H'ce-id:45c83279-c8a1-4db6-a703-b3768db93887' \ -H'ce-time:2019-06-21T17:31:00Z' \ - -H'ce-my-extension:extension value' \ + -H'ce-myextension:extension value' \ http://localhost:3000/ ``` diff --git a/examples/express-ex/index.js b/examples/express-ex/index.js index da72cf89..0939e6fe 100644 --- a/examples/express-ex/index.js +++ b/examples/express-ex/index.js @@ -1,7 +1,7 @@ /* eslint-disable no-console */ const express = require("express"); -const { Receiver } = require("cloudevents-sdk"); +const { Receiver } = require("cloudevents"); const app = express(); const receiver = new Receiver(); @@ -25,7 +25,7 @@ app.post("/", function (req, res) { console.log("BODY", req.body); try { - const event = receiver.accept(req.headers, req.body); + const event = Receiver.accept(req.headers, req.body); console.log(`Accepted event: ${event}`); res.status(201).json(event); } catch (err) { diff --git a/examples/express-ex/package.json b/examples/express-ex/package.json index ae7e226f..3d483f18 100644 --- a/examples/express-ex/package.json +++ b/examples/express-ex/package.json @@ -14,7 +14,7 @@ "author": "fabiojose@gmail.com", "license": "Apache-2.0", "dependencies": { - "cloudevents-sdk": "~2.0.2", + "cloudevents": "~3.0.0", "express": "^4.17.1" } } diff --git a/examples/payload/v03/structured-event-1.json b/examples/payload/v03/structured-event-1.json index 945b3494..a96db00c 100644 --- a/examples/payload/v03/structured-event-1.json +++ b/examples/payload/v03/structured-event-1.json @@ -8,7 +8,7 @@ "data":{ "much":"wow" }, - "my-extension" : { + "myextension" : { "some" : "thing" } } diff --git a/examples/payload/v03/structured-event-2.json b/examples/payload/v03/structured-event-2.json index 2745df36..63ce7880 100644 --- a/examples/payload/v03/structured-event-2.json +++ b/examples/payload/v03/structured-event-2.json @@ -7,7 +7,7 @@ "datacontenttype":"application/json", "datacontentencoding":"base64", "data":"eyJtdWNoIjoid293In0=", - "my-extension" : { + "myextension" : { "some" : "thing" } } diff --git a/examples/payload/v1/structured-event-1.json b/examples/payload/v1/structured-event-1.json index 87b8b706..5551b448 100644 --- a/examples/payload/v1/structured-event-1.json +++ b/examples/payload/v1/structured-event-1.json @@ -8,5 +8,5 @@ "data":{ "much":"wow" }, - "my-extension" : "something" + "myextension" : "something" } diff --git a/examples/payload/v1/structured-event-2.json b/examples/payload/v1/structured-event-2.json index 6401d67e..c4190d4e 100644 --- a/examples/payload/v1/structured-event-2.json +++ b/examples/payload/v1/structured-event-2.json @@ -6,5 +6,5 @@ "time":"2019-11-06T11:08:00Z", "datacontenttype":"application/json", "data_base64":"eyJtdWNoIjoid293In0=", - "my-extension" : "something" + "myextension" : "something" } diff --git a/examples/typescript-ex/package.json b/examples/typescript-ex/package.json index c577e5fb..111195e7 100644 --- a/examples/typescript-ex/package.json +++ b/examples/typescript-ex/package.json @@ -24,8 +24,10 @@ }, "devDependencies": { "@types/node": "^8.9.0", - "cloudevents-sdk": "~2.0.2", "gts": "^1.1.0", "typescript": "~3.9.5" + }, + "dependencies": { + "cloudevents": "~3.0.1" } } diff --git a/examples/typescript-ex/src/index.ts b/examples/typescript-ex/src/index.ts index 0a871ad3..af084a6b 100644 --- a/examples/typescript-ex/src/index.ts +++ b/examples/typescript-ex/src/index.ts @@ -1,8 +1,6 @@ -import { CloudEvent, CloudEventV1, Receiver } from "cloudevents-sdk"; - -export function doSomeStuff() { - const receiver = new Receiver(); +import { CloudEvent, CloudEventV1, Receiver } from "cloudevents"; +export function doSomeStuff(): void { const myevent: CloudEventV1 = new CloudEvent({ source: "/source", type: "type", @@ -10,8 +8,8 @@ export function doSomeStuff() { dataschema: "https://d.schema.com/my.json", subject: "cha.json", data: "my-data", + extension1: "some extension data" }); - myevent.extension1 = "some extension data"; console.log("My structured event:", myevent); @@ -23,7 +21,7 @@ export function doSomeStuff() { // Typically used with an incoming HTTP request where myevent.format() is the actual // body of the HTTP - console.log("Received structured event:", receiver.accept(headers, myevent)); + console.log("Received structured event:", Receiver.accept(headers, myevent)); // ------ receiver binary const data = { @@ -40,10 +38,9 @@ export function doSomeStuff() { "ce-extension1": "extension1", }; - console.log("My binary event:", receiver.accept(attributes, data)); - console.log("My binary event extensions:", receiver.accept(attributes, data)); + console.log("My binary event:", Receiver.accept(attributes, data)); + console.log("My binary event extensions:", Receiver.accept(attributes, data)); - return true; } doSomeStuff(); diff --git a/examples/websocket/README.md b/examples/websocket/README.md index 8c5b7d2d..f39ce101 100644 --- a/examples/websocket/README.md +++ b/examples/websocket/README.md @@ -24,7 +24,7 @@ responds with a CloudEvent containing the body of the Weather API response as th event data. You will need to change one line in the `server.js` file and provide your Open -Weather API key. +Weather API key. You can also create a environment variable `OPEN_WEATHER_API_KEY` and store your key there. To start the server, run `node server.js`. diff --git a/examples/websocket/client.js b/examples/websocket/client.js index eb949c8f..d656571e 100644 --- a/examples/websocket/client.js +++ b/examples/websocket/client.js @@ -3,7 +3,7 @@ const readline = require("readline"); const WebSocket = require("ws"); const ws = new WebSocket("ws://localhost:8080"); -const { CloudEvent } = require("cloudevents-sdk"); +const { CloudEvent } = require("cloudevents"); const rl = readline.createInterface({ input: process.stdin, diff --git a/examples/websocket/index.html b/examples/websocket/index.html index da6ba43d..d3607a8b 100644 --- a/examples/websocket/index.html +++ b/examples/websocket/index.html @@ -2,7 +2,7 @@ CloudEvent Example - + +