1+ import type { StandardSchemaV1 } from '@standard-schema/spec' ;
2+ import type { StreamRecord } from 'aws-lambda' ;
13import { BasePartialBatchProcessor } from './BasePartialBatchProcessor.js' ;
4+ import { EventType , SchemaVendor } from './constants.js' ;
25import { BatchProcessingError } from './errors.js' ;
3- import type { BaseRecord , FailureResponse , SuccessResponse } from './types.js' ;
6+ import type {
7+ BaseRecord ,
8+ EventSourceDataClassTypes ,
9+ FailureResponse ,
10+ SuccessResponse ,
11+ } from './types.js' ;
412
513/**
614 * Process records in a batch asynchronously and handle partial failure cases.
@@ -79,7 +87,7 @@ import type { BaseRecord, FailureResponse, SuccessResponse } from './types.js';
7987 * });
8088 * ```
8189 *
82- * @param eventType The type of event to process (SQS, Kinesis, DynamoDB)
90+ * @param eventType - The type of event to process (SQS, Kinesis, DynamoDB)
8391 */
8492class BatchProcessor extends BasePartialBatchProcessor {
8593 /**
@@ -94,13 +102,17 @@ class BatchProcessor extends BasePartialBatchProcessor {
94102 * If the handler function completes successfully, the method returns a success response.
95103 * Otherwise, it returns a failure response with the error that occurred during processing.
96104 *
97- * @param record The record to be processed
105+ * @param record - The record to be processed
98106 */
99107 public async processRecord (
100108 record : BaseRecord
101109 ) : Promise < SuccessResponse | FailureResponse > {
102110 try {
103- const data = this . toBatchType ( record , this . eventType ) ;
111+ const recordToProcess =
112+ this . schema == null
113+ ? record
114+ : await this . #parseRecord( record , this . eventType , this . schema ) ;
115+ const data = this . toBatchType ( recordToProcess , this . eventType ) ;
104116 const result = await this . handler ( data , this . options ?. context ) ;
105117
106118 return this . successHandler ( record , result ) ;
@@ -112,7 +124,7 @@ class BatchProcessor extends BasePartialBatchProcessor {
112124 /**
113125 * @throws {BatchProcessingError } This method is not implemented for synchronous processing.
114126 *
115- * @param _record The record to be processed
127+ * @param _record - The record to be processed
116128 */
117129 public processRecordSync (
118130 _record : BaseRecord
@@ -121,6 +133,163 @@ class BatchProcessor extends BasePartialBatchProcessor {
121133 'Not implemented. Use asyncProcess() instead.'
122134 ) ;
123135 }
136+
137+ /**
138+ * Extend the schema according to the event type passed.
139+ *
140+ * If useTransformers is true, extend using opinionated transformers.
141+ * Otherwise, extend without any transformers.
142+ *
143+ * @param eventType - The type of event to process (SQS, Kinesis, DynamoDB)
144+ * @param schema - The StandardSchema to be used for parsing
145+ * @param useTransformers - Whether to use transformers for parsing
146+ */
147+ async #createExtendedSchema( options : {
148+ eventType : keyof typeof EventType ;
149+ schema : StandardSchemaV1 ;
150+ useTransformers : boolean ;
151+ } ) {
152+ const { eventType, schema, useTransformers } = options ;
153+ switch ( eventType ) {
154+ case EventType . SQS : {
155+ if ( useTransformers ) {
156+ const [ { JSONStringified } , { SqsRecordSchema } ] = await Promise . all ( [
157+ import ( '@aws-lambda-powertools/parser/helpers' ) ,
158+ import ( '@aws-lambda-powertools/parser/schemas/sqs' ) ,
159+ ] ) ;
160+ return SqsRecordSchema . extend ( {
161+ body : JSONStringified ( schema as any ) ,
162+ } ) ;
163+ }
164+ const { SqsRecordSchema } = await import (
165+ '@aws-lambda-powertools/parser/schemas/sqs'
166+ ) ;
167+ return SqsRecordSchema . extend ( { body : schema } ) ;
168+ }
169+
170+ case EventType . KinesisDataStreams : {
171+ if ( useTransformers ) {
172+ const [
173+ { Base64Encoded } ,
174+ { KinesisDataStreamRecord, KinesisDataStreamRecordPayload } ,
175+ ] = await Promise . all ( [
176+ import ( '@aws-lambda-powertools/parser/helpers' ) ,
177+ import ( '@aws-lambda-powertools/parser/schemas/kinesis' ) ,
178+ ] ) ;
179+ return KinesisDataStreamRecord . extend ( {
180+ kinesis : KinesisDataStreamRecordPayload . extend ( {
181+ data : Base64Encoded ( schema as any ) ,
182+ } ) ,
183+ } ) ;
184+ }
185+ const { KinesisDataStreamRecord, KinesisDataStreamRecordPayload } =
186+ await import ( '@aws-lambda-powertools/parser/schemas/kinesis' ) ;
187+ return KinesisDataStreamRecord . extend ( {
188+ kinesis : KinesisDataStreamRecordPayload . extend ( { data : schema } ) ,
189+ } ) ;
190+ }
191+
192+ case EventType . DynamoDBStreams : {
193+ if ( useTransformers ) {
194+ const [
195+ { DynamoDBMarshalled } ,
196+ { DynamoDBStreamRecord, DynamoDBStreamChangeRecordBase } ,
197+ ] = await Promise . all ( [
198+ import ( '@aws-lambda-powertools/parser/helpers/dynamodb' ) ,
199+ import ( '@aws-lambda-powertools/parser/schemas/dynamodb' ) ,
200+ ] ) ;
201+ return DynamoDBStreamRecord . extend ( {
202+ dynamodb : DynamoDBStreamChangeRecordBase . extend ( {
203+ OldImage : DynamoDBMarshalled < StreamRecord [ 'OldImage' ] > (
204+ schema as any
205+ ) . optional ( ) ,
206+ NewImage : DynamoDBMarshalled < StreamRecord [ 'NewImage' ] > (
207+ schema as any
208+ ) . optional ( ) ,
209+ } ) ,
210+ } ) ;
211+ }
212+ const { DynamoDBStreamRecord, DynamoDBStreamChangeRecordBase } =
213+ await import ( '@aws-lambda-powertools/parser/schemas/dynamodb' ) ;
214+ return DynamoDBStreamRecord . extend ( {
215+ dynamodb : DynamoDBStreamChangeRecordBase . extend ( {
216+ OldImage : ( schema as any ) . optional ( ) ,
217+ NewImage : ( schema as any ) . optional ( ) ,
218+ } ) ,
219+ } ) ;
220+ }
221+
222+ default : {
223+ console . warn (
224+ `The event type provided is not supported. Supported events: ${ Object . values ( EventType ) . join ( ',' ) } `
225+ ) ;
226+ throw new Error ( 'Unsupported event type' ) ;
227+ }
228+ }
229+ }
230+
231+ /**
232+ * Parse the record according to the schema and event type passed.
233+ *
234+ * If the passed schema is already an extended schema,
235+ * use the schema directly to parse the record.
236+ *
237+ * Only Zod Schemas are supported for schema extension.
238+ *
239+ * @param record - The record to be parsed
240+ * @param eventType - The type of event to process
241+ * @param schema - The StandardSchema to be used for parsing
242+ */
243+ async #parseRecord(
244+ record : EventSourceDataClassTypes ,
245+ eventType : keyof typeof EventType ,
246+ schema : StandardSchemaV1
247+ ) : Promise < EventSourceDataClassTypes > {
248+ const { parse } = await import ( '@aws-lambda-powertools/parser' ) ;
249+ // Try parsing with the original schema first
250+ const extendedSchemaParsing = parse ( record , undefined , schema , true ) ;
251+ if ( extendedSchemaParsing . success ) {
252+ return extendedSchemaParsing . data as EventSourceDataClassTypes ;
253+ }
254+ // Only proceed with schema extension if it's a Zod schema
255+ if ( schema [ '~standard' ] . vendor !== SchemaVendor . Zod ) {
256+ console . warn (
257+ 'The schema provided is not supported. Only Zod schemas are supported for extension.'
258+ ) ;
259+ throw new Error ( 'Unsupported schema type' ) ;
260+ }
261+ // Handle schema extension based on event type
262+ // Try without transformers first, then with transformers
263+ const schemaWithoutTransformers = await this . #createExtendedSchema( {
264+ eventType,
265+ schema,
266+ useTransformers : false ,
267+ } ) ;
268+ const schemaWithoutTransformersParsing = parse (
269+ record ,
270+ undefined ,
271+ schemaWithoutTransformers ,
272+ true
273+ ) ;
274+ if ( schemaWithoutTransformersParsing . success ) {
275+ return schemaWithoutTransformersParsing . data as EventSourceDataClassTypes ;
276+ }
277+ const schemaWithTransformers = await this . #createExtendedSchema( {
278+ eventType,
279+ schema,
280+ useTransformers : true ,
281+ } ) ;
282+ const schemaWithTransformersParsing = parse (
283+ record ,
284+ undefined ,
285+ schemaWithTransformers ,
286+ true
287+ ) ;
288+ if ( schemaWithTransformersParsing . success ) {
289+ return schemaWithTransformersParsing . data as EventSourceDataClassTypes ;
290+ }
291+ throw new Error ( 'Failed to parse record' ) ;
292+ }
124293}
125294
126295export { BatchProcessor } ;
0 commit comments