@@ -55,6 +55,90 @@ pub struct KafkaRecord {
5555 pub other : serde_json:: Map < String , Value > ,
5656}
5757
58+ /// `KafkaEventResponse` is the outer structure to report batch item failures for `KafkaEvent`.
59+ #[ non_exhaustive]
60+ #[ cfg_attr( feature = "builders" , derive( Builder ) ) ]
61+ #[ derive( Debug , Default , Clone , Eq , PartialEq , Deserialize , Serialize ) ]
62+ #[ serde( rename_all = "camelCase" ) ]
63+ pub struct KafkaEventResponse {
64+ pub batch_item_failures : Vec < KafkaBatchItemFailure > ,
65+ /// Catchall to catch any additional fields that were present but not explicitly defined by this struct.
66+ /// Enabled with Cargo feature `catch-all-fields`.
67+ /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored.
68+ #[ cfg( feature = "catch-all-fields" ) ]
69+ #[ cfg_attr( docsrs, doc( cfg( feature = "catch-all-fields" ) ) ) ]
70+ #[ serde( flatten) ]
71+ #[ cfg_attr( feature = "builders" , builder( default ) ) ]
72+ pub other : serde_json:: Map < String , Value > ,
73+ }
74+
75+ impl KafkaEventResponse {
76+ /// Add a failed Kafka item identifier to the batch response.
77+ ///
78+ /// Lambda retries the identified records when `ReportBatchItemFailures` is enabled on the
79+ /// Kafka event source mapping. Returning an error from the handler still retries the whole
80+ /// batch.
81+ pub fn add_failure ( & mut self , item_identifier : KafkaItemIdentifier ) {
82+ self . batch_item_failures . push ( KafkaBatchItemFailure {
83+ item_identifier,
84+ ..Default :: default ( )
85+ } ) ;
86+ }
87+
88+ /// Set all failed Kafka item identifiers in the batch response.
89+ ///
90+ /// This replaces any previously registered failures.
91+ pub fn set_failures < I > ( & mut self , item_identifiers : I )
92+ where
93+ I : IntoIterator < Item = KafkaItemIdentifier > ,
94+ {
95+ self . batch_item_failures = item_identifiers
96+ . into_iter ( )
97+ . map ( |item_identifier| KafkaBatchItemFailure {
98+ item_identifier,
99+ ..Default :: default ( )
100+ } )
101+ . collect ( ) ;
102+ }
103+ }
104+
105+ /// `KafkaBatchItemFailure` is an individual Kafka record which failed processing.
106+ #[ non_exhaustive]
107+ #[ cfg_attr( feature = "builders" , derive( Builder ) ) ]
108+ #[ derive( Debug , Default , Clone , Eq , PartialEq , Deserialize , Serialize ) ]
109+ #[ serde( rename_all = "camelCase" ) ]
110+ pub struct KafkaBatchItemFailure {
111+ pub item_identifier : KafkaItemIdentifier ,
112+ /// Catchall to catch any additional fields that were present but not explicitly defined by this struct.
113+ /// Enabled with Cargo feature `catch-all-fields`.
114+ /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored.
115+ #[ cfg( feature = "catch-all-fields" ) ]
116+ #[ cfg_attr( docsrs, doc( cfg( feature = "catch-all-fields" ) ) ) ]
117+ #[ serde( flatten) ]
118+ #[ cfg_attr( feature = "builders" , builder( default ) ) ]
119+ pub other : serde_json:: Map < String , Value > ,
120+ }
121+
122+ /// `KafkaItemIdentifier` identifies a Kafka record for a partial batch response.
123+ #[ non_exhaustive]
124+ #[ cfg_attr( feature = "builders" , derive( Builder ) ) ]
125+ #[ derive( Debug , Default , Clone , Eq , PartialEq , Deserialize , Serialize ) ]
126+ #[ serde( rename_all = "camelCase" ) ]
127+ pub struct KafkaItemIdentifier {
128+ /// The topic-partition key from the Kafka event's `records` map.
129+ pub partition : String ,
130+ /// The Kafka record offset.
131+ pub offset : i64 ,
132+ /// Catchall to catch any additional fields that were present but not explicitly defined by this struct.
133+ /// Enabled with Cargo feature `catch-all-fields`.
134+ /// If `catch-all-fields` is disabled, any additional fields that are present will be ignored.
135+ #[ cfg( feature = "catch-all-fields" ) ]
136+ #[ cfg_attr( docsrs, doc( cfg( feature = "catch-all-fields" ) ) ) ]
137+ #[ serde( flatten) ]
138+ #[ cfg_attr( feature = "builders" , builder( default ) ) ]
139+ pub other : serde_json:: Map < String , Value > ,
140+ }
141+
58142#[ cfg( test) ]
59143mod test {
60144 use super :: * ;
@@ -68,4 +152,54 @@ mod test {
68152 let reparsed: KafkaEvent = serde_json:: from_slice ( output. as_bytes ( ) ) . unwrap ( ) ;
69153 assert_eq ! ( parsed, reparsed) ;
70154 }
155+
156+ #[ test]
157+ #[ cfg( feature = "kafka" ) ]
158+ fn kafka_event_response_serializes_item_identifiers ( ) {
159+ let mut response = KafkaEventResponse :: default ( ) ;
160+ response. add_failure ( KafkaItemIdentifier {
161+ partition : String :: from ( "some.topic-3" ) ,
162+ offset : 42 ,
163+ ..Default :: default ( )
164+ } ) ;
165+
166+ let serialized = serde_json:: to_value ( response) . unwrap ( ) ;
167+
168+ assert_eq ! (
169+ serialized,
170+ serde_json:: json!( {
171+ "batchItemFailures" : [ {
172+ "itemIdentifier" : {
173+ "partition" : "some.topic-3" ,
174+ "offset" : 42 ,
175+ }
176+ } ]
177+ } )
178+ ) ;
179+ }
180+
181+ #[ test]
182+ #[ cfg( feature = "kafka" ) ]
183+ fn kafka_event_response_sets_failures ( ) {
184+ let mut response = KafkaEventResponse :: default ( ) ;
185+ response. set_failures ( [
186+ KafkaItemIdentifier {
187+ partition : String :: from ( "some.topic-3" ) ,
188+ offset : 42 ,
189+ ..Default :: default ( )
190+ } ,
191+ KafkaItemIdentifier {
192+ partition : String :: from ( "some.topic-4" ) ,
193+ offset : 43 ,
194+ ..Default :: default ( )
195+ } ,
196+ ] ) ;
197+
198+ assert_eq ! ( response. batch_item_failures. len( ) , 2 ) ;
199+ assert_eq ! (
200+ response. batch_item_failures[ 0 ] . item_identifier. partition,
201+ "some.topic-3"
202+ ) ;
203+ assert_eq ! ( response. batch_item_failures[ 1 ] . item_identifier. offset, 43 ) ;
204+ }
71205}
0 commit comments