From 48f971e6d89ef2b9d8494ed6384ab25705d49f9b Mon Sep 17 00:00:00 2001 From: Lance Ball Date: Thu, 1 Oct 2020 11:14:28 -0400 Subject: [PATCH 1/2] fix: extend Node.js IncomingHttpHeaders in our Headers type Fixes: https://github.com/cloudevents/sdk-javascript/issues/340 Signed-off-by: Lance Ball --- src/message/http/index.ts | 4 ++-- src/message/index.ts | 5 +++-- src/parsers.ts | 2 +- test/integration/emitter_factory_test.ts | 2 +- test/integration/message_test.ts | 20 ++++++++++++++++++++ 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/message/http/index.ts b/src/message/http/index.ts index 97c005b6..5903c7f6 100644 --- a/src/message/http/index.ts +++ b/src/message/http/index.ts @@ -71,7 +71,7 @@ export function deserialize(message: Message): CloudEvent { * @returns {Mode} the transport mode */ function getMode(headers: Headers): Mode { - const contentType = headers[CONSTANTS.HEADER_CONTENT_TYPE]; + const contentType = headers[CONSTANTS.HEADER_CONTENT_TYPE] as string; if (contentType && contentType.startsWith(CONSTANTS.MIME_CE)) { return Mode.STRUCTURED; } @@ -198,7 +198,7 @@ function parseStructured(message: Message, version: Version): CloudEvent { // Clone and low case all headers names const sanitizedHeaders = sanitize(headers); - const contentType = sanitizedHeaders[CONSTANTS.HEADER_CONTENT_TYPE]; + const contentType = sanitizedHeaders[CONSTANTS.HEADER_CONTENT_TYPE] as string; const parser: Parser = contentType ? parserByContentType[contentType] : new JSONParser(); if (!parser) throw new ValidationError(`invalid content type ${sanitizedHeaders[CONSTANTS.HEADER_CONTENT_TYPE]}`); const incoming = { ...(parser.parse(payload) as Record) }; diff --git a/src/message/index.ts b/src/message/index.ts index 14ed270c..5b4ac9a6 100644 --- a/src/message/index.ts +++ b/src/message/index.ts @@ -1,3 +1,4 @@ +import { IncomingHttpHeaders } from "http"; import { CloudEvent } from ".."; import { binary, deserialize, structured, isEvent } from "./http"; import { headersFor } from "./http/headers"; @@ -18,8 +19,8 @@ export interface Binding { * Headers is an interface representing transport-agnostic headers as * key/value string pairs */ -export interface Headers { - [key: string]: string; +export interface Headers extends IncomingHttpHeaders { + [key: string]: string | string[] | undefined; } /** diff --git a/src/parsers.ts b/src/parsers.ts index bbfe128c..37890e00 100644 --- a/src/parsers.ts +++ b/src/parsers.ts @@ -2,7 +2,7 @@ import CONSTANTS from "./constants"; import { isString, isDefinedOrThrow, isStringOrObjectOrThrow, ValidationError } from "./event/validation"; export abstract class Parser { - abstract parse(payload: Record | string): unknown; + abstract parse(payload: Record | string | string[] | undefined): unknown; } export class JSONParser implements Parser { diff --git a/test/integration/emitter_factory_test.ts b/test/integration/emitter_factory_test.ts index 4f6cba35..43bb9cfe 100644 --- a/test/integration/emitter_factory_test.ts +++ b/test/integration/emitter_factory_test.ts @@ -48,7 +48,7 @@ function superagentEmitter(message: Message, options?: Options): Promise { }).to.throw; }); + it("Can be created with Node's IncomingHttpHeaders", () => { + const headers: IncomingHttpHeaders = { + "content-type": CONSTANTS.DEFAULT_CE_CONTENT_TYPE, + }; + const body = JSON.stringify({ + id, + type, + source, + specversion: Version.V1, + data: { lunch: "tacos" }, + }); + const message: Message = { + headers, + body, + }; + const event = HTTP.toEvent(message); + expect(event.data).to.deep.equal({ lunch: "tacos" }); + }); + describe("Specification version V1", () => { const fixture: CloudEvent = new CloudEvent({ specversion: Version.V1, From 63c8440d5b8243cbef470db9a831f831db762bec Mon Sep 17 00:00:00 2001 From: Lance Ball Date: Tue, 6 Oct 2020 15:43:19 -0400 Subject: [PATCH 2/2] fixup: incorporate PR comments Signed-off-by: Lance Ball --- src/constants.ts | 2 +- src/event/cloudevent.ts | 1 - src/message/http/index.ts | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index 449fe06a..30d1dbe5 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -48,6 +48,6 @@ const CONSTANTS = Object.freeze({ DATA_SCHEMA: "dataschema", DATA_BASE64: "data_base64", }, -}); +} as const); export default CONSTANTS; diff --git a/src/event/cloudevent.ts b/src/event/cloudevent.ts index c9ade6f1..a8231cc1 100644 --- a/src/event/cloudevent.ts +++ b/src/event/cloudevent.ts @@ -175,7 +175,6 @@ export class CloudEvent implements CloudEventV1, CloudEventV03 { try { return validateCloudEvent(this); } catch (e) { - console.error(e.errors); if (e instanceof ValidationError) { throw e; } else { diff --git a/src/message/http/index.ts b/src/message/http/index.ts index 5903c7f6..97c005b6 100644 --- a/src/message/http/index.ts +++ b/src/message/http/index.ts @@ -71,7 +71,7 @@ export function deserialize(message: Message): CloudEvent { * @returns {Mode} the transport mode */ function getMode(headers: Headers): Mode { - const contentType = headers[CONSTANTS.HEADER_CONTENT_TYPE] as string; + const contentType = headers[CONSTANTS.HEADER_CONTENT_TYPE]; if (contentType && contentType.startsWith(CONSTANTS.MIME_CE)) { return Mode.STRUCTURED; } @@ -198,7 +198,7 @@ function parseStructured(message: Message, version: Version): CloudEvent { // Clone and low case all headers names const sanitizedHeaders = sanitize(headers); - const contentType = sanitizedHeaders[CONSTANTS.HEADER_CONTENT_TYPE] as string; + const contentType = sanitizedHeaders[CONSTANTS.HEADER_CONTENT_TYPE]; const parser: Parser = contentType ? parserByContentType[contentType] : new JSONParser(); if (!parser) throw new ValidationError(`invalid content type ${sanitizedHeaders[CONSTANTS.HEADER_CONTENT_TYPE]}`); const incoming = { ...(parser.parse(payload) as Record) };