Skip to content

Commit

Permalink
src(templates)!: modify the nodejs event template to accept a cloudev…
Browse files Browse the repository at this point in the history
…ent (#356)

* Simplify the nodejs events template

Pretty-printing the contents of the event and its envelope for each
request. This is handy when invoked as a Knative event sink as some
sources, e.g. Kafka, ignore the response body.

Co-authored-by: Lance Ball <lball@redhat.com>

* Expect the event parameter to be the entire CloudEvent

This will require a change to the faas-js-runtime, but at least the
template won't need to change when that's released

* Update nodejs guide to reflect 2nd arg's type is CloudEvent

* Templates now using faas-js-runtime 0.7.1

Co-authored-by: Lance Ball <lball@redhat.com>
  • Loading branch information
jcrossley3 and lance committed May 25, 2021
1 parent f62303b commit caf0659
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 194 deletions.
60 changes: 35 additions & 25 deletions docs/guides/nodejs.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,20 +86,27 @@ If you prefer another, that's no problem. Just remove the `tape` dependency from

## Function reference

Boson Node.js functions have very few restrictions. You can add any required dependencies
in `package.json`, and you may include additional local JavaScript files. The only real
requirements are that your project contain an `index.js` file which exports a single function.
In this section, we will look in a little more detail at how Boson functions are invoked,
and what APIs are available to you as a developer.
Boson Node.js functions have very few restrictions. You can add any
required dependencies in `package.json`, and you may include
additional local JavaScript files. The only real requirements are that
your project contain an `index.js` file which exports a single
function. In this section, we will look in a little more detail at
how Boson functions are invoked, and what APIs are available to you as
a developer.

### Invocation parameters

When using the `func` CLI to create a function project, you may choose to generate a project
that responds to a `CloudEvent` or simple HTTP. `CloudEvents` in Knative are transported over
HTTP as a `POST` request, so in many ways, the two types of functions are very much the same.
They each will listen and respond to incoming HTTP events.
When using the `func` CLI to create a function project, you may choose
to generate a project that responds to a `CloudEvent` or simple
HTTP. `CloudEvents` in Knative are transported over HTTP as a `POST`
request, so in many ways, the two types of functions are very much the
same. They each will listen and respond to incoming HTTP events.

When an incoming request is received, your function will be invoked with a `Context` object as the first parameter. If the incoming request is a `CloudEvent`, any data associated with the `CloudEvent` is extracted from the event and provided as a second parameter. For example, a `CloudEvent` is received which contains a JSON string such as this in its data property,
When an incoming request is received, your function will be invoked
with a `Context` object as the first parameter. If the incoming
request is a `CloudEvent`, it will be provided as the second
parameter. For example, a `CloudEvent` is received which contains a
JSON string such as this in its data property,

```json
{
Expand All @@ -108,12 +115,12 @@ When an incoming request is received, your function will be invoked with a `Cont
}
```

When invoked, the second parameter to the function, after the `Context` object, will be
a JavaScript object having `customerId` and `productId` properties. The function
signature might look like this, with the `data` parameter having these properties.
So to log that string from your function, you might do this:

```js
function processPurchase(context, data)
function handle(context, event) {
console.log(JSON.stringify(event.data, null, 2));
}
```
### Return Values
Functions may return any valid JavaScript type, or nothing at all. When a
Expand All @@ -126,7 +133,10 @@ the returned values are extracted and sent with the response.

#### Example
```js
function processCustomer(context, customer) {
function handle(context, event) {
return processCustomer(event.data)
}
function processCustomer(customer) {
// process customer and return a new CloudEvent
return new CloudEvent({
source: 'customer.processor',
Expand All @@ -142,7 +152,7 @@ extracted and sent with the response to the caller.

#### Example
```js
function processCustomer(context, customer) {
function processCustomer(customer) {
// process customer and return custom headers
// the response will be '204 No content'
return { headers: { customerid: customer.id } };
Expand All @@ -155,7 +165,7 @@ Developers may set the response code returned to the caller by adding a

#### Example
```js
function processCustomer(context, customer) {
function processCustomer(customer) {
// process customer
if (customer.restricted) {
return { statusCode: 451 }
Expand All @@ -167,7 +177,7 @@ This also works with `Error` objects thrown from the function.

#### Example
```js
function processCustomer(context, customer) {
function processCustomer(customer) {
// process customer
if (customer.restricted) {
const err = new Error(‘Unavailable for legal reasons’);
Expand All @@ -193,7 +203,7 @@ The log adheres to the Pino logging API (https://getpino.io/#/docs/api).

#### Example
```js
Function myFunction(context) {
Function handle(context) {
context.log.info(“Processing customer”);
}
```
Expand All @@ -216,7 +226,7 @@ attributes are also found on the context object itself.

#### Example
```js
Function myFunction(context) {
Function handle(context) {
// Log the 'name' query parameter
context.log.info(context.query.name);
// Query parameters also are attached to the context
Expand All @@ -242,7 +252,7 @@ be parsed so that the attributes are directly available.

#### Example
```js
Function myFunction(context) {
Function handle(context) {
// log the incoming request body's 'hello' parameter
context.log.info(context.body.hello);
}
Expand All @@ -264,7 +274,7 @@ Returns the HTTP request headers as an object.

#### Example
```js
Function myFunction(context) {
Function handle(context) {
context.log.info(context.headers[custom-header]);
}
```
Expand Down Expand Up @@ -309,10 +319,10 @@ A function which accepts a data value and returns a CloudEvent.
#### Example
```js
// Expects to receive a CloudEvent with customer data
function processCustomer(context, customer) {
function handle(context, event) {
// process the customer
const processed = processCustomer(customer);
return context.cloudEventResponse(customer);
const processed = processCustomer(event.data);
return context.cloudEventResponse(processed);
}
```

Expand Down
11 changes: 3 additions & 8 deletions templates/node/events/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
# Node.js Cloud Events Function

Welcome to your new Node.js function project! The boilerplate function code can
be found in [`index.js`](./index.js). This function is meant to respond
exclusively to [Cloud Events](https://cloudevents.io/), but you can remove the
check for this in the function and it will respond just fine to plain vanilla
incoming HTTP requests. Additionally, this example function is written
asynchronously, returning a `Promise`. If your function does not perform any
asynchronous execution, you can safely remove the `async` keyword from the
function, and return raw values instead of a `Promise`.
Welcome to your new Node.js function project! The boilerplate function
code can be found in [`index.js`](./index.js). This function is meant
to respond to [Cloud Events](https://cloudevents.io/).

## Local execution

Expand Down
43 changes: 15 additions & 28 deletions templates/node/events/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
const { CloudEvent, HTTP } = require('cloudevents');

/**
* An example function that responds to incoming CloudEvents over HTTP. For example,
* from the Knative event Broker. Try invoking with a request such as this.
* A function that responds to incoming CloudEvents over HTTP from,
* for example, a Knative event Source, Channel or Broker.
*
* If running via 'npm run local', it can be invoked like so:
*
* curl -X POST -d '{"name": "Tiger", "customerId": "0123456789"}' \
* -H'Content-type: application/json' \
Expand All @@ -13,37 +15,22 @@ const { CloudEvent, HTTP } = require('cloudevents');
* -H'Ce-specversion: 1.0' \
* http://localhost:8080
*
* The event data is extracted from the incoming event and provided as the first
* parameter to the function. The CloudEvent object itself may be accessed via the
* context parameter, For example:
*
* const incomingEvent = context.cloudevent;
*
* @param {Context} context the invocation context
* @param {Object} user the CloudEvent data. If the data content type is application/json
* this will be converted to an Object via JSON.parse()
* @param {Object} event the CloudEvent
*/
function verifyUser(context, user) {
if (!context.cloudevent) {
return {
message: 'No cloud event received'
};
}
function handle(context, event) {

context.log.info('Processing user', user);
context.log.info(`CloudEvent received: ${context.cloudevent.toString()}`);
context.log.info("context");
console.log(JSON.stringify(context, null, 2));

context.log.info("event");
console.log(JSON.stringify(event, null, 2));

user = verify(user);
return HTTP.binary(new CloudEvent({
source: 'function.verifyUser',
type: 'user:verified',
data: user
source: 'event.handler',
type: 'echo',
data: event
}));
};

function verify(user) {
// do something with the user
return user;
}

module.exports = verifyUser;
module.exports = handle;
64 changes: 32 additions & 32 deletions templates/node/events/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion templates/node/events/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"debug": "nodemon --inspect ./node_modules/faas-js-runtime/bin/cli.js ./index.js"
},
"devDependencies": {
"faas-js-runtime": "0.6.0",
"faas-js-runtime": "0.7.1",
"nodemon": "^2.0.4",
"supertest": "^4.0.2",
"tape": "^4.13.0"
Expand Down

0 comments on commit caf0659

Please sign in to comment.