Skip to content
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
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,29 @@ const event3 = new CloudEvent({
});
```

### Extension Attributes

Any property passed to the `CloudEvent` constructor that is not a core attribute becomes an
[extension attribute](https://github.com/cloudevents/spec/blob/v1.0/spec.md#extension-context-attributes).
In binary mode each extension is sent as its own header, named after the attribute with the
transport's prefix: an extension `traceid` becomes the `ce-traceid` HTTP header. Extension names
must be lowercase letters and digits only.

Extension values should be one of the CloudEvents types: string, boolean, integer, binary, URI or
timestamp. The spec has no object type, so encode structured data as a string yourself:

```js
const ce = new CloudEvent({
source: "/orders",
type: "order.placed",
traceinfo: JSON.stringify({ span: "abc", sampled: true }),
});
```

As a convenience the SDK also accepts an object as an extension value and serializes it as JSON
when the event is sent in binary mode. This is not spec-conformant: a receiver gets a plain string
and has to know to parse it, and other SDKs may reject the event. Prefer the explicit form above.

### A Note About Big Integers

When parsing JSON data, if a JSON field value is a number, and that number
Expand Down
13 changes: 12 additions & 1 deletion src/event/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,18 @@ export interface CloudEventV1OptionalAttributes<T> {
data_base64?: string;

/**
* [OPTIONAL] CloudEvents extension attributes.
* [OPTIONAL] CloudEvents extension attributes. Any property that is not a
* core attribute is treated as an extension. Values should be one of the
* CloudEvents types: string, boolean, integer, binary, URI or timestamp.
*
* Object values are accepted as a convenience and for compatibility reasons.
* They are serialized as JSON in binary mode, but this is not spec-conformant;
* prefer encoding the value as a string yourself.
*
* @deprecated automatic serialization of object values is not spec-conformant
* and may be removed in future versions of the SDK.
*
* @see https://github.com/cloudevents/spec/blob/v1.0/spec.md#extension-context-attributes
*/
[key: string]: unknown;
}
7 changes: 6 additions & 1 deletion src/message/http/headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ export function headersFor<T>(event: CloudEventV1<T>): Headers {
if (map) {
headers[map.name] = map.parser.parse(value as string) as string;
} else if (property !== CONSTANTS.DATA_ATTRIBUTE && property !== `${CONSTANTS.DATA_ATTRIBUTE}_base64`) {
headers[`${CONSTANTS.EXTENSIONS_PREFIX}${property}`] = value as string;
// The CloudEvents type system has no object type: extension values are strings,
// booleans, integers, binary, URIs or timestamps. The SDK accepts objects as a
// convenience, so serialize them as JSON rather than let the HTTP client coerce
// them to "[object Object]". Prefer encoding the value as a string yourself.
headers[`${CONSTANTS.EXTENSIONS_PREFIX}${property}`] =
typeof value === "object" ? JSON.stringify(value) : (value as string);
}
}
});
Expand Down
17 changes: 17 additions & 0 deletions test/integration/message_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,23 @@ describe("HTTP transport", () => {
expect(message.headers["ce-extboolean"]).to.equal(false);
});

it("Serializes object-valued extensions as JSON in binary mode", () => {
const extobject = { some: "object", nested: { answer: 42 } };
const evt = new CloudEvent({ source: "test", type: "test", extobject });
const message = HTTP.binary(evt);
// HTTP headers can only carry strings; a raw object reaches the wire as "[object Object]"
expect(message.headers["ce-extobject"]).to.equal(JSON.stringify(extobject));
});

it("Round-trips an object-valued extension through binary mode as a JSON string", () => {
const extobject = { some: "object", nested: { answer: 42 } };
const evt = new CloudEvent({ source: "test", type: "test", extobject });
const received = HTTP.toEvent(HTTP.binary(evt)) as CloudEvent;
// the receiver sees the serialized form and is responsible for parsing it
expect(received.extobject).to.equal(JSON.stringify(extobject));
expect(JSON.parse(received.extobject as string)).to.deep.equal(extobject);
});

it("Includes extensions in structured when type is 'boolean' with a false value", () => {
const evt = new CloudEvent({ source: "test", type: "test", extboolean: false });
expect(evt.hasOwnProperty("extboolean")).to.equal(true);
Expand Down
Loading