@@ -233,11 +233,15 @@ fn field_filter_input_values(
233233 // `where: { others: ["some-id", "other-id"] }`. In both cases,
234234 // we allow ID strings as the values to be passed to these
235235 // filters.
236- field_scalar_filter_input_values (
236+ let mut input_values = field_scalar_filter_input_values (
237237 schema,
238238 field,
239239 & ScalarType :: new ( String :: from ( "String" ) ) ,
240- )
240+ ) ;
241+
242+ extend_with_child_filter_input_value ( field, name, & mut input_values) ;
243+
244+ input_values
241245 }
242246 }
243247 TypeDefinition :: Scalar ( ref t) => field_scalar_filter_input_values ( schema, field, t) ,
@@ -301,6 +305,19 @@ fn field_scalar_filter_input_values(
301305 . collect ( )
302306}
303307
308+ /// Appends a child filter to input values
309+ fn extend_with_child_filter_input_value (
310+ field : & Field ,
311+ field_type_name : & String ,
312+ input_values : & mut Vec < InputValue > ,
313+ ) {
314+ input_values. push ( input_value (
315+ & format ! ( "{}_" , field. name) ,
316+ "" ,
317+ Type :: NamedType ( format ! ( "{}_filter" , field_type_name) ) ,
318+ ) ) ;
319+ }
320+
304321/// Generates `*_filter` input values for the given enum field.
305322fn field_enum_filter_input_values (
306323 _schema : & Document ,
@@ -333,40 +350,51 @@ fn field_list_filter_input_values(
333350 // Decide what type of values can be passed to the filter. In the case
334351 // one-to-many or many-to-many object or interface fields that are not
335352 // derived, we allow ID strings to be passed on.
336- let input_field_type = match typedef {
337- TypeDefinition :: Interface ( _ ) | TypeDefinition :: Object ( _ ) => {
353+ let ( input_field_type, parent_type_name ) = match typedef {
354+ TypeDefinition :: Object ( parent ) => {
338355 if ast:: get_derived_from_directive ( field) . is_some ( ) {
339356 return None ;
340357 } else {
341- Type :: NamedType ( "String" . into ( ) )
358+ ( Type :: NamedType ( "String" . into ( ) ) , Some ( parent . name . clone ( ) ) )
342359 }
343360 }
344- TypeDefinition :: Scalar ( ref t) => Type :: NamedType ( t. name . to_owned ( ) ) ,
345- TypeDefinition :: Enum ( ref t) => Type :: NamedType ( t. name . to_owned ( ) ) ,
361+ TypeDefinition :: Interface ( parent) => {
362+ if ast:: get_derived_from_directive ( field) . is_some ( ) {
363+ return None ;
364+ } else {
365+ ( Type :: NamedType ( "String" . into ( ) ) , Some ( parent. name . clone ( ) ) )
366+ }
367+ }
368+ TypeDefinition :: Scalar ( ref t) => ( Type :: NamedType ( t. name . to_owned ( ) ) , None ) ,
369+ TypeDefinition :: Enum ( ref t) => ( Type :: NamedType ( t. name . to_owned ( ) ) , None ) ,
346370 TypeDefinition :: InputObject ( _) | TypeDefinition :: Union ( _) => return None ,
347371 } ;
348372
349- Some (
350- vec ! [
351- "" ,
352- "not" ,
353- "contains" ,
354- "contains_nocase" ,
355- "not_contains" ,
356- "not_contains_nocase" ,
357- ]
358- . into_iter ( )
359- . map ( |filter_type| {
360- input_value (
361- & field. name ,
362- filter_type,
363- Type :: ListType ( Box :: new ( Type :: NonNullType ( Box :: new (
364- input_field_type. clone ( ) ,
365- ) ) ) ) ,
366- )
367- } )
368- . collect ( ) ,
369- )
373+ let mut input_values: Vec < InputValue > = vec ! [
374+ "" ,
375+ "not" ,
376+ "contains" ,
377+ "contains_nocase" ,
378+ "not_contains" ,
379+ "not_contains_nocase" ,
380+ ]
381+ . into_iter ( )
382+ . map ( |filter_type| {
383+ input_value (
384+ & field. name ,
385+ filter_type,
386+ Type :: ListType ( Box :: new ( Type :: NonNullType ( Box :: new (
387+ input_field_type. clone ( ) ,
388+ ) ) ) ) ,
389+ )
390+ } )
391+ . collect ( ) ;
392+
393+ if let Some ( parent) = parent_type_name {
394+ extend_with_child_filter_input_value ( field, & parent, & mut input_values) ;
395+ }
396+
397+ Some ( input_values)
370398 } )
371399}
372400
@@ -900,6 +928,7 @@ mod tests {
900928 "pets_contains_nocase" ,
901929 "pets_not_contains" ,
902930 "pets_not_contains_nocase" ,
931+ "pets_" ,
903932 "favoriteFurType" ,
904933 "favoriteFurType_not" ,
905934 "favoriteFurType_in" ,
@@ -924,6 +953,7 @@ mod tests {
924953 "favoritePet_ends_with_nocase" ,
925954 "favoritePet_not_ends_with" ,
926955 "favoritePet_not_ends_with_nocase" ,
956+ "favoritePet_" ,
927957 ]
928958 . iter( )
929959 . map( ToString :: to_string)
@@ -1135,4 +1165,56 @@ type Gravatar @entity {
11351165 }
11361166 . expect ( "\" metadata\" field is missing on Query type" ) ;
11371167 }
1168+
1169+ #[ test]
1170+ fn api_schema_contains_child_entity_filter_on_lists ( ) {
1171+ let input_schema = parse_schema (
1172+ r#"
1173+ type Note @entity {
1174+ id: ID!
1175+ text: String!
1176+ author: User! @derived(field: "id")
1177+ }
1178+
1179+ type User @entity {
1180+ id: ID!
1181+ dateOfBirth: String
1182+ country: String
1183+ notes: [Note!]!
1184+ }
1185+ "# ,
1186+ )
1187+ . expect ( "Failed to parse input schema" ) ;
1188+ let schema = api_schema ( & input_schema) . expect ( "Failed to derived API schema" ) ;
1189+
1190+ println ! ( "{}" , schema) ;
1191+
1192+ let user_filter = schema
1193+ . get_named_type ( "User_filter" )
1194+ . expect ( "User_filter type is missing in derived API schema" ) ;
1195+
1196+ let filter_type = match user_filter {
1197+ TypeDefinition :: InputObject ( t) => Some ( t) ,
1198+ _ => None ,
1199+ }
1200+ . expect ( "User_filter type is not an input object" ) ;
1201+
1202+ let user_notes_filter_field = filter_type
1203+ . fields
1204+ . iter ( )
1205+ . find_map ( |field| {
1206+ if field. name == "notes_" {
1207+ Some ( field)
1208+ } else {
1209+ None
1210+ }
1211+ } )
1212+ . expect ( "notes_ field is missing in the User_filter input object" ) ;
1213+
1214+ assert_eq ! ( user_notes_filter_field. name, "notes_" ) ;
1215+ assert_eq ! (
1216+ user_notes_filter_field. value_type,
1217+ Type :: NamedType ( String :: from( "Note_filter" ) )
1218+ ) ;
1219+ }
11381220}
0 commit comments