@@ -8,6 +8,13 @@ import { Message, Headers, Binding } from "..";
8
8
import { headersFor , HEADER_MAP , KAFKA_CE_HEADERS } from "./headers" ;
9
9
import { sanitize } from "../http/headers" ;
10
10
11
+ // Export the binding implementation and message interface
12
+ export {
13
+ Kafka ,
14
+ KafkaMessage ,
15
+ KafkaEvent
16
+ } ;
17
+
11
18
/**
12
19
* Bindings for Kafka transport
13
20
* @implements {@linkcode Binding }
@@ -19,45 +26,39 @@ import { sanitize } from "../http/headers";
19
26
isEvent : isKafkaEvent ,
20
27
} ;
21
28
29
+ type Key = string | Buffer ;
30
+
22
31
/**
23
32
* Extends the base Message type to include
24
33
* Kafka-specific fields
25
34
*/
26
35
interface KafkaMessage < T = string > extends Message {
27
- key : string | Buffer
36
+ key : Key
28
37
value : T | string | Buffer | unknown
29
38
timestamp ?: string
30
39
}
31
40
32
41
/**
33
- * Extends the base CloudEvent type to include
34
- * the Kafka-specific `partitionkey` field, which
35
- * is explicitly mapped to `KafkaMessage#key`.
42
+ * Extends the base CloudEventV1 interface to include a `partitionkey` field
43
+ * which is explicitly mapped to KafkaMessage#key
36
44
*/
37
45
interface KafkaEvent < T > extends CloudEventV1 < T > {
38
46
/**
39
47
* Maps to KafkaMessage#key per
40
48
* https://github.com/cloudevents/spec/blob/v1.0.1/kafka-protocol-binding.md#31-key-mapping
41
49
*/
42
- partitionkey : string | Buffer
50
+ partitionkey : Key
43
51
}
44
52
45
- // Export the binding implementation and message interface
46
- export {
47
- Kafka ,
48
- KafkaMessage ,
49
- KafkaEvent
50
- } ;
51
-
52
53
/**
53
54
* Serialize a CloudEvent for Kafka in binary mode
54
55
* @implements {Serializer}
55
56
* @see https://github.com/cloudevents/spec/blob/v1.0.1/kafka-protocol-binding.md#32-binary-content-mode
56
57
*
57
- * @param {CloudEvent <T> } event The event to serialize
58
+ * @param {KafkaEvent <T> } event The event to serialize
58
59
* @returns {KafkaMessage<T> } a KafkaMessage instance
59
60
*/
60
- function toBinaryKafkaMessage < T > ( event : CloudEvent < T > ) : KafkaMessage < T > {
61
+ function toBinaryKafkaMessage < T > ( event : CloudEventV1 < T > ) : KafkaMessage < T > {
61
62
// 3.2.1. Content Type
62
63
// For the binary mode, the header content-type property MUST be mapped directly
63
64
// to the CloudEvents datacontenttype attribute.
@@ -67,7 +68,7 @@ function toBinaryKafkaMessage<T>(event: CloudEvent<T>): KafkaMessage<T> {
67
68
} ;
68
69
return {
69
70
headers,
70
- key : createKey ( event ) ,
71
+ key : event . partitionkey as Key ,
71
72
value : event . data ,
72
73
body : event . data ,
73
74
timestamp : timestamp ( event . time )
@@ -82,14 +83,17 @@ function toBinaryKafkaMessage<T>(event: CloudEvent<T>): KafkaMessage<T> {
82
83
* @param {CloudEvent<T> } event the CloudEvent to be serialized
83
84
* @returns {KafkaMessage<T> } a KafkaMessage instance
84
85
*/
85
- function toStructuredKafkaMessage < T > ( event : CloudEvent < T > ) : KafkaMessage < T > {
86
- if ( event . data_base64 ) {
86
+ function toStructuredKafkaMessage < T > ( event : CloudEventV1 < T > ) : KafkaMessage < T > {
87
+ if ( event . data_base64 && event instanceof CloudEvent ) {
87
88
// The event's data is binary - delete it
88
89
event = event . cloneWith ( { data : undefined } ) ;
89
90
}
90
91
const value = event . toString ( ) ;
91
92
return {
92
- key : createKey ( event ) ,
93
+ // All events may not have a partitionkey set, but if they do,
94
+ // use it for the KafkaMessage#key per
95
+ // https://github.com/cloudevents/spec/blob/v1.0.1/kafka-protocol-binding.md#31-key-mapping
96
+ key : event . partitionkey as Key ,
93
97
value,
94
98
headers : {
95
99
[ CONSTANTS . HEADER_CONTENT_TYPE ] : CONSTANTS . DEFAULT_CE_CONTENT_TYPE ,
@@ -104,27 +108,28 @@ function toBinaryKafkaMessage<T>(event: CloudEvent<T>): KafkaMessage<T> {
104
108
* @implements {Deserializer}
105
109
*
106
110
* @param {Message } message the incoming message
107
- * @return {CloudEvent } A new {CloudEvent } instance
111
+ * @return {KafkaEvent } A new {KafkaEvent } instance
108
112
*/
109
- function deserializeKafkaMessage < T > ( message : Message ) : CloudEventV1 < T > | CloudEvent < T > | CloudEvent < T > [ ] {
113
+ function deserializeKafkaMessage < T > ( message : Message ) : CloudEvent < T > | CloudEvent < T > [ ] {
110
114
if ( ! isKafkaEvent ( message ) ) {
111
115
throw new ValidationError ( "No CloudEvent detected" ) ;
112
116
}
113
- if ( ! ( message as KafkaMessage < T > ) . value ) {
117
+ const m = message as KafkaMessage < T > ;
118
+ if ( ! m . value ) {
114
119
throw new ValidationError ( "Value is null or undefined" ) ;
115
120
}
116
- if ( ! message . headers ) {
121
+ if ( ! m . headers ) {
117
122
throw new ValidationError ( "Headers are null or undefined" ) ;
118
123
}
119
- const cleanHeaders : Headers = sanitize ( message . headers ) ;
124
+ const cleanHeaders : Headers = sanitize ( m . headers ) ;
120
125
const mode : Mode = getMode ( cleanHeaders ) ;
121
126
switch ( mode ) {
122
127
case Mode . BINARY :
123
- return parseBinary ( message as KafkaMessage < T > ) as CloudEventV1 < T > ;
128
+ return parseBinary ( m ) ;
124
129
case Mode . STRUCTURED :
125
- return parseStructured ( message as KafkaMessage < T > ) ;
130
+ return parseStructured ( m ) ;
126
131
case Mode . BATCH :
127
- return parseBatched ( message as KafkaMessage < T > ) ;
132
+ return parseBatched ( m ) ;
128
133
default :
129
134
throw new ValidationError ( "Unknown Message mode" ) ;
130
135
}
@@ -137,22 +142,13 @@ function deserializeKafkaMessage<T>(message: Message): CloudEventV1<T> | CloudEv
137
142
* @param {Message } message an incoming Message object
138
143
* @returns {boolean } true if this Message is a CloudEvent
139
144
*/
140
- function isKafkaEvent < T > ( message : Message < T > ) : boolean {
145
+ function isKafkaEvent ( message : Message ) : boolean {
141
146
const headers = sanitize ( message . headers ) ;
142
147
return ! ! headers [ KAFKA_CE_HEADERS . ID ] || // A binary mode event
143
148
headers [ CONSTANTS . HEADER_CONTENT_TYPE ] ?. startsWith ( CONSTANTS . MIME_CE ) as boolean || // A structured mode event
144
149
headers [ CONSTANTS . HEADER_CONTENT_TYPE ] ?. startsWith ( CONSTANTS . MIME_CE_BATCH ) as boolean ; // A batch of events
145
150
}
146
151
147
- /**
148
- * Creates a Kafka key for the event provided
149
- * @param {CloudEvent } e an event
150
- * @returns {string } the Kafka key
151
- */
152
- function createKey < T > ( e : CloudEvent < T > ) : string {
153
- return `${ e . source } /${ e . id } ` ;
154
- }
155
-
156
152
/**
157
153
* Determines what content mode a Kafka message is in given the provided headers
158
154
* @param {Headers } headers the headers
@@ -175,7 +171,7 @@ function getMode(headers: Headers): Mode {
175
171
* @param {KafkaMessage } message the message
176
172
* @returns {CloudEvent<T> } a CloudEvent<T>
177
173
*/
178
- function parseBinary < T > ( message : KafkaMessage < T > ) : KafkaEvent < T > {
174
+ function parseBinary < T > ( message : KafkaMessage < T > ) : CloudEvent < T > {
179
175
const eventObj : { [ key : string ] : unknown } = { } ;
180
176
const headers = { ...message . headers } ;
181
177
@@ -196,7 +192,7 @@ function parseBinary<T>(message: KafkaMessage<T>): KafkaEvent<T> {
196
192
}
197
193
}
198
194
199
- return new KafkaEvent ( {
195
+ return new CloudEvent ( {
200
196
...eventObj ,
201
197
data : extractBinaryData ( message ) ,
202
198
partitionkey : message . key ,
@@ -205,8 +201,8 @@ function parseBinary<T>(message: KafkaMessage<T>): KafkaEvent<T> {
205
201
206
202
/**
207
203
* Parses a structured kafka CE message and returns a CloudEvent
208
- * @param {KafkaMessage } message the message
209
- * @returns {CloudEvent<T> } a CloudEvent <T>
204
+ * @param {KafkaMessage<T> } message the message
205
+ * @returns {CloudEvent<T> } a KafkaEvent <T>
210
206
*/
211
207
function parseStructured < T > ( message : KafkaMessage < T > ) : CloudEvent < T > {
212
208
// Although the format of a structured encoded event could be something
@@ -215,13 +211,17 @@ function parseStructured<T>(message: KafkaMessage<T>): CloudEvent<T> {
215
211
if ( ! message . headers [ CONSTANTS . HEADER_CONTENT_TYPE ] ?. startsWith ( CONSTANTS . MIME_CE_JSON ) ) {
216
212
throw new ValidationError ( `Unsupported event encoding ${ message . headers [ CONSTANTS . HEADER_CONTENT_TYPE ] } ` ) ;
217
213
}
218
- return new CloudEvent ( JSON . parse ( message . value as string ) , false ) ;
214
+
215
+ return new CloudEvent ( {
216
+ ...JSON . parse ( message . value as string ) ,
217
+ partitionkey : message . key ,
218
+ } , false ) ;
219
219
}
220
220
221
221
/**
222
222
* Parses a batch kafka CE message and returns a CloudEvent[]
223
- * @param {KafkaMessage } message the message
224
- * @returns {CloudEvent<T> } a CloudEvent <T>[]
223
+ * @param {KafkaMessage<T> } message the message
224
+ * @returns {CloudEvent<T>[] } an array of KafkaEvent <T>
225
225
*/
226
226
function parseBatched < T > ( message : KafkaMessage < T > ) : CloudEvent < T > [ ] {
227
227
// Although the format of batch encoded events could be something
@@ -231,7 +231,7 @@ function parseBatched<T>(message: KafkaMessage<T>): CloudEvent<T>[] {
231
231
throw new ValidationError ( `Unsupported event encoding ${ message . headers [ CONSTANTS . HEADER_CONTENT_TYPE ] } ` ) ;
232
232
}
233
233
const events = JSON . parse ( message . value as string ) as Record < string , unknown > [ ] ;
234
- return events . map ( ( e ) => new CloudEvent ( e , false ) ) ;
234
+ return events . map ( ( e ) => new CloudEvent ( { ... e , partitionkey : message . key } , false ) ) ;
235
235
}
236
236
237
237
/**
0 commit comments