prisma-generator-express 1.13.0 → 1.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -15
- package/dist/helpers/generateRouteFile.js +1 -1
- package/package.json +1 -1
- package/src/copy/transformZod.spec.ts +212 -5
- package/src/copy/transformZod.ts +78 -57
- package/src/helpers/generateRouteFile.ts +1 -1
package/README.md
CHANGED
|
@@ -114,9 +114,10 @@ The library will create functions to generate routers per each model in schema.
|
|
|
114
114
|
import express, { json } from 'express'
|
|
115
115
|
import type { Response, Request, NextFunction, RequestHandler } from 'express'
|
|
116
116
|
|
|
117
|
-
import { orderItemRouter } from '../prisma/generated/express/orderItem'
|
|
118
|
-
import RouteConfig from '../prisma/generated/express/routeConfig'
|
|
119
117
|
import { PrismaClient } from '../prisma/generated/client'
|
|
118
|
+
import { UserAccountRouter } from '../prisma/generated/express/UserAccount'
|
|
119
|
+
import { RouteConfig } from '~prisma/generated/express/routeConfig'
|
|
120
|
+
import { UserAccountFindFirstSchema } from '../prisma/generated/prisma-zod-generator/schemas'
|
|
120
121
|
|
|
121
122
|
const app = express()
|
|
122
123
|
|
|
@@ -132,28 +133,27 @@ const addPrisma: RequestHandler = (
|
|
|
132
133
|
next: NextFunction,
|
|
133
134
|
) => {
|
|
134
135
|
req.prisma = prisma
|
|
135
|
-
req.omitOutputValidation = true
|
|
136
|
+
// req.omitOutputValidation = true (not required if you use `select` instead of `include`)
|
|
136
137
|
next()
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
/**
|
|
140
|
-
*
|
|
141
|
-
* Demonstrates how to add custom properties to the request object to be used in later middleware or route handlers.
|
|
141
|
+
* Run context-related operations or modify `req` properties to control the behavior of the route
|
|
142
142
|
*/
|
|
143
|
-
const
|
|
143
|
+
const beforeFindFirst: RequestHandler = (
|
|
144
144
|
req: Request,
|
|
145
145
|
res: Response,
|
|
146
146
|
next: NextFunction,
|
|
147
147
|
) => {
|
|
148
|
-
|
|
148
|
+
req.passToNext = true
|
|
149
149
|
next()
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
/**
|
|
153
|
-
*
|
|
154
|
-
*
|
|
153
|
+
* if `req.passToNext` is true, then the result of generated middleware
|
|
154
|
+
* will be available in req.locals?.data for modifications
|
|
155
155
|
*/
|
|
156
|
-
const
|
|
156
|
+
const afterFindFirst: RequestHandler = (
|
|
157
157
|
req: Request,
|
|
158
158
|
res: Response,
|
|
159
159
|
next: NextFunction,
|
|
@@ -162,17 +162,39 @@ const afterFindMany: RequestHandler = (
|
|
|
162
162
|
next()
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
/**
|
|
166
|
+
* For generated route the middleware order will be as follows:
|
|
167
|
+
* 1. Query parser (kicks in for GET requests)
|
|
168
|
+
* 2. Custom middlewares: config.{method}.before[]
|
|
169
|
+
* 3. Input validator middleware (Optional): config.{method}.input
|
|
170
|
+
* 4. Generated middleware
|
|
171
|
+
* 5. Output validator middleware: config.{method}.input
|
|
172
|
+
* 6. Custom middlewares: config.{method}.after[] (not available if req.passToNext is falsy)
|
|
173
|
+
*/
|
|
165
174
|
const someRouterConfig: RouteConfig<RequestHandler> = {
|
|
166
|
-
|
|
167
|
-
before: [
|
|
168
|
-
after: [
|
|
175
|
+
FindFirst: {
|
|
176
|
+
before: [beforeFindFirst],
|
|
177
|
+
after: [afterFindFirst],
|
|
178
|
+
input: {
|
|
179
|
+
schema: UserAccountFindFirstSchema, // make sure you set `isGenerateSelect = true` in prisma-zod-generator
|
|
180
|
+
allow: [
|
|
181
|
+
'select.id',
|
|
182
|
+
'select.full_name',
|
|
183
|
+
'select.emailAddress',
|
|
184
|
+
'select.orders[].ProductName',
|
|
185
|
+
'select.orders[].quantity',
|
|
186
|
+
'where.id',
|
|
187
|
+
'where.createdAt',
|
|
188
|
+
],
|
|
189
|
+
},
|
|
169
190
|
},
|
|
170
191
|
addModelPrefix: true,
|
|
171
192
|
enableAll: true,
|
|
172
193
|
customUrlPrefix: '/v1',
|
|
173
194
|
}
|
|
174
195
|
|
|
175
|
-
app.use(addPrisma
|
|
196
|
+
app.use(addPrisma)
|
|
197
|
+
app.use(UserAccountRouter(someRouterConfig))
|
|
176
198
|
|
|
177
199
|
app.listen(3000, () => {
|
|
178
200
|
console.log('Server is running on http://localhost:3000')
|
|
@@ -197,7 +219,7 @@ The following properties can be attached to the `req` object to control the beha
|
|
|
197
219
|
| ------------ | -------- | ------------ |
|
|
198
220
|
| `findUnique` | `GET` | `/:id` |
|
|
199
221
|
| `findFirst` | `GET` | `/first` |
|
|
200
|
-
| `
|
|
222
|
+
| `FindFirst` | `GET` | `/` |
|
|
201
223
|
| `create` | `POST` | `/` |
|
|
202
224
|
| `createMany` | `POST` | `/many` |
|
|
203
225
|
| `update` | `PUT` | `/` |
|
|
@@ -27,7 +27,7 @@ import { ${modelName}GroupBy } from './${modelName}GroupBy';
|
|
|
27
27
|
import { createValidatorMiddleware, ValidatorOptions } from '../createValidatorMiddleware'
|
|
28
28
|
import { createOutputValidatorMiddleware } from '../createOutputValidatorMiddleware'
|
|
29
29
|
import { RouteConfig, ValidatorConfig } from '../routeConfig'
|
|
30
|
-
import { parseQueryParams } from "../
|
|
30
|
+
import { parseQueryParams } from "../parseQueryParams";
|
|
31
31
|
|
|
32
32
|
const defaultBeforeAfter = {
|
|
33
33
|
before: [] as RequestHandler[],
|
package/package.json
CHANGED
|
@@ -30,8 +30,8 @@ describe('Cryptocurrency Schema Validation', () => {
|
|
|
30
30
|
const allowedFields = [
|
|
31
31
|
'wallet.id',
|
|
32
32
|
'wallet.owner.name',
|
|
33
|
-
'transactions.amount',
|
|
34
|
-
'transactions.currency',
|
|
33
|
+
'transactions[].amount',
|
|
34
|
+
'transactions[].currency',
|
|
35
35
|
]
|
|
36
36
|
const forbiddenFields = ['wallet.owner.age', 'transactions.details.fee']
|
|
37
37
|
|
|
@@ -225,9 +225,9 @@ describe('Cryptocurrency Schema Validation', () => {
|
|
|
225
225
|
'wallet.id',
|
|
226
226
|
'wallet.owner.name',
|
|
227
227
|
'wallet.owner.age',
|
|
228
|
-
'transactions.amount',
|
|
229
|
-
'transactions.currency',
|
|
230
|
-
'transactions.details.fee',
|
|
228
|
+
'transactions[].amount',
|
|
229
|
+
'transactions[].currency',
|
|
230
|
+
'transactions[].details.fee',
|
|
231
231
|
]
|
|
232
232
|
|
|
233
233
|
try {
|
|
@@ -553,4 +553,211 @@ describe('Cryptocurrency Schema Validation', () => {
|
|
|
553
553
|
expect(error).toBeInstanceOf(ZodError)
|
|
554
554
|
}
|
|
555
555
|
})
|
|
556
|
+
describe('Prisma example', () => {
|
|
557
|
+
const inputData = {
|
|
558
|
+
select: {
|
|
559
|
+
id: true,
|
|
560
|
+
project_id: true,
|
|
561
|
+
list_id: true,
|
|
562
|
+
user_assignments: {
|
|
563
|
+
select: {
|
|
564
|
+
user: true,
|
|
565
|
+
},
|
|
566
|
+
},
|
|
567
|
+
tags_mappings: {
|
|
568
|
+
select: {
|
|
569
|
+
tag: true,
|
|
570
|
+
},
|
|
571
|
+
},
|
|
572
|
+
attachments: {
|
|
573
|
+
select: {
|
|
574
|
+
attachment: true,
|
|
575
|
+
},
|
|
576
|
+
where: {
|
|
577
|
+
is_image: true,
|
|
578
|
+
},
|
|
579
|
+
take: 100,
|
|
580
|
+
orderBy: {
|
|
581
|
+
created_at: 'desc',
|
|
582
|
+
},
|
|
583
|
+
},
|
|
584
|
+
rendered_description: true,
|
|
585
|
+
description: true,
|
|
586
|
+
created_at: true,
|
|
587
|
+
start_date: true,
|
|
588
|
+
reactions: true,
|
|
589
|
+
intervals: true,
|
|
590
|
+
column_id: true,
|
|
591
|
+
priority: true,
|
|
592
|
+
due_date: true,
|
|
593
|
+
column: true,
|
|
594
|
+
title: true,
|
|
595
|
+
order: true,
|
|
596
|
+
color: true,
|
|
597
|
+
},
|
|
598
|
+
where: {
|
|
599
|
+
id: 'task_id',
|
|
600
|
+
AND: [
|
|
601
|
+
{
|
|
602
|
+
OR: [{ to_delete: false }, { to_delete: null }],
|
|
603
|
+
},
|
|
604
|
+
],
|
|
605
|
+
},
|
|
606
|
+
}
|
|
607
|
+
it('deep nesting', () => {
|
|
608
|
+
const taskSchema = z.object({
|
|
609
|
+
select: z
|
|
610
|
+
.object({
|
|
611
|
+
id: z.boolean().optional(),
|
|
612
|
+
project_id: z.boolean().optional(),
|
|
613
|
+
list_id: z.boolean().optional(),
|
|
614
|
+
user_assignments: z
|
|
615
|
+
.object({
|
|
616
|
+
select: z.object({
|
|
617
|
+
user: z.boolean().optional(),
|
|
618
|
+
}),
|
|
619
|
+
})
|
|
620
|
+
.optional(),
|
|
621
|
+
tags_mappings: z
|
|
622
|
+
.object({
|
|
623
|
+
select: z.object({
|
|
624
|
+
tag: z.boolean().optional(),
|
|
625
|
+
}),
|
|
626
|
+
})
|
|
627
|
+
.optional(),
|
|
628
|
+
attachments: z
|
|
629
|
+
.object({
|
|
630
|
+
select: z.object({
|
|
631
|
+
attachment: z.boolean().optional(),
|
|
632
|
+
}),
|
|
633
|
+
where: z
|
|
634
|
+
.object({
|
|
635
|
+
is_image: z.boolean().optional(),
|
|
636
|
+
})
|
|
637
|
+
.optional(),
|
|
638
|
+
take: z.number().optional(),
|
|
639
|
+
orderBy: z
|
|
640
|
+
.object({
|
|
641
|
+
created_at: z.enum(['asc', 'desc']).optional(),
|
|
642
|
+
})
|
|
643
|
+
.optional(),
|
|
644
|
+
})
|
|
645
|
+
.optional(),
|
|
646
|
+
rendered_description: z.boolean().optional(),
|
|
647
|
+
description: z.boolean().optional(),
|
|
648
|
+
created_at: z.boolean().optional(),
|
|
649
|
+
start_date: z.boolean().optional(),
|
|
650
|
+
reactions: z.boolean().optional(),
|
|
651
|
+
intervals: z.boolean().optional(),
|
|
652
|
+
column_id: z.boolean().optional(),
|
|
653
|
+
priority: z.boolean().optional(),
|
|
654
|
+
due_date: z.boolean().optional(),
|
|
655
|
+
column: z.boolean().optional(),
|
|
656
|
+
title: z.boolean().optional(),
|
|
657
|
+
order: z.boolean().optional(),
|
|
658
|
+
color: z.boolean().optional(),
|
|
659
|
+
})
|
|
660
|
+
.optional(),
|
|
661
|
+
where: z
|
|
662
|
+
.object({
|
|
663
|
+
id: z.string().optional(),
|
|
664
|
+
AND: z
|
|
665
|
+
.array(
|
|
666
|
+
z.object({
|
|
667
|
+
OR: z
|
|
668
|
+
.array(
|
|
669
|
+
z.object({
|
|
670
|
+
to_delete: z.boolean().nullable().optional(),
|
|
671
|
+
}),
|
|
672
|
+
)
|
|
673
|
+
.optional(),
|
|
674
|
+
}),
|
|
675
|
+
)
|
|
676
|
+
.optional(),
|
|
677
|
+
})
|
|
678
|
+
.optional(),
|
|
679
|
+
})
|
|
680
|
+
|
|
681
|
+
const allowedFields = [
|
|
682
|
+
'select.id',
|
|
683
|
+
'select.project_id',
|
|
684
|
+
'select.list_id',
|
|
685
|
+
'select.user_assignments.select.user',
|
|
686
|
+
'select.tags_mappings.select.tag',
|
|
687
|
+
'select.attachments.select.attachment',
|
|
688
|
+
'select.attachments.where.is_image',
|
|
689
|
+
'select.attachments.take',
|
|
690
|
+
'select.attachments.orderBy.created_at',
|
|
691
|
+
'select.rendered_description',
|
|
692
|
+
'select.description',
|
|
693
|
+
'select.created_at',
|
|
694
|
+
'select.start_date',
|
|
695
|
+
'select.reactions',
|
|
696
|
+
'select.intervals',
|
|
697
|
+
'select.column_id',
|
|
698
|
+
'select.priority',
|
|
699
|
+
'select.due_date',
|
|
700
|
+
'select.column',
|
|
701
|
+
'select.order',
|
|
702
|
+
'select.color',
|
|
703
|
+
'where.id',
|
|
704
|
+
'where.AND[].OR[].to_delete',
|
|
705
|
+
]
|
|
706
|
+
|
|
707
|
+
try {
|
|
708
|
+
const result = allow(taskSchema, allowedFields).safeParse(inputData)
|
|
709
|
+
expect(result.success).toBe(false)
|
|
710
|
+
} catch (error) {
|
|
711
|
+
expect(error).toBeInstanceOf(ZodError)
|
|
712
|
+
}
|
|
713
|
+
})
|
|
714
|
+
|
|
715
|
+
it('deep nesting 2', () => {
|
|
716
|
+
const taskSchema = z.object({
|
|
717
|
+
where: z
|
|
718
|
+
.object({
|
|
719
|
+
id: z.string().optional(),
|
|
720
|
+
AND: z
|
|
721
|
+
.array(
|
|
722
|
+
z.object({
|
|
723
|
+
OR: z
|
|
724
|
+
.array(
|
|
725
|
+
z.object({
|
|
726
|
+
to_delete: z.boolean().nullable().optional(),
|
|
727
|
+
}),
|
|
728
|
+
)
|
|
729
|
+
.optional(),
|
|
730
|
+
}),
|
|
731
|
+
)
|
|
732
|
+
.optional(),
|
|
733
|
+
})
|
|
734
|
+
.optional(),
|
|
735
|
+
})
|
|
736
|
+
|
|
737
|
+
const allowedFields = [
|
|
738
|
+
'select.id',
|
|
739
|
+
'select.project_id',
|
|
740
|
+
'select.list_id',
|
|
741
|
+
'select.user_assignments.select.user',
|
|
742
|
+
'select.tags_mappings.select.tag',
|
|
743
|
+
'select.attachments.select.attachment',
|
|
744
|
+
'select.attachments.where.is_image',
|
|
745
|
+
'select.attachments.take',
|
|
746
|
+
'select.attachments.orderBy.created_at',
|
|
747
|
+
'select.rendered_description',
|
|
748
|
+
'select.description',
|
|
749
|
+
'select.created_at',
|
|
750
|
+
'select.start_date',
|
|
751
|
+
'where.id',
|
|
752
|
+
'where.AND[].OR[].to_delete',
|
|
753
|
+
]
|
|
754
|
+
|
|
755
|
+
try {
|
|
756
|
+
const result = allow(taskSchema, allowedFields).safeParse(inputData)
|
|
757
|
+
expect(result.success).toBe(false)
|
|
758
|
+
} catch (error) {
|
|
759
|
+
expect(error).toBeInstanceOf(ZodError)
|
|
760
|
+
}
|
|
761
|
+
})
|
|
762
|
+
})
|
|
556
763
|
})
|
package/src/copy/transformZod.ts
CHANGED
|
@@ -9,39 +9,54 @@ import {
|
|
|
9
9
|
ZodTypeAny,
|
|
10
10
|
} from 'zod'
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
function startsWith(str: string, prefix: string): boolean {
|
|
13
|
+
return str.slice(0, prefix.length) === prefix
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function every<T>(
|
|
17
|
+
array: T[],
|
|
18
|
+
callback: (value: T, index: number, array: T[]) => boolean,
|
|
19
|
+
): boolean {
|
|
20
|
+
for (let i = 0; i < array.length; i++) {
|
|
21
|
+
if (!callback(array[i], i, array)) {
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return true
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isKeyAllowed(key: string, allowedPaths: string[]): boolean {
|
|
29
|
+
return !every(
|
|
30
|
+
allowedPaths,
|
|
31
|
+
(path) =>
|
|
32
|
+
!startsWith(key.replace(/\[\d+\]/g, ''), path.replace(/\[\d+\]/g, '')) &&
|
|
33
|
+
!startsWith(path.replace(/\[\d+\]/g, ''), key.replace(/\[\d+\]/g, '')),
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function allow<T extends ZodTypeAny>(
|
|
13
38
|
schema: T,
|
|
14
39
|
allowedPaths: string[],
|
|
15
40
|
): ZodEffects<T, any, any> {
|
|
16
|
-
const rootSchema = schema instanceof z.ZodObject ? schema : undefined
|
|
41
|
+
const rootSchema = schema instanceof z.ZodObject ? schema.strict() : undefined
|
|
17
42
|
|
|
18
|
-
return
|
|
43
|
+
return rootSchema?.transform((data) => {
|
|
19
44
|
const flatData = flattenObject(data, '', rootSchema)
|
|
45
|
+
|
|
20
46
|
const disallowedPaths: string[] = []
|
|
21
47
|
|
|
22
48
|
for (const key of Object.keys(flatData)) {
|
|
23
|
-
if (
|
|
24
|
-
allowedPaths.every(
|
|
25
|
-
(path) => !(key.startsWith(path) || path.startsWith(key)),
|
|
26
|
-
)
|
|
27
|
-
) {
|
|
49
|
+
if (!isKeyAllowed(key, allowedPaths)) {
|
|
28
50
|
disallowedPaths.push(key)
|
|
29
51
|
}
|
|
30
52
|
}
|
|
31
53
|
|
|
32
54
|
if (disallowedPaths.length > 0) {
|
|
33
|
-
|
|
34
|
-
for (const path of disallowedPaths) {
|
|
35
|
-
errors.push({
|
|
36
|
-
code: ZodIssueCode.custom,
|
|
37
|
-
message: `Field '${path}' is not allowed.`,
|
|
38
|
-
path: path.split('.'),
|
|
39
|
-
})
|
|
40
|
-
}
|
|
41
|
-
throw new ZodError(errors)
|
|
55
|
+
throw createZodErrorFromPaths(disallowedPaths, 'Field is not allowed:')
|
|
42
56
|
}
|
|
57
|
+
|
|
43
58
|
return data
|
|
44
|
-
}) as ZodEffects<T, any, any>
|
|
59
|
+
}) as unknown as ZodEffects<T, any, any>
|
|
45
60
|
}
|
|
46
61
|
|
|
47
62
|
export function forbid<T extends z.ZodTypeAny>(
|
|
@@ -60,30 +75,12 @@ export function forbid<T extends z.ZodTypeAny>(
|
|
|
60
75
|
}
|
|
61
76
|
|
|
62
77
|
if (forbiddenMatches.length > 0) {
|
|
63
|
-
|
|
64
|
-
for (const path of forbiddenMatches) {
|
|
65
|
-
errors.push({
|
|
66
|
-
code: ZodIssueCode.custom,
|
|
67
|
-
message: `Field '${path}' is forbidden.`,
|
|
68
|
-
path: [path],
|
|
69
|
-
})
|
|
70
|
-
}
|
|
71
|
-
throw new ZodError(errors)
|
|
78
|
+
throw createZodErrorFromPaths(forbiddenMatches, 'Field is forbidden:')
|
|
72
79
|
}
|
|
73
80
|
return data
|
|
74
81
|
}) as ZodEffects<T, any, any>
|
|
75
82
|
}
|
|
76
83
|
|
|
77
|
-
function isJsonLikeUnion(schemaPart: ZodTypeAny): boolean {
|
|
78
|
-
if (schemaPart instanceof z.ZodOptional) {
|
|
79
|
-
schemaPart = schemaPart.unwrap()
|
|
80
|
-
}
|
|
81
|
-
return (
|
|
82
|
-
schemaPart instanceof z.ZodUnion &&
|
|
83
|
-
schemaPart.options.some((option: ZodTypeAny) => option instanceof z.ZodLazy)
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
84
|
export function flattenObject(
|
|
88
85
|
obj: Record<string, any>,
|
|
89
86
|
prefix = '',
|
|
@@ -91,29 +88,53 @@ export function flattenObject(
|
|
|
91
88
|
): Record<string, any> {
|
|
92
89
|
const result: Record<string, any> = {}
|
|
93
90
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (currentSchema && isJsonLikeUnion(currentSchema)) {
|
|
103
|
-
result[`${pre}${key}`] = obj[key]
|
|
104
|
-
} else if (
|
|
105
|
-
typeof obj[key] === 'object' &&
|
|
106
|
-
obj[key] !== null &&
|
|
107
|
-
currentSchema instanceof ZodObject
|
|
108
|
-
) {
|
|
109
|
-
Object.assign(
|
|
110
|
-
result,
|
|
111
|
-
flattenObject(obj[key], `${pre}${key}`, currentSchema),
|
|
112
|
-
)
|
|
91
|
+
function flatten(current: any, prop: string, schema?: ZodObject<any>) {
|
|
92
|
+
if (Object(current) !== current) {
|
|
93
|
+
result[prop] = current
|
|
94
|
+
} else if (Array.isArray(current)) {
|
|
95
|
+
current.forEach((item, index) => {
|
|
96
|
+
flatten(item, `${prop}[]`, schema)
|
|
97
|
+
})
|
|
113
98
|
} else {
|
|
114
|
-
|
|
99
|
+
let isEmpty = true
|
|
100
|
+
for (const key in current) {
|
|
101
|
+
if (current.hasOwnProperty(key)) {
|
|
102
|
+
isEmpty = false
|
|
103
|
+
const currentSchema = schema?.shape[key]
|
|
104
|
+
if (
|
|
105
|
+
currentSchema instanceof z.ZodOptional &&
|
|
106
|
+
current[key] === undefined
|
|
107
|
+
) {
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
flatten(
|
|
111
|
+
current[key],
|
|
112
|
+
prop ? `${prop}.${key}` : key,
|
|
113
|
+
currentSchema instanceof ZodObject ? currentSchema : undefined,
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (isEmpty) {
|
|
118
|
+
result[prop] = {}
|
|
119
|
+
}
|
|
115
120
|
}
|
|
116
121
|
}
|
|
117
122
|
|
|
123
|
+
flatten(obj, prefix, schema)
|
|
118
124
|
return result
|
|
119
125
|
}
|
|
126
|
+
|
|
127
|
+
function createZodErrorFromPaths(
|
|
128
|
+
disallowedPaths: string[],
|
|
129
|
+
errorMessage: string,
|
|
130
|
+
): ZodError {
|
|
131
|
+
const errors: ZodIssue[] = []
|
|
132
|
+
for (const path of disallowedPaths) {
|
|
133
|
+
errors.push({
|
|
134
|
+
code: ZodIssueCode.custom,
|
|
135
|
+
message: `${errorMessage} '${path}'`,
|
|
136
|
+
path: path.split('.'),
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
return new ZodError(errors)
|
|
140
|
+
}
|
|
@@ -31,7 +31,7 @@ import { ${modelName}GroupBy } from './${modelName}GroupBy';
|
|
|
31
31
|
import { createValidatorMiddleware, ValidatorOptions } from '../createValidatorMiddleware'
|
|
32
32
|
import { createOutputValidatorMiddleware } from '../createOutputValidatorMiddleware'
|
|
33
33
|
import { RouteConfig, ValidatorConfig } from '../routeConfig'
|
|
34
|
-
import { parseQueryParams } from "../
|
|
34
|
+
import { parseQueryParams } from "../parseQueryParams";
|
|
35
35
|
|
|
36
36
|
const defaultBeforeAfter = {
|
|
37
37
|
before: [] as RequestHandler[],
|