@@ -25,21 +25,17 @@ class GraphQLServerOptions:
2525class GraphQLServer :
2626 def __init__ (self , mapping : Mapping ):
2727 self ._mapping = mapping
28- self ._field_dict : dict [str , list [StrawberryField ]] = {
29- "Query" : [],
30- "Mutation" : [],
31- }
28+ self ._fields_tree : FieldsTree = FieldsTree ("" )
3229 self ._app = self ._create_app ()
3330
3431 def _create_app (self ) -> FastAPI :
35- _add_dev_attributes (self ._field_dict , self ._mapping )
36- _add_dev_commands (self ._field_dict , self ._mapping )
32+ _add_dev_attributes (self ._fields_tree , self ._mapping )
33+ _add_dev_commands (self ._fields_tree , self ._mapping )
3734
3835 schema_kwargs = {}
39- for key , value in self ._field_dict .items ():
40- if self ._field_dict [key ]:
41- # Strawberry types map to graphql object
42- schema_kwargs [key .lower ()] = create_type (key , value )
36+ for key in ["query" , "mutation" ]:
37+ if s_type := self ._fields_tree .create_type (key ):
38+ schema_kwargs [key ] = s_type
4339 schema = strawberry .Schema (** schema_kwargs ) # type: ignore
4440 graphql_app : GraphQL = GraphQL (schema )
4541
@@ -90,36 +86,94 @@ async def _dynamic_f() -> Any:
9086 return _dynamic_f
9187
9288
93- def _add_dev_attributes (
94- field_dict : dict [str , list [StrawberryField ]], mapping : Mapping
95- ) -> None :
96- for single_mapping in mapping .get_controller_mappings ():
97- path = single_mapping .controller .path
98- # nest for each controller
99- # if path:
89+ def _wrap_as_field (
90+ field_name : str ,
91+ strawberry_type : type ,
92+ ) -> StrawberryField :
93+ def _dynamic_field ():
94+ return strawberry_type ()
10095
101- for attr_name , attribute in single_mapping .attributes .items ():
102- attr_name = attr_name .title ().replace ("_" , "" )
103- d_attr_name = f"{ '/' .join (path )} /{ attr_name } " if path else attr_name
96+ _dynamic_field .__name__ = field_name
97+ _dynamic_field .__annotations__ ["return" ] = strawberry_type
10498
105- match attribute :
106- case AttrRW ():
107- field_dict ["Query" ].append (
108- strawberry .field (_wrap_attr_get (d_attr_name , attribute ))
109- )
110- field_dict ["Mutation" ].append (
111- strawberry .mutation (_wrap_attr_set (d_attr_name , attribute ))
112- ) # mutation for server changes https://graphql.org/learn/queries/
99+ return strawberry .field (_dynamic_field )
113100
114- case AttrR ():
115- field_dict ["Query" ].append (
116- strawberry .field (_wrap_attr_get (d_attr_name , attribute ))
117- )
118101
119- case AttrW ():
120- field_dict ["Mutation" ].append (
121- strawberry .mutation (_wrap_attr_set (d_attr_name , attribute ))
122- )
102+ class FieldsTree :
103+ def __init__ (self , name : str ):
104+ self .name = name
105+ self .children : list [FieldsTree ] = []
106+ self .fields_dict : dict [str , list [StrawberryField ]] = {
107+ "query" : [],
108+ "mutation" : [],
109+ }
110+
111+ def insert (self , path : list [str ]):
112+ # Create child if not exist
113+ name = path .pop (0 )
114+ if not (child := self .get_child (name )):
115+ child = FieldsTree (name )
116+ self .children .append (child )
117+ else :
118+ child = self .get_child (name )
119+
120+ # Recurse if needed
121+ if path :
122+ return child .insert (path ) # type: ignore
123+ else :
124+ return child
125+
126+ def get_child (self , name : str ):
127+ for child in self .children :
128+ if child .name == name :
129+ return child
130+ return None
131+
132+ def create_type (self , strawberry_type : str ):
133+ for child in self .children :
134+ child_field = _wrap_as_field (
135+ child .name ,
136+ child .create_type (strawberry_type ),
137+ )
138+ self .fields_dict [strawberry_type ].append (child_field )
139+
140+ return create_type (
141+ f"{ self .name } { strawberry_type } " , self .fields_dict [strawberry_type ]
142+ )
143+
144+
145+ def _add_dev_attributes (
146+ fields_tree : FieldsTree ,
147+ mapping : Mapping ,
148+ ) -> None :
149+ for single_mapping in mapping .get_controller_mappings ():
150+ path = single_mapping .controller .path
151+ if path :
152+ node = fields_tree .insert (path )
153+ else :
154+ node = fields_tree
155+
156+ if node is not None :
157+ for attr_name , attribute in single_mapping .attributes .items ():
158+ attr_name = attr_name .title ().replace ("_" , "" )
159+
160+ match attribute :
161+ # mutation for server changes https://graphql.org/learn/queries/
162+ case AttrRW ():
163+ node .fields_dict ["query" ].append (
164+ strawberry .field (_wrap_attr_get (attr_name , attribute ))
165+ )
166+ node .fields_dict ["mutation" ].append (
167+ strawberry .mutation (_wrap_attr_set (attr_name , attribute ))
168+ )
169+ case AttrR ():
170+ node .fields_dict ["query" ].append (
171+ strawberry .field (_wrap_attr_get (attr_name , attribute ))
172+ )
173+ case AttrW ():
174+ node .fields_dict ["mutation" ].append (
175+ strawberry .mutation (_wrap_attr_set (attr_name , attribute ))
176+ )
123177
124178
125179def _wrap_command (
@@ -135,20 +189,25 @@ async def _dynamic_f() -> bool:
135189
136190
137191def _add_dev_commands (
138- field_dict : dict [str , list [StrawberryField ]], mapping : Mapping
192+ fields_tree : FieldsTree ,
193+ mapping : Mapping ,
139194) -> None :
140195 for single_mapping in mapping .get_controller_mappings ():
141196 path = single_mapping .controller .path
142-
143- for name , method in single_mapping .command_methods .items ():
144- cmd_name = name .title ().replace ("_" , "" )
145- d_cmd_name = f"{ '/' .join (path )} /{ cmd_name } " if path else cmd_name
146- field_dict ["Mutation" ].append (
147- strawberry .mutation (
148- _wrap_command (
149- d_cmd_name ,
150- method .fn ,
151- single_mapping .controller ,
197+ if path :
198+ node = fields_tree .insert (path )
199+ else :
200+ node = fields_tree
201+
202+ if node is not None :
203+ for name , method in single_mapping .command_methods .items ():
204+ cmd_name = name .title ().replace ("_" , "" )
205+ node .fields_dict ["mutation" ].append (
206+ strawberry .mutation (
207+ _wrap_command (
208+ cmd_name ,
209+ method .fn ,
210+ single_mapping .controller ,
211+ )
152212 )
153213 )
154- )
0 commit comments