fix: do not require an HTTP body on incoming binary event messages
This commit modifies the HTTP receivers/parsers to allow for the incoming body of an HTTP request to be empty if the event message is sent using the binary mode. In structured mode, a `ValidationError` will still be thrown, since the entire event must be encoded in the HTTP body. Signed-off-by: Lance Ball <lball@redhat.com>
This commit is contained in:
parent
d866691e29
commit
a7c326b48c
|
@ -32,10 +32,11 @@ export class BinaryHTTPReceiver {
|
||||||
* @returns {CloudEvent} an instance of CloudEvent representing the incoming request
|
* @returns {CloudEvent} an instance of CloudEvent representing the incoming request
|
||||||
* @throws {ValidationError} of the event does not conform to the spec
|
* @throws {ValidationError} of the event does not conform to the spec
|
||||||
*/
|
*/
|
||||||
parse(payload: string | Record<string, unknown>, headers: Headers): CloudEvent {
|
parse(payload: string | Record<string, unknown> | undefined | null, headers: Headers): CloudEvent {
|
||||||
if (!payload) throw new ValidationError("payload is null or undefined");
|
|
||||||
if (!headers) throw new ValidationError("headers is null or undefined");
|
if (!headers) throw new ValidationError("headers is null or undefined");
|
||||||
isStringOrObjectOrThrow(payload, new ValidationError("payload must be an object or a string"));
|
if (payload) {
|
||||||
|
isStringOrObjectOrThrow(payload, new ValidationError("payload must be an object or a string"));
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
headers[CONSTANTS.CE_HEADERS.SPEC_VERSION] &&
|
headers[CONSTANTS.CE_HEADERS.SPEC_VERSION] &&
|
||||||
|
@ -61,11 +62,15 @@ export class BinaryHTTPReceiver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const parser = parserByContentType[eventObj.datacontenttype as string];
|
let parsedPayload;
|
||||||
if (!parser) {
|
|
||||||
throw new ValidationError(`no parser found for content type ${eventObj.datacontenttype}`);
|
if (payload) {
|
||||||
|
const parser = parserByContentType[eventObj.datacontenttype as string];
|
||||||
|
if (!parser) {
|
||||||
|
throw new ValidationError(`no parser found for content type ${eventObj.datacontenttype}`);
|
||||||
|
}
|
||||||
|
parsedPayload = parser.parse(payload);
|
||||||
}
|
}
|
||||||
const parsedPayload = parser.parse(payload);
|
|
||||||
|
|
||||||
// Every unprocessed header can be an extension
|
// Every unprocessed header can be an extension
|
||||||
for (const header in sanitizedHeaders) {
|
for (const header in sanitizedHeaders) {
|
||||||
|
|
|
@ -31,7 +31,7 @@ export class StructuredHTTPReceiver {
|
||||||
* @returns {CloudEvent} a new CloudEvent instance for the provided headers and payload
|
* @returns {CloudEvent} a new CloudEvent instance for the provided headers and payload
|
||||||
* @throws {ValidationError} if the payload and header combination do not conform to the spec
|
* @throws {ValidationError} if the payload and header combination do not conform to the spec
|
||||||
*/
|
*/
|
||||||
parse(payload: Record<string, unknown> | string, headers: Headers): CloudEvent {
|
parse(payload: Record<string, unknown> | string | undefined | null, headers: Headers): CloudEvent {
|
||||||
if (!payload) throw new ValidationError("payload is null or undefined");
|
if (!payload) throw new ValidationError("payload is null or undefined");
|
||||||
if (!headers) throw new ValidationError("headers is null or undefined");
|
if (!headers) throw new ValidationError("headers is null or undefined");
|
||||||
isStringOrObjectOrThrow(payload, new ValidationError("payload must be an object or a string"));
|
isStringOrObjectOrThrow(payload, new ValidationError("payload must be an object or a string"));
|
||||||
|
|
|
@ -59,7 +59,10 @@ export class Receiver {
|
||||||
* @param {Object|JSON} body The body of the HTTP request
|
* @param {Object|JSON} body The body of the HTTP request
|
||||||
* @return {CloudEvent} A new {CloudEvent} instance
|
* @return {CloudEvent} A new {CloudEvent} instance
|
||||||
*/
|
*/
|
||||||
accept(headers: Headers, body: string | Record<string, unknown> | CloudEventV1 | CloudEventV03): CloudEvent {
|
accept(
|
||||||
|
headers: Headers,
|
||||||
|
body: string | Record<string, unknown> | CloudEventV1 | CloudEventV03 | undefined | null,
|
||||||
|
): CloudEvent {
|
||||||
const cleanHeaders: Headers = sanitize(headers);
|
const cleanHeaders: Headers = sanitize(headers);
|
||||||
const mode: Mode = getMode(cleanHeaders);
|
const mode: Mode = getMode(cleanHeaders);
|
||||||
const version = getVersion(mode, cleanHeaders, body);
|
const version = getVersion(mode, cleanHeaders, body);
|
||||||
|
@ -103,7 +106,7 @@ function getMode(headers: Headers): Mode {
|
||||||
function getVersion(
|
function getVersion(
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
headers: Headers,
|
headers: Headers,
|
||||||
body: string | Record<string, unknown> | CloudEventV03 | CloudEventV1,
|
body: string | Record<string, unknown> | CloudEventV03 | CloudEventV1 | undefined | null,
|
||||||
) {
|
) {
|
||||||
if (mode === Mode.BINARY) {
|
if (mode === Mode.BINARY) {
|
||||||
// Check the headers for the version
|
// Check the headers for the version
|
||||||
|
@ -113,7 +116,7 @@ function getVersion(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// structured mode - the version is in the body
|
// structured mode - the version is in the body
|
||||||
return typeof body === "string" ? JSON.parse(body).specversion : body.specversion;
|
return typeof body === "string" ? JSON.parse(body).specversion : (body as CloudEvent).specversion;
|
||||||
}
|
}
|
||||||
return Version.V1;
|
return Version.V1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,30 @@ describe("HTTP Transport Binding Receiver for CloudEvents", () => {
|
||||||
expect((event.data as Record<string, string>).lunch).to.equal("sushi");
|
expect((event.data as Record<string, string>).lunch).to.equal("sushi");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Accepts binary events when the data property is undefined", () => {
|
||||||
|
const binaryHeaders = {
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"ce-specversion": specversion,
|
||||||
|
"ce-id": id,
|
||||||
|
"ce-type": type,
|
||||||
|
"ce-source": source,
|
||||||
|
};
|
||||||
|
const event = receiver.accept(binaryHeaders, undefined);
|
||||||
|
expect(event.data).to.be.undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Accepts binary events when the data property is null", () => {
|
||||||
|
const binaryHeaders = {
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"ce-specversion": specversion,
|
||||||
|
"ce-id": id,
|
||||||
|
"ce-type": type,
|
||||||
|
"ce-source": source,
|
||||||
|
};
|
||||||
|
const event = receiver.accept(binaryHeaders, null);
|
||||||
|
expect(event.data).to.be.undefined;
|
||||||
|
});
|
||||||
|
|
||||||
it("Converts the JSON body of a structured event to an Object", () => {
|
it("Converts the JSON body of a structured event to an Object", () => {
|
||||||
const payload = {
|
const payload = {
|
||||||
id,
|
id,
|
||||||
|
|
|
@ -9,18 +9,6 @@ const receiver = new BinaryHTTPReceiver(Version.V03);
|
||||||
|
|
||||||
describe("HTTP Transport Binding Binary Receiver for CloudEvents v0.3", () => {
|
describe("HTTP Transport Binding Binary Receiver for CloudEvents v0.3", () => {
|
||||||
describe("Check", () => {
|
describe("Check", () => {
|
||||||
it("Throw error when payload arg is null or undefined", () => {
|
|
||||||
// setup
|
|
||||||
const payload = undefined;
|
|
||||||
const attributes = {};
|
|
||||||
|
|
||||||
// act and assert
|
|
||||||
expect(receiver.parse.bind(receiver, (payload as unknown) as string, attributes)).to.throw(
|
|
||||||
ValidationError,
|
|
||||||
"payload is null or undefined",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Throw error when attributes arg is null or undefined", () => {
|
it("Throw error when attributes arg is null or undefined", () => {
|
||||||
// setup
|
// setup
|
||||||
const payload = {};
|
const payload = {};
|
||||||
|
|
|
@ -10,18 +10,6 @@ const receiver = new BinaryHTTPReceiver(Version.V1);
|
||||||
|
|
||||||
describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => {
|
describe("HTTP Transport Binding Binary Receiver for CloudEvents v1.0", () => {
|
||||||
describe("Check", () => {
|
describe("Check", () => {
|
||||||
it("Throw error when payload arg is null or undefined", () => {
|
|
||||||
// setup
|
|
||||||
const payload = null;
|
|
||||||
const attributes = {};
|
|
||||||
|
|
||||||
// act and assert
|
|
||||||
expect(receiver.parse.bind(receiver, (payload as unknown) as string, attributes)).to.throw(
|
|
||||||
ValidationError,
|
|
||||||
"payload is null or undefined",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Throw error when attributes arg is null or undefined", () => {
|
it("Throw error when attributes arg is null or undefined", () => {
|
||||||
// setup
|
// setup
|
||||||
const payload = {};
|
const payload = {};
|
||||||
|
|
Loading…
Reference in New Issue