11use std:: str:: FromStr ;
22
3- use graphql_parser:: Pos ;
3+ use graphql_parser:: { schema :: TypeDefinition , Pos } ;
44use inflector:: Inflector ;
55use lazy_static:: lazy_static;
66
@@ -157,16 +157,7 @@ fn add_order_by_type(
157157 description : None ,
158158 name : type_name,
159159 directives : vec ! [ ] ,
160- values : fields
161- . iter ( )
162- . map ( |field| & field. name )
163- . map ( |name| EnumValue {
164- position : Pos :: default ( ) ,
165- description : None ,
166- name : name. to_owned ( ) ,
167- directives : vec ! [ ] ,
168- } )
169- . collect ( ) ,
160+ values : field_enum_values ( schema, fields) ?,
170161 } ) ;
171162 let def = Definition :: TypeDefinition ( typedef) ;
172163 schema. definitions . push ( def) ;
@@ -176,6 +167,80 @@ fn add_order_by_type(
176167 Ok ( ( ) )
177168}
178169
170+ /// Generates enum values for the given set of fields.
171+ fn field_enum_values (
172+ schema : & Document ,
173+ fields : & [ Field ] ,
174+ ) -> Result < Vec < EnumValue > , APISchemaError > {
175+ let mut enum_values = vec ! [ ] ;
176+ for field in fields {
177+ enum_values. push ( EnumValue {
178+ position : Pos :: default ( ) ,
179+ description : None ,
180+ name : field. name . to_owned ( ) ,
181+ directives : vec ! [ ] ,
182+ } ) ;
183+ enum_values. extend ( field_enum_values_from_child_entity ( schema, field) ?) ;
184+ }
185+ Ok ( enum_values)
186+ }
187+
188+ fn enum_value_from_child_entity_field (
189+ schema : & Document ,
190+ parent_field_name : & str ,
191+ field : & Field ,
192+ ) -> Option < EnumValue > {
193+ if ast:: is_list_or_non_null_list_field ( field) || ast:: is_entity_type ( schema, & field. field_type )
194+ {
195+ // Sorting on lists or entities is not supported.
196+ None
197+ } else {
198+ Some ( EnumValue {
199+ position : Pos :: default ( ) ,
200+ description : None ,
201+ name : format ! ( "{}__{}" , parent_field_name, field. name) ,
202+ directives : vec ! [ ] ,
203+ } )
204+ }
205+ }
206+
207+ fn field_enum_values_from_child_entity (
208+ schema : & Document ,
209+ field : & Field ,
210+ ) -> Result < Vec < EnumValue > , APISchemaError > {
211+ fn resolve_supported_type_name ( field_type : & Type ) -> Option < & String > {
212+ match field_type {
213+ Type :: NamedType ( name) => Some ( name) ,
214+ Type :: ListType ( _) => None ,
215+ Type :: NonNullType ( of_type) => resolve_supported_type_name ( of_type) ,
216+ }
217+ }
218+
219+ let type_name = match ENV_VARS . graphql . disable_child_sorting {
220+ true => None ,
221+ false => resolve_supported_type_name ( & field. field_type ) ,
222+ } ;
223+
224+ Ok ( match type_name {
225+ Some ( name) => {
226+ let named_type = schema
227+ . get_named_type ( name)
228+ . ok_or_else ( || APISchemaError :: TypeNotFound ( name. clone ( ) ) ) ?;
229+ match named_type {
230+ TypeDefinition :: Object ( ObjectType { fields, .. } )
231+ | TypeDefinition :: Interface ( InterfaceType { fields, .. } ) => fields
232+ . iter ( )
233+ . filter_map ( |f| {
234+ enum_value_from_child_entity_field ( schema, field. name . as_str ( ) , f)
235+ } )
236+ . collect ( ) ,
237+ _ => vec ! [ ] ,
238+ }
239+ }
240+ None => vec ! [ ] ,
241+ } )
242+ }
243+
179244/// Adds a `<type_name>_filter` enum type for the given fields to the schema.
180245fn add_filter_type (
181246 schema : & mut Document ,
@@ -887,6 +952,180 @@ mod tests {
887952 assert_eq ! ( values, [ "id" , "name" ] ) ;
888953 }
889954
955+ #[ test]
956+ fn api_schema_contains_field_order_by_enum_for_child_entity ( ) {
957+ let input_schema = parse_schema (
958+ r#"
959+ enum FurType {
960+ NONE
961+ FLUFFY
962+ BRISTLY
963+ }
964+
965+ type Pet {
966+ id: ID!
967+ name: String!
968+ mostHatedBy: [User!]!
969+ mostLovedBy: [User!]!
970+ }
971+
972+ interface Recipe {
973+ id: ID!
974+ name: String!
975+ author: User!
976+ lovedBy: [User!]!
977+ ingredients: [String!]!
978+ }
979+
980+ type FoodRecipe implements Recipe {
981+ id: ID!
982+ name: String!
983+ author: User!
984+ ingredients: [String!]!
985+ }
986+
987+ type DrinkRecipe implements Recipe {
988+ id: ID!
989+ name: String!
990+ author: User!
991+ ingredients: [String!]!
992+ }
993+
994+ interface Meal {
995+ id: ID!
996+ name: String!
997+ mostHatedBy: [User!]!
998+ mostLovedBy: [User!]!
999+ }
1000+
1001+ type Pizza implements Meal {
1002+ id: ID!
1003+ name: String!
1004+ toppings: [String!]!
1005+ mostHatedBy: [User!]!
1006+ mostLovedBy: [User!]!
1007+ }
1008+
1009+ type Burger implements Meal {
1010+ id: ID!
1011+ name: String!
1012+ bun: String!
1013+ mostHatedBy: [User!]!
1014+ mostLovedBy: [User!]!
1015+ }
1016+
1017+ type User {
1018+ id: ID!
1019+ name: String!
1020+ favoritePetNames: [String!]
1021+ pets: [Pet!]!
1022+ favoriteFurType: FurType!
1023+ favoritePet: Pet!
1024+ leastFavoritePet: Pet @derivedFrom(field: "mostHatedBy")
1025+ mostFavoritePets: [Pet!] @derivedFrom(field: "mostLovedBy")
1026+ favoriteMeal: Meal!
1027+ leastFavoriteMeal: Meal @derivedFrom(field: "mostHatedBy")
1028+ mostFavoriteMeals: [Meal!] @derivedFrom(field: "mostLovedBy")
1029+ recipes: [Recipe!]! @derivedFrom(field: "author")
1030+ }
1031+ "# ,
1032+ )
1033+ . expect ( "Failed to parse input schema" ) ;
1034+ let schema = api_schema ( & input_schema) . expect ( "Failed to derived API schema" ) ;
1035+
1036+ let user_order_by = schema
1037+ . get_named_type ( "User_orderBy" )
1038+ . expect ( "User_orderBy type is missing in derived API schema" ) ;
1039+
1040+ let enum_type = match user_order_by {
1041+ TypeDefinition :: Enum ( t) => Some ( t) ,
1042+ _ => None ,
1043+ }
1044+ . expect ( "User_orderBy type is not an enum" ) ;
1045+
1046+ let values: Vec < & str > = enum_type
1047+ . values
1048+ . iter ( )
1049+ . map ( |value| value. name . as_str ( ) )
1050+ . collect ( ) ;
1051+
1052+ assert_eq ! (
1053+ values,
1054+ [
1055+ "id" ,
1056+ "name" ,
1057+ "favoritePetNames" ,
1058+ "pets" ,
1059+ "favoriteFurType" ,
1060+ "favoritePet" ,
1061+ "favoritePet__id" ,
1062+ "favoritePet__name" ,
1063+ "leastFavoritePet" ,
1064+ "leastFavoritePet__id" ,
1065+ "leastFavoritePet__name" ,
1066+ "mostFavoritePets" ,
1067+ "favoriteMeal" ,
1068+ "favoriteMeal__id" ,
1069+ "favoriteMeal__name" ,
1070+ "leastFavoriteMeal" ,
1071+ "leastFavoriteMeal__id" ,
1072+ "leastFavoriteMeal__name" ,
1073+ "mostFavoriteMeals" ,
1074+ "recipes" ,
1075+ ]
1076+ ) ;
1077+
1078+ let meal_order_by = schema
1079+ . get_named_type ( "Meal_orderBy" )
1080+ . expect ( "Meal_orderBy type is missing in derived API schema" ) ;
1081+
1082+ let enum_type = match meal_order_by {
1083+ TypeDefinition :: Enum ( t) => Some ( t) ,
1084+ _ => None ,
1085+ }
1086+ . expect ( "Meal_orderBy type is not an enum" ) ;
1087+
1088+ let values: Vec < & str > = enum_type
1089+ . values
1090+ . iter ( )
1091+ . map ( |value| value. name . as_str ( ) )
1092+ . collect ( ) ;
1093+
1094+ assert_eq ! ( values, [ "id" , "name" , "mostHatedBy" , "mostLovedBy" , ] ) ;
1095+
1096+ let recipe_order_by = schema
1097+ . get_named_type ( "Recipe_orderBy" )
1098+ . expect ( "Recipe_orderBy type is missing in derived API schema" ) ;
1099+
1100+ let enum_type = match recipe_order_by {
1101+ TypeDefinition :: Enum ( t) => Some ( t) ,
1102+ _ => None ,
1103+ }
1104+ . expect ( "Recipe_orderBy type is not an enum" ) ;
1105+
1106+ let values: Vec < & str > = enum_type
1107+ . values
1108+ . iter ( )
1109+ . map ( |value| value. name . as_str ( ) )
1110+ . collect ( ) ;
1111+
1112+ assert_eq ! (
1113+ values,
1114+ [
1115+ "id" ,
1116+ "name" ,
1117+ "author" ,
1118+ "author__id" ,
1119+ "author__name" ,
1120+ "author__favoriteFurType" ,
1121+ "author__favoritePet" ,
1122+ "author__leastFavoritePet" ,
1123+ "lovedBy" ,
1124+ "ingredients"
1125+ ]
1126+ ) ;
1127+ }
1128+
8901129 #[ test]
8911130 fn api_schema_contains_object_type_filter_enum ( ) {
8921131 let input_schema = parse_schema (
0 commit comments