11import { useDecorators } from '@tsed/core' ;
2- import { Delete , Get , inject , Post , Put , } from '@tsed/common' ;
3- import { CollectionOf , Default , Example , Property , Required , Returns , Summary } from '@tsed/schema' ;
2+ import { Delete , Get , Post , Put , } from '@tsed/common' ;
3+ import { CollectionOf , Default , Enum , Example , Property , Required , Returns , Summary } from '@tsed/schema' ;
44import _ from 'lodash' ;
55import { SearchFilterRecord } from './types.js' ;
6+ import { PrismaMetaMapper } from './prismaMetaMapper.js' ;
7+ import aigle from 'aigle' ;
8+ const { Aigle } = aigle ;
69
710function nameWithoutModel ( model : any ) : string {
811 return _ . replace ( model . name , 'Model' , '' ) ;
@@ -98,3 +101,142 @@ export function getItems(options: GetItems): Function {
98101 . Description ( `Return a list of ${ nameWithoutModel ( model ) } ` )
99102 ) ;
100103}
104+
105+
106+
107+
108+ export type FilterMode =
109+ | "EM"
110+ | "EQ"
111+ | "EX"
112+ | "LT"
113+ | "LTE"
114+ | "GT"
115+ | "GTE"
116+ | "NEM"
117+ | "RG" ;
118+
119+ export const FilterModeEnum : FilterMode [ ] = [
120+ "EM" , "EQ" , "EX" , "LT" , "LTE" , "GT" , "GTE" , "NEM" , "RG"
121+ ] ;
122+
123+ // Typed filter record
124+ export type SearchFilter < TFields extends string > = {
125+ [ K in TFields ] : {
126+ mode : FilterMode ;
127+ value : any ;
128+ isRelation ?: boolean ;
129+ } ;
130+ } ;
131+
132+ export class BaseSearchParams {
133+ @Default ( 10 )
134+ limit ?: number ;
135+
136+ @Default ( 0 )
137+ offset ?: number ;
138+ }
139+
140+ export class FilterItemModel {
141+ @Enum ( FilterModeEnum )
142+ mode ! : FilterMode ;
143+ @Property ( )
144+ value ! : any ;
145+ @Property ( )
146+ isRelation ?: boolean ;
147+ }
148+
149+ export async function makeSearchParamsForPrismaModel < TField extends string > ( model : string ) {
150+ const entityFieldMapping = await PrismaMetaMapper . getEntityFieldMapping ( PrismaMetaMapper . normalizeEntityName ( model ) ) ;
151+ const scalarExamples = _ . transform ( entityFieldMapping , ( result , value , key ) => {
152+ if ( ! value . isList && ! value . relationName ) {
153+ result . push ( key as TField ) ;
154+ }
155+ } , [ ] as TField [ ] ) ;
156+ const relationExamples = await Aigle . transform ( entityFieldMapping , async ( result , value , key ) => {
157+ if ( value . isList || value . relationName ) {
158+ const relationFieldMapping = await PrismaMetaMapper . getEntityFieldMapping ( PrismaMetaMapper . normalizeEntityName ( value . type ) ) ;
159+ for ( const [ relationFieldName , relationField ] of Object . entries ( relationFieldMapping ) ) {
160+ if ( ! relationField . isList && ! relationField . relationName ) {
161+ result . push ( `${ key } .${ relationFieldName } ` as TField ) ;
162+ }
163+ }
164+ }
165+ } , [ ] as TField [ ] ) ;
166+ return makeSearchParamsFor < TField > ( [ ...scalarExamples , ...relationExamples ] , entityFieldMapping ) ;
167+ }
168+
169+ function getPrismaExample ( fieldInfo : { type : string ; isArray ?: boolean } ) {
170+ let example : any ;
171+
172+ switch ( fieldInfo . type ) {
173+ case "String" :
174+ example = "example" ;
175+ break ;
176+ case "Int" :
177+ example = 123 ;
178+ break ;
179+ case "BigInt" :
180+ example = 123n ;
181+ break ;
182+ case "Float" :
183+ example = 12.34 ;
184+ break ;
185+ case "Decimal" :
186+ example = "12.34" ; // Prisma Decimal is a string
187+ break ;
188+ case "Boolean" :
189+ example = true ;
190+ break ;
191+ case "DateTime" :
192+ example = new Date ( ) . toISOString ( ) ;
193+ break ;
194+ case "Json" :
195+ example = { key : "value" } ;
196+ break ;
197+ case "Bytes" :
198+ example = "AA==" ; // base64 string
199+ break ;
200+ default :
201+ example = "example" ;
202+ }
203+
204+ if ( fieldInfo . isArray ) {
205+ return [ example ] ;
206+ }
207+
208+ return example ;
209+ }
210+
211+ export function makeSearchParamsFor < TField extends string > ( examples : TField [ ] , entityFieldMapping ?: Record < string , any > ) {
212+ const filterExample = [
213+ examples . reduce ( ( acc , f ) => {
214+ if ( ! f . includes ( '.' ) ) {
215+ const fieldInfo = entityFieldMapping ? entityFieldMapping [ f ] : null ;
216+ acc [ f ] = { mode : "EQ" , value : getPrismaExample ( fieldInfo ) } ;
217+ }
218+ return acc ;
219+ } , { } as Record < TField , any > )
220+ ]
221+ const orderByExample = examples . reduce ( ( acc , f ) => {
222+ if ( ! f . includes ( '.' ) ) {
223+ acc [ f ] = "asc" ;
224+ }
225+ return acc ;
226+ } , { } as Record < TField , "asc" | "desc" > ) ;
227+ class DynamicSearchParams extends BaseSearchParams {
228+ @Example ( examples )
229+ @Required ( true )
230+ @CollectionOf ( String )
231+ fields ! : TField [ ] ;
232+ @Example ( orderByExample )
233+ @CollectionOf ( Object )
234+ orderBy : Record < TField , "asc" | "desc" > ;
235+ @Property ( )
236+ @CollectionOf ( Object )
237+ @Example ( filterExample )
238+ filters : SearchFilter < TField > [ ] ;
239+ }
240+
241+ return DynamicSearchParams ;
242+ }
0 commit comments