Skip to content

feat: integrate cloudevents module #160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"types": "build/src/invoker.d.ts",
"dependencies": {
"body-parser": "^1.18.3",
"cloudevents": "^3.0.1",
"express": "^4.16.4",
"minimist": "^1.2.0",
"on-finished": "^2.3.0"
Expand Down
40 changes: 6 additions & 34 deletions src/cloudevents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,43 +13,15 @@
// limitations under the License.

import * as express from 'express';
import { CloudEventsContext } from './functions';
import { CloudEvent, Receiver } from 'cloudevents';

/**
* Checks whether the incoming request is a CloudEvents event in binary content
* mode. This is verified by checking the presence of required headers.
*
* @link https://github.com/cloudevents/spec/blob/master/http-protocol-binding.md#3-http-message-mapping
*
* @param req Express request object.
* @return True if the request is a CloudEvents event in binary content mode,
* false otherwise.
*/
export function isBinaryCloudEvent(req: express.Request): boolean {
return !!(
req.header('ce-type') &&
req.header('ce-specversion') &&
req.header('ce-source') &&
req.header('ce-id')
);
}

/**
* Returns a CloudEvents context from the given CloudEvents request. Context
* attributes are retrieved from request headers.
* Returns a CloudEvent from the given CloudEvents HTTP request.
*
* @param req Express request object.
* @return CloudEvents context.
* @return The CloudEvent
*/
export function getBinaryCloudEventContext(
req: express.Request
): CloudEventsContext {
const context: CloudEventsContext = {};
for (const name in req.headers) {
if (name.startsWith('ce-')) {
const attributeName = name.substr('ce-'.length);
context[attributeName] = req.header(name);
}
}
return context;
export function getCloudEvent(req: express.Request): CloudEvent {
const headers = req.headers as { [key: string]: string };
return Receiver.accept(headers, req.body);
}
46 changes: 4 additions & 42 deletions src/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

import * as express from 'express';
import { CloudEventV1Attributes } from 'cloudevents';

export interface HttpFunction {
// tslint:disable-next-line:no-any express interface.
Expand All @@ -28,11 +29,11 @@ export interface EventFunctionWithCallback {
}
export interface CloudEventFunction {
// tslint:disable-next-line:no-any
(cloudevent: CloudEventsContext): any;
(cloudevent: CloudEventV1Attributes): any;
}
export interface CloudEventFunctionWithCallback {
// tslint:disable-next-line:no-any
(cloudevent: CloudEventsContext, callback: Function): any;
(cloudevent: CloudEventV1Attributes, callback: Function): any;
}
export type HandlerFunction =
| HttpFunction
Expand Down Expand Up @@ -66,43 +67,4 @@ export interface CloudFunctionsContext {
resource?: string;
}

/**
* The CloudEvents v1.0 context object for the event.
*
* @link https://github.com/cloudevents/spec/blob/master/spec.md#context-attributes
*/
export interface CloudEventsContext {
/**
* Type of occurrence which has happened.
*/
type?: string;
/**
* The version of the CloudEvents specification which the event uses.
*/
specversion?: string;
/**
* The event producer.
*/
source?: string;
/**
* ID of the event.
*/
id?: string;
/**
* Timestamp of when the event happened.
*/
time?: string;
/**
* A link to the schema that the event data adheres to.
*/
schemaurl?: string;
/**
* Content type of the event data.
*/
contenttype?: string;

// tslint:disable-next-line:no-any CloudEvents extension attributes.
[key: string]: any;
}

export type Context = CloudFunctionsContext | CloudEventsContext;
export type Context = CloudFunctionsContext | CloudEventV1Attributes;
22 changes: 6 additions & 16 deletions src/invoker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import * as onFinished from 'on-finished';

import { FUNCTION_STATUS_HEADER_FIELD } from './types';
import { logAndSendError } from './logger';
import { isBinaryCloudEvent, getBinaryCloudEventContext } from './cloudevents';
import { getCloudEvent } from './cloudevents';
import {
HttpFunction,
EventFunction,
Expand Down Expand Up @@ -144,11 +144,7 @@ function wrapCloudEventFunction(
}
}
);
let cloudevent = req.body;
if (isBinaryCloudEvent(req)) {
cloudevent = getBinaryCloudEventContext(req);
cloudevent.data = req.body;
}
const cloudevent = getCloudEvent(req);
// Callback style if user function has more than 2 arguments.
if (userFunction!.length > 2) {
const fn = userFunction as CloudEventFunctionWithCallback;
Expand Down Expand Up @@ -197,17 +193,11 @@ function wrapEventFunction(
}
}
);
let data = event.data;
const data = event.data;
let context = event.context;
if (isBinaryCloudEvent(req)) {
// Support CloudEvents in binary content mode, with data being the whole
// request body and context attributes retrieved from request headers.
data = event;
context = getBinaryCloudEventContext(req);
} else if (context === undefined) {
// Support legacy events and CloudEvents in structured content mode, with
// context properties represented as event top-level properties.
// Context is everything but data.
if (context === undefined) {
// Support legacy events with context properties represented as event
// top-level properties. Context is everything but data.
context = event;
// Clear the property before removing field so the data object
// is not deleted.
Expand Down
Loading