3030
3131def graphql_version ():
3232 from graphql import __version__ as version
33+
3334 return tuple (int (v ) for v in version .split ("." ))
3435
3536
@@ -69,13 +70,13 @@ def wrap_executor_context_init(wrapped, instance, args, kwargs):
6970 instance .field_resolver = wrap_resolver (instance .field_resolver )
7071 instance .field_resolver ._nr_wrapped = True
7172
72-
7373 return result
7474
7575
7676def bind_operation_v3 (operation , root_value ):
7777 return operation
7878
79+
7980def bind_operation_v2 (exe_context , operation , root_value ):
8081 return operation
8182
@@ -105,12 +106,22 @@ def wrap_execute_operation(wrapped, instance, args, kwargs):
105106 if get_node_value (field , "name" ) in GRAPHQL_INTROSPECTION_FIELDS :
106107 ignore_transaction ()
107108
108- deepest_path = traverse_deepest_unique_path (fields )
109+ if graphql_version () <= (3 , 0 , 0 ):
110+ fragments = args [
111+ 0
112+ ].fragments # In v2, args[0] is the ExecutionContext object
113+ else :
114+ fragments = instance .fragments # instance is the ExecutionContext object
115+ deepest_path = traverse_deepest_unique_path (fields , fragments )
109116 trace .deepest_path = deepest_path = "." .join (deepest_path ) or ""
110117
111118 transaction .set_transaction_name (callable_name (wrapped ), "GraphQL" , priority = 11 )
112119 result = wrapped (* args , ** kwargs )
113- transaction_name = "%s/%s/%s" % (operation_type , operation_name , deepest_path ) if deepest_path else "%s/%s" % (operation_type , operation_name )
120+ transaction_name = (
121+ "%s/%s/%s" % (operation_type , operation_name , deepest_path )
122+ if deepest_path
123+ else "%s/%s" % (operation_type , operation_name )
124+ )
114125 transaction .set_transaction_name (transaction_name , "GraphQL" , priority = 14 )
115126
116127 return result
@@ -123,74 +134,121 @@ def get_node_value(field, attr, subattr="value"):
123134 return field_name
124135
125136
137+ def is_fragment_spread_node (field ):
138+ # Resolve version specific imports
139+ try :
140+ from graphql .language .ast import FragmentSpread
141+ except ImportError :
142+ from graphql import FragmentSpreadNode as FragmentSpread
143+
144+ return isinstance (field , FragmentSpread )
145+
146+
126147def is_fragment (field ):
127148 # Resolve version specific imports
128149 try :
129150 from graphql .language .ast import FragmentSpread , InlineFragment
130151 except ImportError :
131- from graphql import FragmentSpreadNode as FragmentSpread , InlineFragmentNode as InlineFragment
152+ from graphql import (
153+ FragmentSpreadNode as FragmentSpread ,
154+ InlineFragmentNode as InlineFragment ,
155+ )
132156
133157 _fragment_types = (InlineFragment , FragmentSpread )
134158
135159 return isinstance (field , _fragment_types )
136160
161+
137162def is_named_fragment (field ):
138163 # Resolve version specific imports
139164 try :
140165 from graphql .language .ast import NamedType
141166 except ImportError :
142167 from graphql import NamedTypeNode as NamedType
143168
144- return is_fragment (field ) and getattr (field , "type_condition" , None ) is not None and isinstance (field .type_condition , NamedType )
169+ return (
170+ is_fragment (field )
171+ and getattr (field , "type_condition" , None ) is not None
172+ and isinstance (field .type_condition , NamedType )
173+ )
145174
146175
147- def traverse_deepest_unique_path (fields ):
148- deepest_path = deque ()
176+ def filter_ignored_fields (fields ):
177+ filtered_fields = [
178+ f for f in fields if get_node_value (f , "name" ) not in GRAPHQL_IGNORED_FIELDS
179+ ]
180+ return filtered_fields
181+
149182
183+ def traverse_deepest_unique_path (fields , fragments ):
184+ deepest_path = deque ()
150185 while fields is not None and len (fields ) > 0 :
151- fields = [ f for f in fields if get_node_value ( f , "name" ) not in GRAPHQL_IGNORED_FIELDS ]
186+ fields = filter_ignored_fields ( fields )
152187 if len (fields ) != 1 : # Either selections is empty, or non-unique
153188 return deepest_path
154189 field = fields [0 ]
155-
156190 field_name = get_node_value (field , "name" )
191+ fragment_selection_set = []
192+
157193 if is_named_fragment (field ):
158194 name = get_node_value (field .type_condition , "name" )
159195 if name :
160196 deepest_path .append ("%s<%s>" % (deepest_path .pop (), name ))
197+
161198 elif is_fragment (field ):
162- break
199+ if len (list (fragments .values ())) != 1 :
200+ return deepest_path
201+
202+ # list(fragments.values())[0] 's index is OK because the previous line
203+ # ensures that there is only one field in the list
204+ full_fragment_selection_set = list (fragments .values ())[
205+ 0
206+ ].selection_set .selections
207+ fragment_selection_set = filter_ignored_fields (full_fragment_selection_set )
208+
209+ if len (fragment_selection_set ) != 1 :
210+ return deepest_path
211+ else :
212+ fragment_field_name = get_node_value (fragment_selection_set [0 ], "name" )
213+ deepest_path .append (fragment_field_name )
214+
163215 else :
164216 if field_name :
165217 deepest_path .append (field_name )
166218
219+ if is_fragment_spread_node (field ):
220+ field = fragment_selection_set [0 ]
167221 if field .selection_set is None :
168222 break
169223 else :
170224 fields = field .selection_set .selections
171225
172226 return deepest_path
173227
228+
174229def bind_get_middleware_resolvers (middlewares ):
175230 return middlewares
176231
177232
178233def wrap_get_middleware_resolvers (wrapped , instance , args , kwargs ):
179234 middlewares = bind_get_middleware_resolvers (* args , ** kwargs )
180- middlewares = [wrap_middleware (m ) if not hasattr (m , "_nr_wrapped" ) else m for m in middlewares ]
235+ middlewares = [
236+ wrap_middleware (m ) if not hasattr (m , "_nr_wrapped" ) else m for m in middlewares
237+ ]
181238 for m in middlewares :
182239 m ._nr_wrapped = True
183240
184241 return wrapped (middlewares )
185242
243+
186244@function_wrapper
187245def wrap_middleware (wrapped , instance , args , kwargs ):
188246 transaction = current_transaction ()
189247 if transaction is None :
190248 return wrapped (* args , ** kwargs )
191249
192250 name = callable_name (wrapped )
193- transaction .set_transaction_name (name , ' GraphQL' , priority = 12 )
251+ transaction .set_transaction_name (name , " GraphQL" , priority = 12 )
194252 with FunctionTrace (name ):
195253 with ErrorTrace (ignore = ignore_graphql_duplicate_exception ):
196254 return wrapped (* args , ** kwargs )
@@ -253,7 +311,7 @@ def wrap_validate(wrapped, instance, args, kwargs):
253311 if transaction is None :
254312 return wrapped (* args , ** kwargs )
255313
256- transaction .set_transaction_name (callable_name (wrapped ),"GraphQL" , priority = 10 )
314+ transaction .set_transaction_name (callable_name (wrapped ), "GraphQL" , priority = 10 )
257315
258316 # Run and collect errors
259317 errors = wrapped (* args , ** kwargs )
@@ -267,6 +325,7 @@ def wrap_validate(wrapped, instance, args, kwargs):
267325
268326 return errors
269327
328+
270329def wrap_parse (wrapped , instance , args , kwargs ):
271330 transaction = current_transaction ()
272331 if transaction is None :
@@ -281,7 +340,9 @@ def bind_resolve_field_v3(parent_type, source, field_nodes, path):
281340 return parent_type , field_nodes , path
282341
283342
284- def bind_resolve_field_v2 (exe_context , parent_type , source , field_asts , parent_info , field_path ):
343+ def bind_resolve_field_v2 (
344+ exe_context , parent_type , source , field_asts , parent_info , field_path
345+ ):
285346 return parent_type , field_asts , field_path
286347
287348
@@ -323,7 +384,8 @@ def bind_execute_graphql_query(
323384 operation_name = None ,
324385 middleware = None ,
325386 backend = None ,
326- ** execute_options ):
387+ ** execute_options
388+ ):
327389
328390 return request_string
329391
@@ -335,9 +397,9 @@ def wrap_graphql_impl(wrapped, instance, args, kwargs):
335397 return wrapped (* args , ** kwargs )
336398
337399 version = graphql_version ()
338- framework_version = '.' .join (map (str , version ))
400+ framework_version = "." .join (map (str , version ))
339401
340- transaction .add_framework_info (name = ' GraphQL' , version = framework_version )
402+ transaction .add_framework_info (name = " GraphQL" , version = framework_version )
341403
342404 if graphql_version () <= (3 , 0 , 0 ):
343405 bind_query = bind_execute_graphql_query
@@ -378,9 +440,8 @@ def instrument_graphql_execute(module):
378440 wrap_function_wrapper (module , "resolve_field" , wrap_resolve_field )
379441
380442 if hasattr (module , "execute_operation" ):
381- wrap_function_wrapper (
382- module , "execute_operation" , wrap_execute_operation
383- )
443+ wrap_function_wrapper (module , "execute_operation" , wrap_execute_operation )
444+
384445
385446def instrument_graphql_execution_utils (module ):
386447 if hasattr (module , "ExecutionContext" ):
0 commit comments