Repository: elastic/apm-agent-python

Issue:
'FragmentDefinition' object has no attribute 'operation'"
**Describe the bug**: 
I'm using elastic APM with Django 3.1.2 and graphql.
On every GraphQL Query, I'm seeing now this error: `AttributeError: 'FragmentDefinition' object has no attribute 'operation'`

The relevant file is: `elasticapm/instrumentation/packages/graphql.py in get_graphql_tx_name at line 99`

**To Reproduce**
I'm not sure yet, why the error is occurring and I'm just getting started with the service. If you can guide me to the right direction, I can create a reproducible example.

**Environment (please complete the following information)**
- OS: Linux docker Container
- Python version:
- Framework and version : Django 3.1.2
- APM Server version: 
- Agent version: 5.9.0


**Additional context**

Add any other context about the problem here.

- Agent config options <!-- be careful not to post sensitive information -->
  <details>
    <summary>Click to expand</summary>

    ```
    replace this line with your agent config options
    remember to mask any sensitive fields like tokens
    ```
  </details>
- `requirements.txt`:
  <details>
    <summary>Click to expand</summary>

    ```
    replace this line with your `requirements.txt`
    ```
  </details>



Discussion:

Resolving patch:
diff --git a/elasticapm/instrumentation/packages/graphql.py b/elasticapm/instrumentation/packages/graphql.py
--- a/elasticapm/instrumentation/packages/graphql.py
+++ b/elasticapm/instrumentation/packages/graphql.py
@@ -53,20 +53,20 @@ def call(self, module, method, wrapped, instance, args, kwargs):
 
         if "ResolveInfo" == type(query).__name__:
             if str(query.return_type) in [
-                    'Boolean',
-                    'Context',
-                    'Date',
-                    'DateTime',
-                    'Decimal',
-                    'Dynamic',
-                    'Float',
-                    'ID',
-                    'Int',
-                    'String',
-                    'Time',
-                    'UUID',
-                    'Boolean',
-                    'String'
+                "Boolean",
+                "Context",
+                "Date",
+                "DateTime",
+                "Decimal",
+                "Dynamic",
+                "Float",
+                "ID",
+                "Int",
+                "String",
+                "Time",
+                "UUID",
+                "Boolean",
+                "String",
             ]:
                 return wrapped(*args, **kwargs)
 
@@ -78,12 +78,7 @@ def call(self, module, method, wrapped, instance, args, kwargs):
         else:
             info = str(query)
 
-        with capture_span(
-                "%s.%s" % (name, info),
-                span_type="external",
-                span_subtype="graphql",
-                span_action="query"
-        ):
+        with capture_span("%s.%s" % (name, info), span_type="external", span_subtype="graphql", span_action="query"):
             return wrapped(*args, **kwargs)
 
 
@@ -96,9 +91,15 @@ class GraphQLBackendInstrumentation(AbstractInstrumentedModule):
     ]
 
     def get_graphql_tx_name(self, graphql_doc):
-        op = graphql_doc.definitions[0].operation
-        fields = graphql_doc.definitions[0].selection_set.selections
-        return "GraphQL %s %s" % (op.upper(), "+".join([f.name.value for f in fields]))
+        try:
+            op_def = [i for i in graphql_doc.definitions if type(i).__name__ == "OperationDefinition"][0]
+        except KeyError:
+            return "GraphQL unknown operation"
+
+        op = op_def.operation
+        name = op_def.name
+        fields = op_def.selection_set.selections
+        return "GraphQL %s %s" % (op.upper(), name if name else "+".join([f.name.value for f in fields]))
 
     def call(self, module, method, wrapped, instance, args, kwargs):
         graphql_document = wrapped(*args, **kwargs)


Test patch: