1
- import type { ZodSchema , z } from 'zod' ;
1
+ import { ZodError , type ZodIssue , type ZodSchema , type z } from 'zod' ;
2
2
import { ParseError } from '../errors.js' ;
3
3
import { CloudWatchLogsSchema } from '../schemas/index.js' ;
4
4
import type { ParsedResult } from '../types/index.js' ;
5
- import { Envelope , envelopeDiscriminator } from './envelope.js' ;
5
+ import { envelopeDiscriminator } from './envelope.js' ;
6
6
7
7
/**
8
- * CloudWatch Envelope to extract a List of log records.
9
- *
10
- * The record's body parameter is a string (after being base64 decoded and gzipped),
11
- * though it can also be a JSON encoded string.
12
- * Regardless of its type it'll be parsed into a BaseModel object.
13
- *
14
- * Note: The record will be parsed the same way so if model is str
8
+ * CloudWatch Envelope to extract messages from the `awslogs.data.logEvents` key.
15
9
*/
16
10
export const CloudWatchEnvelope = {
17
11
/**
@@ -22,45 +16,112 @@ export const CloudWatchEnvelope = {
22
16
parse < T extends ZodSchema > ( data : unknown , schema : T ) : z . infer < T > [ ] {
23
17
const parsedEnvelope = CloudWatchLogsSchema . parse ( data ) ;
24
18
25
- return parsedEnvelope . awslogs . data . logEvents . map ( ( record ) => {
26
- return Envelope . parse ( record . message , schema ) ;
19
+ return parsedEnvelope . awslogs . data . logEvents . map ( ( record , index ) => {
20
+ try {
21
+ return schema . parse ( record . message ) ;
22
+ } catch ( error ) {
23
+ throw new ParseError (
24
+ `Failed to parse CloudWatch log event at index ${ index } ` ,
25
+ {
26
+ cause : new ZodError (
27
+ ( error as ZodError ) . issues . map ( ( issue ) => ( {
28
+ ...issue ,
29
+ path : [
30
+ 'awslogs' ,
31
+ 'data' ,
32
+ 'logEvents' ,
33
+ index ,
34
+ 'message' ,
35
+ ...issue . path ,
36
+ ] ,
37
+ } ) )
38
+ ) ,
39
+ }
40
+ ) ;
41
+ }
27
42
} ) ;
28
43
} ,
29
44
30
45
safeParse < T extends ZodSchema > (
31
46
data : unknown ,
32
47
schema : T
33
48
) : ParsedResult < unknown , z . infer < T > [ ] > {
34
- const parsedEnvelope = CloudWatchLogsSchema . safeParse ( data ) ;
49
+ let parsedEnvelope : ParsedResult < unknown , z . infer < T > > ;
50
+ try {
51
+ parsedEnvelope = CloudWatchLogsSchema . safeParse ( data ) ;
52
+ } catch ( error ) {
53
+ parsedEnvelope = {
54
+ success : false ,
55
+ error : error as Error ,
56
+ } ;
57
+ }
35
58
36
59
if ( ! parsedEnvelope . success ) {
37
60
return {
38
61
success : false ,
39
- error : new ParseError ( 'Failed to parse CloudWatch envelope' , {
62
+ error : new ParseError ( 'Failed to parse CloudWatch Log envelope' , {
40
63
cause : parsedEnvelope . error ,
41
64
} ) ,
42
65
originalEvent : data ,
43
66
} ;
44
67
}
45
- const parsedLogEvents : z . infer < T > [ ] = [ ] ;
46
68
47
- for ( const record of parsedEnvelope . data . awslogs . data . logEvents ) {
48
- const parsedMessage = Envelope . safeParse ( record . message , schema ) ;
49
- if ( ! parsedMessage . success ) {
50
- return {
51
- success : false ,
52
- error : new ParseError ( 'Failed to parse CloudWatch log event' , {
53
- cause : parsedMessage . error ,
54
- } ) ,
55
- originalEvent : data ,
56
- } ;
69
+ const result = parsedEnvelope . data . awslogs . data . logEvents . reduce (
70
+ (
71
+ acc : {
72
+ success : boolean ;
73
+ messages : z . infer < T > ;
74
+ errors : { [ key : number ] : { issues : ZodIssue [ ] } } ;
75
+ } ,
76
+ record : { message : string } ,
77
+ index : number
78
+ ) => {
79
+ const result = schema . safeParse ( record . message ) ;
80
+ if ( ! result . success ) {
81
+ const issues = result . error . issues . map ( ( issue ) => ( {
82
+ ...issue ,
83
+ path : [
84
+ 'awslogs' ,
85
+ 'data' ,
86
+ 'logEvents' ,
87
+ index ,
88
+ 'message' ,
89
+ ...issue . path ,
90
+ ] ,
91
+ } ) ) ;
92
+
93
+ acc . success = false ;
94
+ acc . errors [ index ] = { issues } ;
95
+ return acc ;
96
+ }
97
+
98
+ acc . messages . push ( result . data ) ;
99
+ return acc ;
100
+ } ,
101
+ {
102
+ success : true ,
103
+ messages : [ ] ,
104
+ errors : { } ,
57
105
}
58
- parsedLogEvents . push ( parsedMessage . data ) ;
106
+ ) ;
107
+
108
+ if ( result . success ) {
109
+ return { success : true , data : result . messages } ;
59
110
}
60
111
112
+ const errorMessage =
113
+ Object . keys ( result . errors ) . length > 1
114
+ ? `Failed to parse CloudWatch Log messages at indexes ${ Object . keys ( result . errors ) . join ( ', ' ) } `
115
+ : `Failed to parse CloudWatch Log message at index ${ Object . keys ( result . errors ) [ 0 ] } ` ;
116
+ const errorCause = new ZodError (
117
+ // @ts -expect-error - issues are assigned because success is false
118
+ Object . values ( result . errors ) . flatMap ( ( error ) => error . issues )
119
+ ) ;
120
+
61
121
return {
62
- success : true ,
63
- data : parsedLogEvents ,
122
+ success : false ,
123
+ error : new ParseError ( errorMessage , { cause : errorCause } ) ,
124
+ originalEvent : data ,
64
125
} ;
65
126
} ,
66
127
} ;
0 commit comments