Browse Source

Better support for request query string

David Burke 3 years ago
parent
commit
88c4c4eb7f

+ 28 - 0
events/fields.py

@@ -1,4 +1,5 @@
 from collections import OrderedDict
+from urllib.parse import parse_qs
 from typing import List
 import re
 from rest_framework import serializers
@@ -74,3 +75,30 @@ class ForgivingDisallowRegexField(ForgivingFieldMixin, serializers.CharField):
                 self.update_handled_errors_context([error])
                 return None
         return data
+
+
+class QueryStringField(serializers.ListField):
+    """
+    Can be given as unparsed string, dictionary, or list of tuples
+    Should store as List[List[str]] where inner List is always of length 2
+    """
+
+    child = serializers.ListField(child=serializers.CharField())
+
+    def to_internal_value(self, data):
+        if isinstance(data, str) and data:
+            qs = parse_qs(data)
+            result = []
+            for key, values in qs.items():
+                for value in values:
+                    result.append([key, value])
+            return result
+        elif isinstance(data, dict):
+            return [[key, value] for key, value in data.items()]
+        elif isinstance(data, list):
+            result = []
+            for item in data:
+                if isinstance(item, list) and len(item) >= 2:
+                    result.append(item[:2])
+            return result
+        return None

+ 7 - 4
events/serializers.py

@@ -14,7 +14,12 @@ from environments.models import Environment
 from releases.models import Release
 from glitchtip.serializers import FlexibleDateTimeField
 from .models import Event, LogLevel
-from .fields import GenericField, ForgivingHStoreField, ForgivingDisallowRegexField
+from .fields import (
+    GenericField,
+    ForgivingHStoreField,
+    ForgivingDisallowRegexField,
+    QueryStringField,
+)
 from .event_tag_processors import TAG_PROCESSORS
 from .event_context_processors import EVENT_CONTEXT_PROCESSORS
 
@@ -58,9 +63,7 @@ class RequestSerializer(serializers.Serializer):
     headers = serializers.DictField(required=False)
     url = serializers.CharField(required=False, allow_blank=True)
     method = serializers.CharField(required=False, allow_blank=True)
-    query_string = serializers.CharField(
-        required=False, allow_blank=True, allow_null=True
-    )
+    query_string = QueryStringField(required=False, allow_null=True)
 
 
 class BreadcrumbsSerializer(BaseBreadcrumbsSerializer):

+ 1 - 1
events/test_data/incoming_events/python_zero_division.json

@@ -227,7 +227,7 @@
   "extra": { "sys.argv": ["./manage.py", "runserver", "0.0.0.0:8001"] },
   "request": {
     "url": "http://localhost:8001/divide-zero/",
-    "query_string": "",
+    "query_string": "foo=bar&foo=bars&lol=ha",
     "method": "GET",
     "env": { "SERVER_NAME": "2f96b0310f37", "SERVER_PORT": "8001" },
     "headers": {

+ 5 - 1
events/test_data/oss_sentry_events/python_zero_division.json

@@ -377,7 +377,11 @@
           ]
         ],
         "url": "http://localhost:8001/divide-zero/",
-        "query": [],
+        "query": [
+          ["foo", "bar"],
+          ["foo", "bars"],
+          ["lol", "ha"]
+        ],
         "data": null,
         "method": "GET"
       }

+ 5 - 0
events/test_data/oss_sentry_json/python_zero_division.json

@@ -278,6 +278,11 @@
     ],
     "method": "GET",
     "env": { "SERVER_NAME": "2f96b0310f37", "SERVER_PORT": "8001" },
+    "query_string": [
+      ["foo", "bar"],
+      ["foo", "bars"],
+      ["lol", "ha"]
+    ],
     "inferred_content_type": "text/plain"
   },
   "sdk": {

+ 26 - 0
events/tests/tests.py

@@ -367,3 +367,29 @@ class EventStoreTestCase(APITestCase):
         res = self.client.post(self.url, data, format="json")
         self.assertTrue(Event.objects.filter().exists())
         self.assertFalse(Environment.objects.exists())
+
+    def test_query_string_formats(self):
+        data = {
+            "event_id": "11111111111111111111111111111111",
+            "exception": [{"type": "a", "value": "a", "module": None,}],
+            "request": {"method": "GET", "query_string": {"search": "foo"},},
+        }
+        self.client.post(self.url, data, format="json")
+        data = {
+            "event_id": "11111111111111111111111111111112",
+            "exception": [{"type": "a", "value": "a", "module": None,}],
+            "request": {"query_string": "search=foo",},
+        }
+        self.client.post(self.url, data, format="json")
+        data = {
+            "event_id": "11111111111111111111111111111113",
+            "exception": [{"type": "a", "value": "a", "module": None,}],
+            "request": {"query_string": [["search", "foo"]]},
+        }
+        self.client.post(self.url, data, format="json")
+        self.assertEqual(
+            Event.objects.filter(
+                data__request__query_string=[["search", "foo"]]
+            ).count(),
+            3,
+        )

+ 12 - 0
issues/tests/test_sentry_api_compat.py

@@ -349,6 +349,18 @@ class SentryAPICompatTestCase(GlitchTipTestCase):
                 "breadcrumbs",
             ],
         )
+        self.assertCompareData(
+            event_json["request"],
+            sentry_json["request"],
+            [
+                "url",
+                # "headers",
+                "method",
+                "env",
+                "query_string",
+                "inferred_content_type",
+            ],
+        )
         self.assertEqual(
             event_json["datetime"][:22],
             sentry_json["datetime"][:22],