11import { DMMF } from '@prisma/generator-helper' ;
2- import { PluginError , PluginOptions } from '@zenstackhq/sdk' ;
2+ import { CrudFailureReason , PluginError , PluginOptions , RUNTIME_PACKAGE } from '@zenstackhq/sdk' ;
33import { Model } from '@zenstackhq/sdk/ast' ;
4+ import { camelCase } from 'change-case' ;
45import { promises as fs } from 'fs' ;
56import path from 'path' ;
6- import { generate as PrismaZodGenerator } from './zod/generator' ;
7- import { generateProcedure , generateRouterSchemaImports , getInputTypeByOpName , resolveModelsComments } from './helpers' ;
7+ import { Project } from 'ts-morph' ;
8+ import {
9+ generateHelperImport ,
10+ generateProcedure ,
11+ generateRouterSchemaImports ,
12+ getInputTypeByOpName ,
13+ resolveModelsComments ,
14+ } from './helpers' ;
815import { project } from './project' ;
916import removeDir from './utils/removeDir' ;
10- import { camelCase } from 'change-case' ;
11- import { Project } from 'ts-morph' ;
17+ import { generate as PrismaZodGenerator } from './zod/generator' ;
1218
1319export async function generate ( model : Model , options : PluginOptions , dmmf : DMMF . Document ) {
1420 let outDir = options . output as string ;
@@ -33,6 +39,13 @@ export async function generate(model: Model, options: PluginOptions, dmmf: DMMF.
3339 const hiddenModels : string [ ] = [ ] ;
3440 resolveModelsComments ( models , hiddenModels ) ;
3541
42+ createAppRouter ( outDir , modelOperations , hiddenModels ) ;
43+ createHelper ( outDir ) ;
44+
45+ await project . save ( ) ;
46+ }
47+
48+ function createAppRouter ( outDir : string , modelOperations : DMMF . ModelMapping [ ] , hiddenModels : string [ ] ) {
3649 const appRouter = project . createSourceFile ( path . resolve ( outDir , 'routers' , `index.ts` ) , undefined , {
3750 overwrite : true ,
3851 } ) ;
@@ -110,7 +123,6 @@ export async function generate(model: Model, options: PluginOptions, dmmf: DMMF.
110123 } ) ;
111124
112125 appRouter . formatText ( ) ;
113- await project . save ( ) ;
114126}
115127
116128function generateModelCreateRouter (
@@ -133,6 +145,7 @@ function generateModelCreateRouter(
133145 ] ) ;
134146
135147 generateRouterSchemaImports ( modelRouter , model ) ;
148+ generateHelperImport ( modelRouter ) ;
136149
137150 modelRouter
138151 . addFunction ( {
@@ -162,3 +175,108 @@ function generateModelCreateRouter(
162175
163176 modelRouter . formatText ( ) ;
164177}
178+
179+ function createHelper ( outDir : string ) {
180+ const sf = project . createSourceFile ( path . resolve ( outDir , 'helper.ts' ) , undefined , {
181+ overwrite : true ,
182+ } ) ;
183+
184+ sf . addStatements ( `import { TRPCError } from '@trpc/server';` ) ;
185+ sf . addStatements ( `import { isPrismaClientKnownRequestError } from '${ RUNTIME_PACKAGE } ';` ) ;
186+
187+ const checkMutate = sf . addFunction ( {
188+ name : 'checkMutate' ,
189+ typeParameters : [ { name : 'T' } ] ,
190+ parameters : [
191+ {
192+ name : 'promise' ,
193+ type : 'Promise<T>' ,
194+ } ,
195+ ] ,
196+ isAsync : true ,
197+ isExported : true ,
198+ returnType : 'Promise<T | undefined>' ,
199+ } ) ;
200+
201+ checkMutate . setBodyText (
202+ `try {
203+ return await promise;
204+ } catch (err: any) {
205+ if (isPrismaClientKnownRequestError(err)) {
206+ if (err.code === 'P2004') {
207+ if (err.meta?.reason === '${ CrudFailureReason . RESULT_NOT_READABLE } ') {
208+ // unable to readback data
209+ return undefined;
210+ } else {
211+ // rejected by policy
212+ throw new TRPCError({
213+ code: 'FORBIDDEN',
214+ message: err.message,
215+ cause: err,
216+ });
217+ }
218+ } else {
219+ // request error
220+ throw new TRPCError({
221+ code: 'BAD_REQUEST',
222+ message: err.message,
223+ cause: err,
224+ });
225+ }
226+ } else {
227+ throw err;
228+ }
229+ }
230+ `
231+ ) ;
232+ checkMutate . formatText ( ) ;
233+
234+ const checkRead = sf . addFunction ( {
235+ name : 'checkRead' ,
236+ typeParameters : [ { name : 'T' } ] ,
237+ parameters : [
238+ {
239+ name : 'promise' ,
240+ type : 'Promise<T>' ,
241+ } ,
242+ ] ,
243+ isAsync : true ,
244+ isExported : true ,
245+ returnType : 'Promise<T>' ,
246+ } ) ;
247+
248+ checkRead . setBodyText (
249+ `try {
250+ return await promise;
251+ } catch (err: any) {
252+ if (isPrismaClientKnownRequestError(err)) {
253+ if (err.code === 'P2004') {
254+ // rejected by policy
255+ throw new TRPCError({
256+ code: 'FORBIDDEN',
257+ message: err.message,
258+ cause: err,
259+ });
260+ } else if (err.code === 'P2025') {
261+ // not found
262+ throw new TRPCError({
263+ code: 'NOT_FOUND',
264+ message: err.message,
265+ cause: err,
266+ });
267+ } else {
268+ // request error
269+ throw new TRPCError({
270+ code: 'BAD_REQUEST',
271+ message: err.message,
272+ cause: err,
273+ })
274+ }
275+ } else {
276+ throw err;
277+ }
278+ }
279+ `
280+ ) ;
281+ checkRead . formatText ( ) ;
282+ }
0 commit comments