From ff5d96548176720acc56480aee18c1b4043081c0 Mon Sep 17 00:00:00 2001 From: Lance Ball Date: Fri, 8 May 2020 12:09:13 -0400 Subject: [PATCH] docs: update README and examples with new API This commit modifies the README to show new API usage for the `HTTPReceiver` and `CloudEvent` classes, and updates the examples to use this as well. Overall structure and content has been modified to look more like the sdk-go README. Fixes: https://github.com/cloudevents/sdk-javascript/issues/128 Signed-off-by: Lance Ball --- README.md | 297 ++++++---------------------- examples/express-ex/README.md | 29 ++- examples/express-ex/index.js | 64 +----- examples/typescript-ex/src/index.ts | 44 +---- 4 files changed, 85 insertions(+), 349 deletions(-) diff --git a/README.md b/README.md index 9e6e51f2..f856086f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ [![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) [![Build Status](https://travis-ci.org/cloudevents/sdk-javascript.svg?branch=master)](https://travis-ci.org/cloudevents/sdk-javascript) -[![downloads](https://img.shields.io/npm/dy/cloudevents-sdk.svg)](https://www.npmjs.com/package/cloudevents-sdk) [![npm version](https://img.shields.io/npm/v/cloudevents-sdk.svg)](https://www.npmjs.com/package/cloudevents-sdk) [![vulnerabilities](https://snyk.io/test/github/cloudevents/sdk-javascript/badge.svg)](https://snyk.io/test/github/cloudevents/sdk-javascript) [![licence](https://img.shields.io/github/license/cloudevents/sdk-javascript)](http://www.apache.org/licenses/LICENSE-2.0) @@ -11,273 +10,91 @@ The CloudEvents SDK for JavaScript. -## Status +This module will help you to: -This SDK is still considered a work in progress. +* Represent CloudEvents in memory +* Use [Event Formats](https://github.com/cloudevents/spec/blob/v1.0/spec.md#event-format) to serialize/deserialize CloudEvents +* Use [Protocol Bindings](https://github.com/cloudevents/spec/blob/v1.0/spec.md#protocol-binding) to send/receive CloudEvents -This SDK current supports the following versions of CloudEvents: +_Note:_ Supported +[CloudEvents specification](https://github.com/cloudevents/spec): 0.3, 1.0 -- v1.0 +### A Note on Versioning -**Checkout the [changelog](CHANGELOG.md) to see what's going on!** +The CloudEvents protocol version is distinct from this module's version number. +For example, this module may be versioned as v2.0.0 but support the v0.3 and v1.0 +versions of the CloudEvent specification. -## Installation +## Usage -This CloudEvents SDK requires nodejs 6.11+ +**See the full working example: [here](./examples/express-ex).** -### Nodejs +### Installation -```sh -npm install cloudevents-sdk -``` -## Specification Support - -These are the supported specifications by this version. - -| **Specifications** | v0.3 | **v1.0** | -|---------------------------------------|------|----------| -| CloudEvents | yes | yes | -| HTTP Transport Binding - Structured | yes | yes | -| HTTP Transport Binding - Binary | yes | yes | -| JSON Event Format | yes | yes | - -### What we can do - -| **What** | v0.3 | **v1.0** | -|-------------------------------------|------|----------| -| Create events | yes | yes | -| Emit Structured events over HTTP | yes | yes | -| Emit Binary events over HTTP | yes | yes | -| JSON Event Format | yes | yes | -| Receive Structured events over HTTP | yes | yes | -| Receive Binary events over HTTP | yes | yes | +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: -## How to use - -### Usage +```console +npm install --save cloudevents-sdk +``` -```js -const v1 = require("cloudevents-sdk/v1"); +### Receiving and Emitting Events -/* - * Creating an event - */ -let myevent = v1.event() - .type("com.github.pull.create") - .source("urn:event:from:myapi/resource/123"); -``` +#### Receiving Events -#### Formatting +You can choose almost any popular web framework for port binding. Use an +`HTTPReceiver` to process the incoming HTTP request. The receiver accepts +binary and structured events in either the 1.0 or 0.3 protocol formats. ```js -const v1 = require("cloudevents-sdk/v1"); +const { + CloudEvent, + HTTPReceiever +} = require("cloudevents-sdk"); -/* - * Creating an event - */ -let myevent = v1.event() - .type("com.github.pull.create") - .source("urn:event:from:myapi/resource/123"); +// Create a receiver to accept events over HTTP +const receiver = new HTTPReceiver(); -/* - * Format the payload and return it - */ -let formatted = myevent.format(); +// body and headers come from an incoming HTTP request, e.g. express.js +const receivedEvent = receiver.accept(req.body, req.headers); +console.log(receivedEvent.format()); ``` -#### Emitting +#### Emitting Events + +Currently, to emit events, you'll need to decide whether the event is in +binary or structured format, and determine what version of the CloudEvents +specification you want to send the event as. ```js -const v1 = require("cloudevents-sdk/v1"); +const { CloudEvent } = require("cloudevents-sdk"); +const { StructuredHTTPEmitter } = require("cloudevents-sdk/v1"); -/* - * Creating an event - */ -let myevent = v1.event() +const myevent = new CloudEvent() .type("com.github.pull.create") .source("urn:event:from:myapi/resource/123"); -// The binding configuration using POST -let config = { +const emitter = new StructuredHTTPEmitter({ method: "POST", url : "https://myserver.com" -}; - -// The binding instance -let binding = new v1.StructuredHTTPEmitter(config); - -// Emit the event using Promise -binding.emit(myevent) - .then(response => { - // Treat the response - console.log(response.data); - - }).catch(err => { - // Deal with errors - console.error(err); - }); -``` - -#### Receiving Events - -You can choose any framework for port binding. But, use the -StructuredHTTPReceiver or BinaryHTTPReceiver to process the HTTP Payload and -HTTP Headers, extracting the CloudEvents. - -:smiley: **Checkout the full working example: [here](./examples/express-ex).** - -```js -// some parts were removed // - -const v1 = require("cloudevents-sdk/v1"); - -const receiver = new v1.StructuredHTTPReceiver(); - -// some parts were removed // - -app.post("/", (req, res) => { - try { - let myevent = receiver.parse(req.body, req.headers); - - // TODO use the event - - res.status(201).send("Event Accepted"); - - } catch(err) { - // TODO deal with errors - console.error(err); - res.status(415) - .header("Content-Type", "application/json") - .send(JSON.stringify(err)); - } }); -``` - -## Unit Testing - -The unit test checks the result of formatted payload and the constraints. -```bash -npm test +// Emit the event +emitter.emit(myevent) ``` -## The API +## Supported specification features -### `CloudEvent` class - -```js -/* - * Format the payload and return an Object. - */ -Object CloudEvent.format() - -/* - * Format the payload as String. - */ -String CloudEvent.toString() -``` - -### `Formatter` classes - -Every formatter class must implement these methods to work properly. - -```js -/* - * Format the CloudEvent payload argument and return an Object. - */ -Object Formatter.format(Object) - -/* - * Format the CloudEvent payload as String. - */ -String Formatter.toString(Object) -``` - -### `Parser` classes - -Every Parser class must implement these methods to work properly. - -```js -/* - * The default constructor with Parser as decorator - */ -Parser(Parser) - -/* - * Try to parse the payload to some event format - */ -Object Parser.parse(payload) -``` - -### `Spec` classes - -Every Spec class must implement these methods to work properly. - -```js -/* - * The constructor must receives the CloudEvent type. - */ -Spec(CloudEvent) - -/* - * Checks the spec constraints, throwing an error if do not pass. - * @throws Error when it is an invalid event - */ -Spec.check() - -/* - * Checks if the argument pass through the spec constraints - * @throws Error when it is an invalid event - */ -Spec.check(Object) -``` - -### `Binding` classes - -Every Binding class must implement these methods to work properly. - -#### Emitter Binding - -Following we have the signature for the binding to emit CloudEvents. - -```js -/* - * The constructor must receives the map of configurations. - */ -Binding(config) - -/* - * Emits the event using an instance of CloudEvent. - */ -Binding.emit(cloudEvent) -``` - -#### Receiver Binding - -Following we have the signature for the binding to receive CloudEvents. - -```js -/* - * The constructor must receives the map of configurations. - */ -Receiver(config) - -/* - * Checks if some Object and a Map of headers - * follows the binding definition, throwing an error if did not follow - */ -Receiver.check(Object, Map) - -/* - * Checks and parse as CloudEvent - */ -CloudEvent Receiver.parse(Object, Map) -``` - -## Versioning - -- `x.M.p`: where `x` relates to spec version, `M` relates to minor and `p` relates -to fixes. See [semver](https://semver.org/) +| | [v0.3](https://github.com/cloudevents/spec/tree/v0.3) | [v1.0](https://github.com/cloudevents/spec/tree/v1.0) | +| ----------------------------- | --- | --- | +| CloudEvents Core | :heavy_check_mark: | :heavy_check_mark: | +| AMQP Protocol Binding | :x: | :x: | +| AVRO Event Format | :x: | :x: | +| HTTP Protocol Binding | :heavy_check_mark: | :heavy_check_mark: | +| JSON Event Format | :heavy_check_mark: | :heavy_check_mark: | +| Kafka Protocol Binding | :x: | :x: | +| NATS Protocol Binding | :x: | :x: | +| STAN Protocol Binding | :x: | :x: | ## Community @@ -291,3 +108,9 @@ to fixes. See [semver](https://semver.org/) [CNCF's Slack workspace](https://slack.cncf.io/). - Email: https://lists.cncf.io/g/cncf-cloudevents-sdk - Contact for additional information: Fabio José (`@fabiojose` on slack). + +## Contributing + +We love contributions from the community! Please check the +[Contributor's Guide](https://github.com/cloudevents/sdk-javascript/blob/master/CONTRIBUTING.md) +for information on how to get involved. diff --git a/examples/express-ex/README.md b/examples/express-ex/README.md index 5ab53b1c..8cf44814 100644 --- a/examples/express-ex/README.md +++ b/examples/express-ex/README.md @@ -17,7 +17,7 @@ __A Structured One__ curl -X POST \ -d'@../payload/v1/structured-event-0.json' \ -H'Content-Type:application/cloudevents+json' \ - http://localhost:3000/v1 + http://localhost:3000/ ``` __A Structured One with Extension__ @@ -28,7 +28,7 @@ __A Structured One with Extension__ curl -X POST \ -d'@../payload/v1/structured-event-1.json' \ -H'Content-Type:application/cloudevents+json' \ - http://localhost:3000/v1 + http://localhost:3000/ ``` __A Structured One with Base64 Event Data__ @@ -39,7 +39,7 @@ __A Structured One with Base64 Event Data__ curl -X POST \ -d'@../payload/v1/structured-event-2.json' \ -H'Content-Type:application/cloudevents+json' \ - http://localhost:3000/v1 + http://localhost:3000/ ``` __A Binary One__ @@ -53,7 +53,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' \ - http://localhost:3000/v1/binary + http://localhost:3000/ ``` __A Binary One with Extension__ @@ -68,7 +68,7 @@ curl -X POST \ -H'ce-id:45c83279-c8a1-4db6-a703-b3768db93887' \ -H'ce-time:2019-11-06T11:17:00Z' \ -H'ce-my-extension:extension value' \ - http://localhost:3000/v1/binary + http://localhost:3000/ ``` __A Binary One with Base 64 Encoding__ @@ -82,12 +82,9 @@ 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' \ - http://localhost:3000/v1/binary + http://localhost:3000/ ``` -__A Batch One__ - -TODO ## Spec v0.3 @@ -99,7 +96,7 @@ __A Structured One__ curl -X POST \ -d'@../payload/v03/structured-event-0.json' \ -H'Content-Type:application/cloudevents+json' \ - http://localhost:3000/v03 + http://localhost:3000/ ``` __A Structured One with Extension__ @@ -110,7 +107,7 @@ __A Structured One with Extension__ curl -X POST \ -d'@../payload/v03/structured-event-1.json' \ -H'Content-Type:application/cloudevents+json' \ - http://localhost:3000/v03 + http://localhost:3000/ ``` __A Binary One__ @@ -124,7 +121,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' \ - http://localhost:3000/v03 + http://localhost:3000/ ``` __A Binary One with Extension__ @@ -139,7 +136,7 @@ curl -X POST \ -H'ce-id:45c83279-c8a1-4db6-a703-b3768db93887' \ -H'ce-time:2019-06-21T17:31:00Z' \ -H'ce-my-extension:extension value' \ - http://localhost:3000/v03 + http://localhost:3000/ ``` __A Binary One with Base 64 Encoding__ @@ -154,10 +151,6 @@ curl -X POST \ -H'ce-id:45c83279-c8a1-4db6-a703-b3768db93887' \ -H'ce-time:2019-06-21T17:31:00Z' \ -H'ce-datacontentencoding:base64' \ - http://localhost:3000/v03 + http://localhost:3000/ ``` -__A Batch One__ - -TODO - diff --git a/examples/express-ex/index.js b/examples/express-ex/index.js index ba6fa5e2..88607d33 100644 --- a/examples/express-ex/index.js +++ b/examples/express-ex/index.js @@ -1,14 +1,10 @@ /* eslint-disable no-console */ const express = require("express"); -const app = express(); - -const v03 = require("cloudevents-sdk/v03"); -const unmarshaller03 = new v03.HTTPUnmarshaller(); +const { HTTPReceiver } = require("../../"); -const v1 = require("cloudevents-sdk/v1"); -const structured1 = new v1.StructuredHTTPReceiver(); -const binary1 = new v1.BinaryHTTPReceiver(); +const app = express(); +const receiver = new HTTPReceiver(); app.use((req, res, next) => { let data = ""; @@ -24,18 +20,15 @@ app.use((req, res, next) => { }); }); -app.post("/v1", function(req, res) { +app.post("/", function(req, res) { console.log(req.headers); console.log(req.body); try { - const myevent = structured1.parse(req.body, req.headers); - // pretty print - console.log("Accepted event:"); - console.log(JSON.stringify(myevent.format(), null, 2)); - - res.status(201) - .json(myevent.format()); + const event = receiver.accept(req.headers, req.body); + const asJSON = event.format(); + console.log(`Accepted event: ${JSON.stringify(event.format(), null, 2)}`); + res.status(201).json(asJSON); } catch (err) { console.error(err); res.status(415) @@ -44,47 +37,6 @@ app.post("/v1", function(req, res) { } }); -app.post("/v1/binary", function(req, res) { - console.log(req.headers); - console.log(req.body); - - try { - const myevent = binary1.parse(req.body, req.headers); - // pretty print - console.log("Accepted event:"); - console.log(JSON.stringify(myevent.format(), null, 2)); - - res.status(201) - .json(myevent.format()); - } catch (err) { - console.error(err); - res.status(415) - .header("Content-Type", "application/json") - .send(JSON.stringify(err)); - } -}); - -app.post("/v03", function(req, res) { - console.log(req.headers); - console.log(req.body); - - unmarshaller03.unmarshall(req.body, req.headers) - .then((cloudevent) => { - // pretty print - console.log("Accepted event:"); - console.log(JSON.stringify(cloudevent.format(), null, 2)); - - res.status(201) - .json(cloudevent.format()); - }) - .catch((err) => { - console.error(err); - res.status(415) - .header("Content-Type", "application/json") - .send(JSON.stringify(err)); - }); -}); - app.listen(3000, function() { console.log("Example app listening on port 3000!"); }); diff --git a/examples/typescript-ex/src/index.ts b/examples/typescript-ex/src/index.ts index f12eee45..c5cc75a1 100644 --- a/examples/typescript-ex/src/index.ts +++ b/examples/typescript-ex/src/index.ts @@ -1,14 +1,9 @@ -import CloudEvent, { - event, - StructuredHTTPEmitter, - BinaryHTTPEmitter, - StructuredHTTPReceiver, - BinaryHTTPReceiver -} from 'cloudevents-sdk/v1'; +import { CloudEvent, HTTPREceiver } from '../../'; export function doSomeStuff() { + const receiver = new HTTPREceiver(); - const myevent: CloudEvent = event() + const myevent: CloudEvent = new CloudEvent() .source('/source') .type('type') .dataContentType('text/plain') @@ -20,39 +15,13 @@ export function doSomeStuff() { console.log(myevent.toString()); console.log(myevent.getExtensions()); - const config = { - method: "POST", - url : "https://enu90y24i64jp.x.pipedream.net/" - }; - - // ------ emitter structured - const structured = new StructuredHTTPEmitter(config); - structured.emit(myevent).then(res => { - // success - console.log("Structured Mode: Success!") - }) - .catch(err => { - // error - console.error(err); - }); - - // ------ emitter binary - const binary = new BinaryHTTPEmitter(config); - binary.emit(myevent).then(res => { - console.log("Binary Mode: Success!"); - }) - .catch(err => { - console.error(err); - }); - // ------ receiver structured const payload = myevent.toString(); const headers = { "Content-Type":"application/cloudevents+json" }; - const receiverStructured = new StructuredHTTPReceiver(); - console.log(receiverStructured.parse(payload, headers).toString()); + console.log(receiver.accept(headers, payload).toString()); // ------ receiver binary const extension1 = "mycuston-ext1"; @@ -70,10 +39,9 @@ export function doSomeStuff() { "ce-extension1" : extension1 }; - const receiverBinary = new BinaryHTTPReceiver(); - console.log(receiverBinary.parse(data, attributes).toString()); + console.log(receiver.accept(attributes, data).toString()); -return true; + return true; } doSomeStuff();