prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN 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>
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `elasticapm/instrumentation/packages/graphql.py`
  Content:
  ```
  1 #  BSD 3-Clause License
  2 #
  3 #  Copyright (c) 2019, Elasticsearch BV
  4 #  All rights reserved.
  5 #
  6 #  Redistribution and use in source and binary forms, with or without
  7 #  modification, are permitted provided that the following conditions are met:
  8 #
  9 #  * Redistributions of source code must retain the above copyright notice, this
  10 #    list of conditions and the following disclaimer.
  11 #
  12 #  * Redistributions in binary form must reproduce the above copyright notice,
  13 #    this list of conditions and the following disclaimer in the documentation
  14 #    and/or other materials provided with the distribution.
  15 #
  16 #  * Neither the name of the copyright holder nor the names of its
  17 #    contributors may be used to endorse or promote products derived from
  18 #    this software without specific prior written permission.
  19 #
  20 #  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  21 #  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22 #  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  23 #  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  24 #  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25 #  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  26 #  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  27 #  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  28 #  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29 #  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30 
  31 from elasticapm import set_transaction_name
  32 from elasticapm.instrumentation.packages.base import AbstractInstrumentedModule
  33 from elasticapm.traces import capture_span
  34 
  35 
  36 class GraphQLExecutorInstrumentation(AbstractInstrumentedModule):
  37     name = "graphql"
  38 
  39     instrument_list = [
  40         ("graphql.execution.executors.sync", "SyncExecutor.execute"),
  41         ("graphql.execution.executors.gevent", "GeventExecutor.execute"),
  42         ("graphql.execution.executors.asyncio", "AsyncioExecutor.execute"),
  43         ("graphql.execution.executors.process", "ProcessExecutor.execute"),
  44         ("graphql.execution.executors.thread", "ThreadExecutor.execute_in_thread"),
  45         ("graphql.execution.executors.thread", "ThreadExecutor.execute_in_pool"),
  46     ]
  47 
  48     def call(self, module, method, wrapped, instance, args, kwargs):
  49         name = "GraphQL"
  50 
  51         info = ""
  52         query = args[2]
  53 
  54         if "ResolveInfo" == type(query).__name__:
  55             if str(query.return_type) in [
  56                     'Boolean',
  57                     'Context',
  58                     'Date',
  59                     'DateTime',
  60                     'Decimal',
  61                     'Dynamic',
  62                     'Float',
  63                     'ID',
  64                     'Int',
  65                     'String',
  66                     'Time',
  67                     'UUID',
  68                     'Boolean',
  69                     'String'
  70             ]:
  71                 return wrapped(*args, **kwargs)
  72 
  73             op = query.operation.operation
  74             field = query.field_name
  75             info = "%s %s" % (op, field)
  76         elif "RequestParams" == type(query).__name__:
  77             info = "%s %s" % ("request", query.query)
  78         else:
  79             info = str(query)
  80 
  81         with capture_span(
  82                 "%s.%s" % (name, info),
  83                 span_type="external",
  84                 span_subtype="graphql",
  85                 span_action="query"
  86         ):
  87             return wrapped(*args, **kwargs)
  88 
  89 
  90 class GraphQLBackendInstrumentation(AbstractInstrumentedModule):
  91     name = "graphql"
  92 
  93     instrument_list = [
  94         ("graphql.backend.core", "GraphQLCoreBackend.document_from_string"),
  95         ("graphql.backend.cache", "GraphQLCachedBackend.document_from_string"),
  96     ]
  97 
  98     def get_graphql_tx_name(self, graphql_doc):
  99         op = graphql_doc.definitions[0].operation
  100         fields = graphql_doc.definitions[0].selection_set.selections
  101         return "GraphQL %s %s" % (op.upper(), "+".join([f.name.value for f in fields]))
  102 
  103     def call(self, module, method, wrapped, instance, args, kwargs):
  104         graphql_document = wrapped(*args, **kwargs)
  105         transaction_name = self.get_graphql_tx_name(graphql_document.document_ast)
  106         set_transaction_name(transaction_name)
  107         return graphql_document
  108
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: 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 @@
   
           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 @@
           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 @@
       ]
   
       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)
  
verification_info: {"golden_diff": "diff --git a/elasticapm/instrumentation/packages/graphql.py b/elasticapm/instrumentation/packages/graphql.py\n--- a/elasticapm/instrumentation/packages/graphql.py\n+++ b/elasticapm/instrumentation/packages/graphql.py\n@@ -53,20 +53,20 @@\n \n         if \"ResolveInfo\" == type(query).__name__:\n             if str(query.return_type) in [\n-                    'Boolean',\n-                    'Context',\n-                    'Date',\n-                    'DateTime',\n-                    'Decimal',\n-                    'Dynamic',\n-                    'Float',\n-                    'ID',\n-                    'Int',\n-                    'String',\n-                    'Time',\n-                    'UUID',\n-                    'Boolean',\n-                    'String'\n+                \"Boolean\",\n+                \"Context\",\n+                \"Date\",\n+                \"DateTime\",\n+                \"Decimal\",\n+                \"Dynamic\",\n+                \"Float\",\n+                \"ID\",\n+                \"Int\",\n+                \"String\",\n+                \"Time\",\n+                \"UUID\",\n+                \"Boolean\",\n+                \"String\",\n             ]:\n                 return wrapped(*args, **kwargs)\n \n@@ -78,12 +78,7 @@\n         else:\n             info = str(query)\n \n-        with capture_span(\n-                \"%s.%s\" % (name, info),\n-                span_type=\"external\",\n-                span_subtype=\"graphql\",\n-                span_action=\"query\"\n-        ):\n+        with capture_span(\"%s.%s\" % (name, info), span_type=\"external\", span_subtype=\"graphql\", span_action=\"query\"):\n             return wrapped(*args, **kwargs)\n \n \n@@ -96,9 +91,15 @@\n     ]\n \n     def get_graphql_tx_name(self, graphql_doc):\n-        op = graphql_doc.definitions[0].operation\n-        fields = graphql_doc.definitions[0].selection_set.selections\n-        return \"GraphQL %s %s\" % (op.upper(), \"+\".join([f.name.value for f in fields]))\n+        try:\n+            op_def = [i for i in graphql_doc.definitions if type(i).__name__ == \"OperationDefinition\"][0]\n+        except KeyError:\n+            return \"GraphQL unknown operation\"\n+\n+        op = op_def.operation\n+        name = op_def.name\n+        fields = op_def.selection_set.selections\n+        return \"GraphQL %s %s\" % (op.upper(), name if name else \"+\".join([f.name.value for f in fields]))\n \n     def call(self, module, method, wrapped, instance, args, kwargs):\n         graphql_document = wrapped(*args, **kwargs)\n", "issue": "'FragmentDefinition' object has no attribute 'operation'\"\n**Describe the bug**: \r\nI'm using elastic APM with Django 3.1.2 and graphql.\r\nOn every GraphQL Query, I'm seeing now this error: `AttributeError: 'FragmentDefinition' object has no attribute 'operation'`\r\n\r\nThe relevant file is: `elasticapm/instrumentation/packages/graphql.py in get_graphql_tx_name at line 99`\r\n\r\n**To Reproduce**\r\nI'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.\r\n\r\n**Environment (please complete the following information)**\r\n- OS: Linux docker Container\r\n- Python version:\r\n- Framework and version : Django 3.1.2\r\n- APM Server version: \r\n- Agent version: 5.9.0\r\n\r\n\r\n**Additional context**\r\n\r\nAdd any other context about the problem here.\r\n\r\n- Agent config options <!-- be careful not to post sensitive information -->\r\n  <details>\r\n    <summary>Click to expand</summary>\r\n\r\n    ```\r\n    replace this line with your agent config options\r\n    remember to mask any sensitive fields like tokens\r\n    ```\r\n  </details>\r\n- `requirements.txt`:\r\n  <details>\r\n    <summary>Click to expand</summary>\r\n\r\n    ```\r\n    replace this line with your `requirements.txt`\r\n    ```\r\n  </details>\r\n\n", "before_files": [{"content": "#  BSD 3-Clause License\n#\n#  Copyright (c) 2019, Elasticsearch BV\n#  All rights reserved.\n#\n#  Redistribution and use in source and binary forms, with or without\n#  modification, are permitted provided that the following conditions are met:\n#\n#  * Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#\n#  * Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n#  * Neither the name of the copyright holder nor the names of its\n#    contributors may be used to endorse or promote products derived from\n#    this software without specific prior written permission.\n#\n#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nfrom elasticapm import set_transaction_name\nfrom elasticapm.instrumentation.packages.base import AbstractInstrumentedModule\nfrom elasticapm.traces import capture_span\n\n\nclass GraphQLExecutorInstrumentation(AbstractInstrumentedModule):\n    name = \"graphql\"\n\n    instrument_list = [\n        (\"graphql.execution.executors.sync\", \"SyncExecutor.execute\"),\n        (\"graphql.execution.executors.gevent\", \"GeventExecutor.execute\"),\n        (\"graphql.execution.executors.asyncio\", \"AsyncioExecutor.execute\"),\n        (\"graphql.execution.executors.process\", \"ProcessExecutor.execute\"),\n        (\"graphql.execution.executors.thread\", \"ThreadExecutor.execute_in_thread\"),\n        (\"graphql.execution.executors.thread\", \"ThreadExecutor.execute_in_pool\"),\n    ]\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        name = \"GraphQL\"\n\n        info = \"\"\n        query = args[2]\n\n        if \"ResolveInfo\" == type(query).__name__:\n            if str(query.return_type) in [\n                    'Boolean',\n                    'Context',\n                    'Date',\n                    'DateTime',\n                    'Decimal',\n                    'Dynamic',\n                    'Float',\n                    'ID',\n                    'Int',\n                    'String',\n                    'Time',\n                    'UUID',\n                    'Boolean',\n                    'String'\n            ]:\n                return wrapped(*args, **kwargs)\n\n            op = query.operation.operation\n            field = query.field_name\n            info = \"%s %s\" % (op, field)\n        elif \"RequestParams\" == type(query).__name__:\n            info = \"%s %s\" % (\"request\", query.query)\n        else:\n            info = str(query)\n\n        with capture_span(\n                \"%s.%s\" % (name, info),\n                span_type=\"external\",\n                span_subtype=\"graphql\",\n                span_action=\"query\"\n        ):\n            return wrapped(*args, **kwargs)\n\n\nclass GraphQLBackendInstrumentation(AbstractInstrumentedModule):\n    name = \"graphql\"\n\n    instrument_list = [\n        (\"graphql.backend.core\", \"GraphQLCoreBackend.document_from_string\"),\n        (\"graphql.backend.cache\", \"GraphQLCachedBackend.document_from_string\"),\n    ]\n\n    def get_graphql_tx_name(self, graphql_doc):\n        op = graphql_doc.definitions[0].operation\n        fields = graphql_doc.definitions[0].selection_set.selections\n        return \"GraphQL %s %s\" % (op.upper(), \"+\".join([f.name.value for f in fields]))\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        graphql_document = wrapped(*args, **kwargs)\n        transaction_name = self.get_graphql_tx_name(graphql_document.document_ast)\n        set_transaction_name(transaction_name)\n        return graphql_document\n", "path": "elasticapm/instrumentation/packages/graphql.py"}], "after_files": [{"content": "#  BSD 3-Clause License\n#\n#  Copyright (c) 2019, Elasticsearch BV\n#  All rights reserved.\n#\n#  Redistribution and use in source and binary forms, with or without\n#  modification, are permitted provided that the following conditions are met:\n#\n#  * Redistributions of source code must retain the above copyright notice, this\n#    list of conditions and the following disclaimer.\n#\n#  * Redistributions in binary form must reproduce the above copyright notice,\n#    this list of conditions and the following disclaimer in the documentation\n#    and/or other materials provided with the distribution.\n#\n#  * Neither the name of the copyright holder nor the names of its\n#    contributors may be used to endorse or promote products derived from\n#    this software without specific prior written permission.\n#\n#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n#  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n#  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n#  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n#  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n#  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n#  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nfrom elasticapm import set_transaction_name\nfrom elasticapm.instrumentation.packages.base import AbstractInstrumentedModule\nfrom elasticapm.traces import capture_span\n\n\nclass GraphQLExecutorInstrumentation(AbstractInstrumentedModule):\n    name = \"graphql\"\n\n    instrument_list = [\n        (\"graphql.execution.executors.sync\", \"SyncExecutor.execute\"),\n        (\"graphql.execution.executors.gevent\", \"GeventExecutor.execute\"),\n        (\"graphql.execution.executors.asyncio\", \"AsyncioExecutor.execute\"),\n        (\"graphql.execution.executors.process\", \"ProcessExecutor.execute\"),\n        (\"graphql.execution.executors.thread\", \"ThreadExecutor.execute_in_thread\"),\n        (\"graphql.execution.executors.thread\", \"ThreadExecutor.execute_in_pool\"),\n    ]\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        name = \"GraphQL\"\n\n        info = \"\"\n        query = args[2]\n\n        if \"ResolveInfo\" == type(query).__name__:\n            if str(query.return_type) in [\n                \"Boolean\",\n                \"Context\",\n                \"Date\",\n                \"DateTime\",\n                \"Decimal\",\n                \"Dynamic\",\n                \"Float\",\n                \"ID\",\n                \"Int\",\n                \"String\",\n                \"Time\",\n                \"UUID\",\n                \"Boolean\",\n                \"String\",\n            ]:\n                return wrapped(*args, **kwargs)\n\n            op = query.operation.operation\n            field = query.field_name\n            info = \"%s %s\" % (op, field)\n        elif \"RequestParams\" == type(query).__name__:\n            info = \"%s %s\" % (\"request\", query.query)\n        else:\n            info = str(query)\n\n        with capture_span(\"%s.%s\" % (name, info), span_type=\"external\", span_subtype=\"graphql\", span_action=\"query\"):\n            return wrapped(*args, **kwargs)\n\n\nclass GraphQLBackendInstrumentation(AbstractInstrumentedModule):\n    name = \"graphql\"\n\n    instrument_list = [\n        (\"graphql.backend.core\", \"GraphQLCoreBackend.document_from_string\"),\n        (\"graphql.backend.cache\", \"GraphQLCachedBackend.document_from_string\"),\n    ]\n\n    def get_graphql_tx_name(self, graphql_doc):\n        try:\n            op_def = [i for i in graphql_doc.definitions if type(i).__name__ == \"OperationDefinition\"][0]\n        except KeyError:\n            return \"GraphQL unknown operation\"\n\n        op = op_def.operation\n        name = op_def.name\n        fields = op_def.selection_set.selections\n        return \"GraphQL %s %s\" % (op.upper(), name if name else \"+\".join([f.name.value for f in fields]))\n\n    def call(self, module, method, wrapped, instance, args, kwargs):\n        graphql_document = wrapped(*args, **kwargs)\n        transaction_name = self.get_graphql_tx_name(graphql_document.document_ast)\n        set_transaction_name(transaction_name)\n        return graphql_document\n", "path": "elasticapm/instrumentation/packages/graphql.py"}]}