Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Scaffolder committed Oct 28, 2024
0 parents commit f666be4
Show file tree
Hide file tree
Showing 10 changed files with 3,990 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .funcignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

# Use the .funcignore file to exclude files which should not be
# tracked in the image build. To instruct the system not to track
# files in the image build, add the regex pattern or file information
# to this file.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

# Functions use the .func directory for local runtime data which should
# generally not be tracked in source control. To instruct the system to track
# .func in source control, comment the following line (prefix it with '# ').
/.func
133 changes: 133 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# 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 to [Cloud Events](https://cloudevents.io/).

## Local execution

After executing `npm install`, you can run this function locally by executing
`npm run local`.

The runtime will expose three endpoints.

* `/` The endpoint for your function.
* `/health/readiness` The endpoint for a readiness health check
* `/health/liveness` The endpoint for a liveness health check

The health checks can be accessed in your browser at
[http://localhost:8080/health/readiness]() and
[http://localhost:8080/health/liveness](). You can use `curl` to `POST` an event
to the function endpoint:

```console
curl -X POST -d '{"name": "Tiger", "customerId": "0123456789"}' \
-H'Content-type: application/json' \
-H'Ce-id: 1' \
-H'Ce-source: cloud-event-example' \
-H'Ce-type: dev.knative.example' \
-H'Ce-specversion: 1.0' \
http://localhost:8080
```

The readiness and liveness endpoints use
[overload-protection](https://www.npmjs.com/package/overload-protection) and
will respond with `HTTP 503 Service Unavailable` with a `Client-Retry` header if
your function is determined to be overloaded, based on the memory usage and
event loop delay.

## The Function Interface

The `index.js` file may export a single function or a `Function`
object. The `Function` object allows developers to add lifecycle hooks for
initialization and shutdown, as well as providing a way to implement custom
health checks.

The `Function` interface is defined as:

```typescript
export interface Function {
// The initialization function, called before the server is started
// This function is optional and should be synchronous.
init?: () => any;

// The shutdown function, called after the server is stopped
// This function is optional and should be synchronous.
shutdown?: () => any;

// The liveness function, called to check if the server is alive
// This function is optional and should return 200/OK if the server is alive.
liveness?: HealthCheck;

// The readiness function, called to check if the server is ready to accept requests
// This function is optional and should return 200/OK if the server is ready.
readiness?: HealthCheck;

logLevel?: LogLevel;

// The function to handle HTTP requests
handle: CloudEventFunction | HTTPFunction;
}
```

## Handle Signature

CloudEvent functions are used in environments where the incoming HTTP request is a CloudEvent. The function signature is:

```typescript
interface CloudEventFunction {
(context: Context, event: CloudEvent): CloudEventFunctionReturn;
}
```

Where the return type is defined as:

```typescript
type CloudEventFunctionReturn = Promise<CloudEvent> | CloudEvent | HTTPFunctionReturn;
type HTTPFunctionReturn = Promise<StructuredReturn> | StructuredReturn | ResponseBody | void;
```

The function return type can be anything that a simple HTTP function can return or a CloudEvent. Whatever is returned, it will be sent back to the caller as a response.

Where the `StructuredReturn` is a JavaScript object with the following properties:

```typescript
interface StructuredReturn {
statusCode?: number;
headers?: Record<string, string>;
body?: ResponseBody;
}
```

If the function returns a `StructuredReturn` object, then the `statusCode` and `headers` properties are used to construct the HTTP response. If the `body` property is present, it is used as the response body. If the function returns `void` or `undefined`, then the response body is empty.

The `ResponseBody` is either a string, a JavaScript object, or a Buffer. JavaScript objects will be serialized as JSON. Buffers will be sent as binary data.

### Health Checks

The `Function` interface also allows for the addition of a `liveness` and `readiness` function. These functions are used to implement health checks for the function. The `liveness` function is called to check if the function is alive. The `readiness` function is called to check if the function is ready to accept requests. If either of these functions returns a non-200 status code, then the function is considered unhealthy.

A health check function is defined as:

```typescript
/**
* The HealthCheck interface describes a health check function,
* including the optional path to which it should be bound.
*/
export interface HealthCheck {
(request: Http2ServerRequest, reply: Http2ServerResponse): any;
path?: string;
}
```

By default, the health checks are bound to the `/health/liveness` and `/health/readiness` paths. You can override this by setting the `path` property on the `HealthCheck` object, or by setting the `LIVENESS_URL` and `READINESS_URL` environment variables.

## Testing

This function project includes a [unit test](./test/unit.js) and an
[integration test](./test/integration.js). All `.js` files in the test directory
are run.

```console
npm test
```
12 changes: 12 additions & 0 deletions catalog-info.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: "shop-email-sender"
annotations:
github.com/project-slug: keventmesh/shop-email-sender
backstage.io/kubernetes-id: shop-email-sender
backstage.io/techdocs-ref: dir:.
spec:
type: website
lifecycle: experimental
owner: "shop"
9 changes: 9 additions & 0 deletions func.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
specVersion: 0.36.0
name: node-cloudevents
runtime: node
created: 2024-01-01T00:00:00.000000+00:00
invoke: cloudevent
build:
buildEnvs: []
run:
envs: []
33 changes: 33 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const { CloudEvent } = require('cloudevents');

/**
* Your CloudEvent handling function, invoked with each request.
* This example function logs its input, and responds with a CloudEvent
* which echoes the incoming event data
*
* It can be invoked with 'func invoke'
* It can be tested with 'npm test'
*
* @param {Context} context a context object.
* @param {object} context.body the request body if any
* @param {object} context.query the query string deserialzed as an object, if any
* @param {object} context.log logging object with methods for 'info', 'warn', 'error', etc.
* @param {object} context.headers the HTTP request headers
* @param {string} context.method the HTTP request method
* @param {string} context.httpVersion the HTTP protocol version
* See: https://github.com/knative/func/blob/main/docs/function-developers/nodejs.md#the-context-object
* @param {CloudEvent} event the CloudEvent
*/
const handle = async (context, event) => {
// YOUR CODE HERE
context.log.info("context", context);
context.log.info("event", event);

return new CloudEvent({
source: 'event.handler',
type: 'echo',
data: event.data
});
};

module.exports = { handle };
Loading

0 comments on commit f666be4

Please sign in to comment.