Skip to content

Commit 1ff97cb

Browse files
authored
fix(parser): fix cause errors nested structure (#3250)
1 parent 62eb2eb commit 1ff97cb

20 files changed

+142
-79
lines changed

Diff for: packages/parser/src/envelopes/apigw.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { ZodSchema, z } from 'zod';
22
import { ParseError } from '../errors.js';
33
import { APIGatewayProxyEventSchema } from '../schemas/apigw.js';
4-
import type { ParsedResult } from '../types/parser.js';
4+
import type { ParsedResult } from '../types';
55
import { Envelope } from './envelope.js';
66

77
/**
@@ -12,10 +12,7 @@ export const ApiGatewayEnvelope = {
1212
return Envelope.parse(APIGatewayProxyEventSchema.parse(data).body, schema);
1313
},
1414

15-
safeParse<T extends ZodSchema>(
16-
data: unknown,
17-
schema: T
18-
): ParsedResult<unknown, z.infer<T>> {
15+
safeParse<T extends ZodSchema>(data: unknown, schema: T): ParsedResult {
1916
const parsedEnvelope = APIGatewayProxyEventSchema.safeParse(data);
2017
if (!parsedEnvelope.success) {
2118
return {

Diff for: packages/parser/src/envelopes/apigwv2.ts

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export const ApiGatewayV2Envelope = {
3939
};
4040
}
4141

42+
// use type assertion to avoid type check, we know it's success here
4243
return parsedBody;
4344
},
4445
};

Diff for: packages/parser/src/envelopes/envelope.ts

+5-15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ZodSchema, z } from 'zod';
22
import { ParseError } from '../errors.js';
3-
import type { ParsedResult } from '../types/parser.js';
3+
import type { ParsedResult } from '../types';
44

55
export const Envelope = {
66
/**
@@ -35,18 +35,14 @@ export const Envelope = {
3535
* @param input
3636
* @param schema
3737
*/
38-
safeParse<T extends ZodSchema>(
39-
input: unknown,
40-
schema: T
41-
): ParsedResult<unknown, z.infer<T>> {
38+
safeParse<T extends ZodSchema>(input: unknown, schema: T): ParsedResult {
4239
try {
4340
if (typeof input !== 'object' && typeof input !== 'string') {
4441
return {
4542
success: false,
46-
error: new ParseError(
43+
error: new Error(
4744
`Invalid data type for envelope. Expected string or object, got ${typeof input}`
4845
),
49-
originalEvent: input,
5046
};
5147
}
5248

@@ -61,18 +57,12 @@ export const Envelope = {
6157
}
6258
: {
6359
success: false,
64-
error: new ParseError('Failed to parse envelope', {
65-
cause: parsed.error,
66-
}),
67-
originalEvent: input,
60+
error: parsed.error,
6861
};
6962
} catch (e) {
7063
return {
7164
success: false,
72-
error: new ParseError('Failed to parse envelope', {
73-
cause: e as Error,
74-
}),
75-
originalEvent: input,
65+
error: e as Error,
7666
};
7767
}
7868
},

Diff for: packages/parser/src/envelopes/kafka.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { Envelope } from './envelope.js';
1818

1919
export const KafkaEnvelope = {
2020
parse<T extends ZodSchema>(data: unknown, schema: T): z.infer<T>[] {
21-
// manually fetch event source to deside between Msk or SelfManaged
21+
// manually fetch event source to decide between Msk or SelfManaged
2222
const eventSource = (data as KafkaMskEvent).eventSource;
2323

2424
const parsedEnvelope:

Diff for: packages/parser/src/types/parser.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type ParsedResultSuccess<Output> = {
2828
type ParsedResultError<Input> = {
2929
success: false;
3030
error: ZodError | Error;
31-
originalEvent: Input;
31+
originalEvent?: Input;
3232
};
3333

3434
/**

Diff for: packages/parser/tests/unit/envelope.test.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import { ZodError, z } from 'zod';
88
import { Envelope } from '../../src/envelopes/envelope.js';
9-
import { ParseError } from '../../src/errors.js';
109

1110
describe('envelope: ', () => {
1211
describe('parseSafe', () => {
@@ -37,8 +36,7 @@ describe('envelope: ', () => {
3736
);
3837
expect(result).toEqual({
3938
success: false,
40-
error: expect.any(ParseError),
41-
originalEvent: { name: 123 },
39+
error: expect.any(Error),
4240
});
4341
});
4442

@@ -49,8 +47,7 @@ describe('envelope: ', () => {
4947
);
5048
expect(result).toEqual({
5149
success: false,
52-
error: expect.any(ParseError),
53-
originalEvent: '{name: "John"}',
50+
error: expect.any(Error),
5451
});
5552
});
5653
});

Diff for: packages/parser/tests/unit/envelopes/apigw.test.ts

+9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* @group unit/parser/envelopes/apigw
55
*/
66

7+
import { ZodError } from 'zod';
78
import { ApiGatewayEnvelope } from '../../../src/envelopes/index.js';
89
import { ParseError } from '../../../src/errors.js';
910
import type { APIGatewayProxyEvent } from '../../../src/types/schema.js';
@@ -68,6 +69,10 @@ describe('API Gateway REST Envelope', () => {
6869
error: expect.any(ParseError),
6970
originalEvent: event,
7071
});
72+
73+
if (!parseResult.success && parseResult.error) {
74+
expect(parseResult.error.cause).toBeInstanceOf(ZodError);
75+
}
7176
});
7277

7378
it('should not throw if the body is null', () => {
@@ -84,6 +89,10 @@ describe('API Gateway REST Envelope', () => {
8489
error: expect.any(ParseError),
8590
originalEvent: event,
8691
});
92+
93+
if (!parseResult.success && parseResult.error) {
94+
expect(parseResult.error.cause).toBeInstanceOf(ZodError);
95+
}
8796
});
8897

8998
it('should not throw if the event is invalid', () => {

Diff for: packages/parser/tests/unit/envelopes/apigwv2.test.ts

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* @group unit/parser/envelopes/apigwv2
55
*/
66

7+
import { ZodError } from 'zod';
78
import { ApiGatewayV2Envelope } from '../../../src/envelopes/index.js';
89
import { ParseError } from '../../../src/errors.js';
910
import type { APIGatewayProxyEventV2 } from '../../../src/types/schema.js';
@@ -68,6 +69,10 @@ describe('API Gateway HTTP Envelope', () => {
6869
error: expect.any(ParseError),
6970
originalEvent: event,
7071
});
72+
73+
if (!parseResult.success && parseResult.error) {
74+
expect(parseResult.error.cause).toBeInstanceOf(ZodError);
75+
}
7176
});
7277

7378
it('should not throw if the body is undefined', () => {

Diff for: packages/parser/tests/unit/envelopes/cloudwatch.test.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import { gzipSync } from 'node:zlib';
88
import { generateMock } from '@anatine/zod-mock';
9+
import { ZodError } from 'zod';
910
import { ParseError } from '../../../src';
1011
import { CloudWatchEnvelope } from '../../../src/envelopes/index.js';
1112
import {
@@ -113,11 +114,16 @@ describe('CloudWatch', () => {
113114
Buffer.from(JSON.stringify(logMock), 'utf8')
114115
).toString('base64');
115116

116-
expect(CloudWatchEnvelope.safeParse(testEvent, TestSchema)).toEqual({
117+
const parseResult = CloudWatchEnvelope.safeParse(testEvent, TestSchema);
118+
expect(parseResult).toEqual({
117119
success: false,
118-
error: expect.any(Error),
120+
error: expect.any(ParseError),
119121
originalEvent: testEvent,
120122
});
123+
124+
if (!parseResult.success && parseResult.error) {
125+
expect(parseResult.error.cause).toBeInstanceOf(ZodError);
126+
}
121127
});
122128

123129
it('should return success false when envelope does not match', () => {

Diff for: packages/parser/tests/unit/envelopes/dynamodb.test.ts

+10-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import { generateMock } from '@anatine/zod-mock';
88
import type { AttributeValue, DynamoDBStreamEvent } from 'aws-lambda';
9-
import { z } from 'zod';
9+
import { ZodError, z } from 'zod';
1010
import { DynamoDBStreamEnvelope } from '../../../src/envelopes/index.js';
1111
import { ParseError } from '../../../src/errors.js';
1212
import { TestEvents } from '../schema/utils.js';
@@ -93,12 +93,19 @@ describe('DynamoDB', () => {
9393
(invalidDDBEvent.Records[1].dynamodb!.OldImage as typeof mockOldImage) =
9494
mockOldImage;
9595

96-
const parsed = DynamoDBStreamEnvelope.safeParse(invalidDDBEvent, schema);
97-
expect(parsed).toEqual({
96+
const parseResult = DynamoDBStreamEnvelope.safeParse(
97+
invalidDDBEvent,
98+
schema
99+
);
100+
expect(parseResult).toEqual({
98101
success: false,
99102
error: expect.any(ParseError),
100103
originalEvent: invalidDDBEvent,
101104
});
105+
106+
if (!parseResult.success && parseResult.error) {
107+
expect(parseResult.error.cause).toBeInstanceOf(ZodError);
108+
}
102109
});
103110

104111
it('safeParse should return error if OldImage is invalid', () => {

Diff for: packages/parser/tests/unit/envelopes/eventbridge.test.ts

+11-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import { generateMock } from '@anatine/zod-mock';
88
import type { EventBridgeEvent } from 'aws-lambda';
9+
import { ZodError } from 'zod';
910
import { EventBridgeEnvelope } from '../../../src/envelopes/index.js';
1011
import { ParseError } from '../../../src/errors.js';
1112
import { TestEvents, TestSchema } from '../schema/utils.js';
@@ -85,13 +86,19 @@ describe('EventBridgeEnvelope ', () => {
8586
foo: 'bar',
8687
};
8788

88-
expect(
89-
EventBridgeEnvelope.safeParse(eventBridgeEvent, TestSchema)
90-
).toEqual({
89+
const parseResult = EventBridgeEnvelope.safeParse(
90+
eventBridgeEvent,
91+
TestSchema
92+
);
93+
expect(parseResult).toEqual({
9194
success: false,
9295
error: expect.any(ParseError),
9396
originalEvent: eventBridgeEvent,
9497
});
98+
99+
if (!parseResult.success && parseResult.error) {
100+
expect(parseResult.error.cause).toBeInstanceOf(ZodError);
101+
}
95102
});
96103

97104
it('should safe parse eventbridge event and return original event if invalid data type', () => {
@@ -106,7 +113,7 @@ describe('EventBridgeEnvelope ', () => {
106113
EventBridgeEnvelope.safeParse(eventBridgeEvent, TestSchema)
107114
).toEqual({
108115
success: false,
109-
error: expect.any(Error),
116+
error: expect.any(ParseError),
110117
originalEvent: eventBridgeEvent,
111118
});
112119
});

Diff for: packages/parser/tests/unit/envelopes/kafka.test.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import { generateMock } from '@anatine/zod-mock';
88
import type { MSKEvent, SelfManagedKafkaEvent } from 'aws-lambda';
9+
import { ParseError } from '../../../src';
910
import { KafkaEnvelope } from '../../../src/envelopes/index.js';
1011
import { TestEvents, TestSchema } from '../schema/utils.js';
1112

@@ -76,18 +77,22 @@ describe('Kafka', () => {
7677
const kafkaEvent = TestEvents.kafkaEventMsk as MSKEvent;
7778
kafkaEvent.records['mytopic-0'][0].value = 'not a valid json';
7879

79-
const result = KafkaEnvelope.safeParse(kafkaEvent, TestSchema);
80+
const parseResult = KafkaEnvelope.safeParse(kafkaEvent, TestSchema);
8081

81-
expect(result).toEqual({
82+
expect(parseResult).toEqual({
8283
success: false,
83-
error: expect.any(Error),
84+
error: expect.any(ParseError),
8485
originalEvent: kafkaEvent,
8586
});
87+
88+
if (!parseResult.success && parseResult.error) {
89+
expect(parseResult.error.cause).toBeInstanceOf(SyntaxError);
90+
}
8691
});
8792
it('should return original event and error if envelope is invalid', () => {
8893
expect(KafkaEnvelope.safeParse({ foo: 'bar' }, TestSchema)).toEqual({
8994
success: false,
90-
error: expect.any(Error),
95+
error: expect.any(ParseError),
9196
originalEvent: { foo: 'bar' },
9297
});
9398
});

Diff for: packages/parser/tests/unit/envelopes/kinesis-firehose.test.ts

+13-15
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
*/
66

77
import { generateMock } from '@anatine/zod-mock';
8-
import type { z } from 'zod';
8+
import { ZodError, type z } from 'zod';
9+
import { ParseError } from '../../../src';
910
import { KinesisFirehoseEnvelope } from '../../../src/envelopes/index.js';
1011
import type { KinesisFirehoseSchema } from '../../../src/schemas/';
1112
import { TestEvents, TestSchema } from '../schema/utils.js';
@@ -128,13 +129,19 @@ describe('Kinesis Firehose Envelope', () => {
128129
expect(resp).toEqual({ success: true, data: [mock, mock] });
129130
});
130131
it('should return original event if envelope is invalid', () => {
131-
expect(
132-
KinesisFirehoseEnvelope.safeParse({ foo: 'bar' }, TestSchema)
133-
).toEqual({
132+
const parseResult = KinesisFirehoseEnvelope.safeParse(
133+
{ foo: 'bar' },
134+
TestSchema
135+
);
136+
expect(parseResult).toEqual({
134137
success: false,
135-
error: expect.any(Error),
138+
error: expect.any(ParseError),
136139
originalEvent: { foo: 'bar' },
137140
});
141+
142+
if (!parseResult.success && parseResult.error) {
143+
expect(parseResult.error.cause).toBeInstanceOf(ZodError);
144+
}
138145
});
139146
it('should return original event if record is not base64 encoded', () => {
140147
const testEvent = TestEvents.kinesisFirehosePutEvent as z.infer<
@@ -147,18 +154,9 @@ describe('Kinesis Firehose Envelope', () => {
147154

148155
expect(KinesisFirehoseEnvelope.safeParse(testEvent, TestSchema)).toEqual({
149156
success: false,
150-
error: expect.any(Error),
157+
error: expect.any(ParseError),
151158
originalEvent: testEvent,
152159
});
153160
});
154-
it('should return original event envelope is invalid', () => {
155-
expect(
156-
KinesisFirehoseEnvelope.safeParse({ foo: 'bar' }, TestSchema)
157-
).toEqual({
158-
success: false,
159-
error: expect.any(Error),
160-
originalEvent: { foo: 'bar' },
161-
});
162-
});
163161
});
164162
});

Diff for: packages/parser/tests/unit/envelopes/kinesis.test.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,16 @@ describe('KinesisEnvelope', () => {
6161
it('should return original event if record is invalid', () => {
6262
const testEvent = TestEvents.kinesisStreamEvent as KinesisStreamEvent;
6363
testEvent.Records[0].kinesis.data = 'invalid';
64-
const resp = KinesisEnvelope.safeParse(testEvent, TestSchema);
65-
expect(resp).toEqual({
64+
const parseResult = KinesisEnvelope.safeParse(testEvent, TestSchema);
65+
expect(parseResult).toEqual({
6666
success: false,
6767
error: expect.any(ParseError),
6868
originalEvent: testEvent,
6969
});
70+
71+
if (!parseResult.success && parseResult.error) {
72+
expect(parseResult.error.cause).toBeInstanceOf(SyntaxError);
73+
}
7074
});
7175
});
7276
});

0 commit comments

Comments
 (0)