1
1
use std:: str:: FromStr ;
2
2
3
- use graphql_parser:: Pos ;
3
+ use graphql_parser:: { schema :: TypeDefinition , Pos } ;
4
4
use inflector:: Inflector ;
5
5
use lazy_static:: lazy_static;
6
6
@@ -157,16 +157,7 @@ fn add_order_by_type(
157
157
description : None ,
158
158
name : type_name,
159
159
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) ?,
170
161
} ) ;
171
162
let def = Definition :: TypeDefinition ( typedef) ;
172
163
schema. definitions . push ( def) ;
@@ -176,6 +167,80 @@ fn add_order_by_type(
176
167
Ok ( ( ) )
177
168
}
178
169
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
+
179
244
/// Adds a `<type_name>_filter` enum type for the given fields to the schema.
180
245
fn add_filter_type (
181
246
schema : & mut Document ,
@@ -887,6 +952,180 @@ mod tests {
887
952
assert_eq ! ( values, [ "id" , "name" ] ) ;
888
953
}
889
954
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
+
890
1129
#[ test]
891
1130
fn api_schema_contains_object_type_filter_enum ( ) {
892
1131
let input_schema = parse_schema (
0 commit comments