Browse Source

Added sample data and moved event_store to it's own app

David Burke 5 years ago
parent
commit
2fc8f66af4

+ 0 - 0
event_store/__init__.py


+ 63 - 0
event_store/serializers.py

@@ -0,0 +1,63 @@
+from rest_framework import serializers
+from issues.event_store.error import ErrorEvent
+from issues.models import EventType, EventTag, Event, Issue
+
+
+class StoreDefaultSerializer(serializers.Serializer):
+    type = EventType.DEFAULT
+    breadcrumbs = serializers.JSONField()
+    contexts = serializers.JSONField(required=False)
+    event_id = serializers.UUIDField()
+    extra = serializers.JSONField(required=False)
+    level = serializers.CharField()
+    message = serializers.CharField(required=False)
+    platform = serializers.CharField()
+    release = serializers.CharField(required=False)
+    sdk = serializers.JSONField()
+    timestamp = serializers.DateTimeField(required=False)
+    modules = serializers.JSONField(required=False)
+
+    def create(self, project, data):
+        error = ErrorEvent()
+
+
+class StoreErrorSerializer(StoreDefaultSerializer):
+    type = EventType.ERROR
+    exception = serializers.JSONField(required=False)
+    request = serializers.JSONField(required=False)
+
+    def create(self, project, data):
+        error = ErrorEvent()
+        metadata = error.get_metadata(data)
+        issue, _ = Issue.objects.get_or_create(
+            title=error.get_title(metadata),
+            culprit=error.get_location(metadata),
+            project=project,
+        )
+
+        level_tag, _ = EventTag.objects.get_or_create(key="level", value=data["level"])
+        # release tag
+        breadcrumbs = data.get("breadcrumbs")
+        entries = [{"type": "breadcrumbs"}, {"data": {"values": breadcrumbs}}]
+        params = {
+            "event_id": data["event_id"],
+            "platform": data["platform"],
+            "sdk": data["sdk"],
+            "entries": entries,
+            "issue": issue,
+        }
+        if data.get("contexts"):
+            params["contexts"] = data["contexts"]
+        if data.get("context"):
+            params["context"] = data["extra"]
+        if data.get("modules"):
+            params["packages"] = data["modules"]
+
+        event = Event.objects.create(**params)
+        event.tags.add(level_tag)
+
+
+class StoreCSPReportSerializer(serializers.Serializer):
+    """ Very different format from others """
+
+    type = EventType.CSP

+ 0 - 0
event_store/test_data/__init__.py


+ 2112 - 0
event_store/test_data/django_error_factory.py

@@ -0,0 +1,2112 @@
+divide_zero = {
+    "level": "error",
+    "exception": {
+        "values": [
+            {
+                "module": None,
+                "type": "ZeroDivisionError",
+                "value": "division by zero",
+                "mechanism": {"type": "django", "handled": False},
+                "stacktrace": {
+                    "frames": [
+                        {
+                            "filename": "django/core/handlers/exception.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py",
+                            "function": "inner",
+                            "module": "django.core.handlers.exception",
+                            "lineno": 34,
+                            "pre_context": [
+                                "    can rely on getting a response instead of an exception.",
+                                '    """',
+                                "    @wraps(get_response)",
+                                "    def inner(request):",
+                                "        try:",
+                            ],
+                            "context_line": "            response = get_response(request)",
+                            "post_context": [
+                                "        except Exception as exc:",
+                                "            response = response_for_exception(request, exc)",
+                                "        return response",
+                                "    return inner",
+                                "",
+                            ],
+                            "vars": {
+                                "request": "<WSGIRequest: GET '/divide-zero/'>",
+                                "exc": "ZeroDivisionError('division by zero')",
+                                "get_response": "<bound method BaseHandler._get_response of <django.core.handlers.wsgi.WSGIHandler object at 0x7f62d45864f0>>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/core/handlers/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py",
+                            "function": "_get_response",
+                            "module": "django.core.handlers.base",
+                            "lineno": 115,
+                            "pre_context": [
+                                "        if response is None:",
+                                "            wrapped_callback = self.make_view_atomic(callback)",
+                                "            try:",
+                                "                response = wrapped_callback(request, *callback_args, **callback_kwargs)",
+                                "            except Exception as e:",
+                            ],
+                            "context_line": "                response = self.process_exception_by_middleware(e, request)",
+                            "post_context": [
+                                "",
+                                "        # Complain if the view returned None (a common error).",
+                                "        if response is None:",
+                                "            if isinstance(callback, types.FunctionType):    # FBV",
+                                "                view_name = callback.__name__",
+                            ],
+                            "vars": {
+                                "self": "<django.core.handlers.wsgi.WSGIHandler object at 0x7f62d45864f0>",
+                                "request": "<WSGIRequest: GET '/divide-zero/'>",
+                                "response": "None",
+                                "resolver": "<URLResolver 'django_error_factory.urls' (None:None) '^/'>",
+                                "resolver_match": "ResolverMatch(func=errors.views.DivideZeroView, args=(), kwargs={}, url_name=divide_zero, app_names=[], namespaces=[], route=divide-zero/)",
+                                "callback": "<function DivideZeroView at 0x7f62d402fe50>",
+                                "callback_args": [],
+                                "callback_kwargs": {},
+                                "middleware_method": "<function CsrfViewMiddleware.process_view at 0x7f62d3710040>",
+                                "wrapped_callback": "<function DivideZeroView at 0x7f62d402fe50>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/core/handlers/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py",
+                            "function": "_get_response",
+                            "module": "django.core.handlers.base",
+                            "lineno": 113,
+                            "pre_context": [
+                                "                break",
+                                "",
+                                "        if response is None:",
+                                "            wrapped_callback = self.make_view_atomic(callback)",
+                                "            try:",
+                            ],
+                            "context_line": "                response = wrapped_callback(request, *callback_args, **callback_kwargs)",
+                            "post_context": [
+                                "            except Exception as e:",
+                                "                response = self.process_exception_by_middleware(e, request)",
+                                "",
+                                "        # Complain if the view returned None (a common error).",
+                                "        if response is None:",
+                            ],
+                            "vars": {
+                                "self": "<django.core.handlers.wsgi.WSGIHandler object at 0x7f62d45864f0>",
+                                "request": "<WSGIRequest: GET '/divide-zero/'>",
+                                "response": "None",
+                                "resolver": "<URLResolver 'django_error_factory.urls' (None:None) '^/'>",
+                                "resolver_match": "ResolverMatch(func=errors.views.DivideZeroView, args=(), kwargs={}, url_name=divide_zero, app_names=[], namespaces=[], route=divide-zero/)",
+                                "callback": "<function DivideZeroView at 0x7f62d402fe50>",
+                                "callback_args": [],
+                                "callback_kwargs": {},
+                                "middleware_method": "<function CsrfViewMiddleware.process_view at 0x7f62d3710040>",
+                                "wrapped_callback": "<function DivideZeroView at 0x7f62d402fe50>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/views/generic/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py",
+                            "function": "view",
+                            "module": "django.views.generic.base",
+                            "lineno": 71,
+                            "pre_context": [
+                                "            if not hasattr(self, 'request'):",
+                                "                raise AttributeError(",
+                                "                    \"%s instance has no 'request' attribute. Did you override \"",
+                                '                    "setup() and forget to call super()?" % cls.__name__',
+                                "                )",
+                            ],
+                            "context_line": "            return self.dispatch(request, *args, **kwargs)",
+                            "post_context": [
+                                "        view.view_class = cls",
+                                "        view.view_initkwargs = initkwargs",
+                                "",
+                                "        # take name and docstring from class",
+                                "        update_wrapper(view, cls, updated=())",
+                            ],
+                            "vars": {
+                                "request": "<WSGIRequest: GET '/divide-zero/'>",
+                                "args": [],
+                                "kwargs": {},
+                                "self": "<errors.views.DivideZeroView object at 0x7f62d2d20100>",
+                                "cls": "<class 'errors.views.DivideZeroView'>",
+                                "initkwargs": {},
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/views/generic/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py",
+                            "function": "dispatch",
+                            "module": "django.views.generic.base",
+                            "lineno": 97,
+                            "pre_context": [
+                                "        # request method isn't on the approved list.",
+                                "        if request.method.lower() in self.http_method_names:",
+                                "            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)",
+                                "        else:",
+                                "            handler = self.http_method_not_allowed",
+                            ],
+                            "context_line": "        return handler(request, *args, **kwargs)",
+                            "post_context": [
+                                "",
+                                "    def http_method_not_allowed(self, request, *args, **kwargs):",
+                                "        logger.warning(",
+                                "            'Method Not Allowed (%s): %s', request.method, request.path,",
+                                "            extra={'status_code': 405, 'request': request}",
+                            ],
+                            "vars": {
+                                "self": "<errors.views.DivideZeroView object at 0x7f62d2d20100>",
+                                "request": "<WSGIRequest: GET '/divide-zero/'>",
+                                "args": [],
+                                "kwargs": {},
+                                "handler": "<bound method DivideZeroView.get of <errors.views.DivideZeroView object at 0x7f62d2d20100>>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "errors/views.py",
+                            "abs_path": "/code/errors/views.py",
+                            "function": "get",
+                            "module": "errors.views",
+                            "lineno": 12,
+                            "pre_context": [
+                                '    template_name = "home.html"',
+                                "",
+                                "",
+                                "class DivideZeroView(View):",
+                                "    def get(self, request, *args, **kwargs):",
+                            ],
+                            "context_line": "        0/0",
+                            "post_context": [
+                                "",
+                                "",
+                                "class DatabaseErrorView(View):",
+                                "    def get(self, request, *args, **kwargs):",
+                                '        User.objects.get(id="9999999")',
+                            ],
+                            "vars": {
+                                "self": "<errors.views.DivideZeroView object at 0x7f62d2d20100>",
+                                "request": "<WSGIRequest: GET '/divide-zero/'>",
+                                "args": [],
+                                "kwargs": {},
+                            },
+                            "in_app": True,
+                        },
+                    ]
+                },
+            }
+        ]
+    },
+    "event_id": "3eb248c5287a4c2fbaa427da654c3795",
+    "timestamp": "2020-01-10T20:21:46.434385Z",
+    "breadcrumbs": [],
+    "transaction": "/divide-zero/",
+    "contexts": {
+        "trace": {
+            "trace_id": "fd41c870fbd44f76ad9f051b488b88bc",
+            "span_id": "9bb4230421b756c2",
+            "parent_span_id": "a0637603c584f464",
+            "op": "django.middleware",
+            "description": "django.middleware.clickjacking.XFrameOptionsMiddleware.__call__",
+        },
+        "runtime": {
+            "name": "CPython",
+            "version": "3.8.1",
+            "build": "3.8.1 (default, Jan  3 2020, 22:55:55) \n[GCC 8.3.0]",
+        },
+    },
+    "modules": {
+        "wheel": "0.33.6",
+        "webencodings": "0.5.1",
+        "wcwidth": "0.1.8",
+        "urllib3": "1.25.7",
+        "typed-ast": "1.4.0",
+        "traitlets": "4.3.3",
+        "tomlkit": "0.5.8",
+        "toml": "0.10.0",
+        "sqlparse": "0.3.0",
+        "six": "1.13.0",
+        "shellingham": "1.3.1",
+        "setuptools": "44.0.0",
+        "sentry-sdk": "0.14.0",
+        "secretstorage": "3.1.1",
+        "requests": "2.22.0",
+        "requests-toolbelt": "0.8.0",
+        "regex": "2020.1.8",
+        "pytz": "2019.3",
+        "pyrsistent": "0.14.11",
+        "pyparsing": "2.4.6",
+        "pylev": "1.3.0",
+        "pygments": "2.5.2",
+        "pycparser": "2.19",
+        "ptyprocess": "0.6.0",
+        "prompt-toolkit": "3.0.2",
+        "poetry": "1.0.0",
+        "pkginfo": "1.5.0.1",
+        "pip": "19.3.1",
+        "pickleshare": "0.7.5",
+        "pexpect": "4.7.0",
+        "pathspec": "0.7.0",
+        "pastel": "0.1.1",
+        "parso": "0.5.2",
+        "msgpack": "0.6.2",
+        "lockfile": "0.12.2",
+        "keyring": "19.3.0",
+        "jsonschema": "3.2.0",
+        "jeepney": "0.4.2",
+        "jedi": "0.15.2",
+        "ipython": "7.11.1",
+        "ipython-genutils": "0.2.0",
+        "ipdb": "0.12.3",
+        "idna": "2.8",
+        "html5lib": "1.0.1",
+        "django": "3.0.2",
+        "decorator": "4.4.1",
+        "cryptography": "2.8",
+        "clikit": "0.4.1",
+        "click": "7.0",
+        "cleo": "0.7.6",
+        "chardet": "3.0.4",
+        "cffi": "1.13.2",
+        "certifi": "2019.11.28",
+        "cachy": "0.3.0",
+        "cachecontrol": "0.12.6",
+        "black": "19.10b0",
+        "backcall": "0.1.0",
+        "attrs": "19.3.0",
+        "asgiref": "3.2.3",
+        "appdirs": "1.4.3",
+    },
+    "extra": {"sys.argv": ["./manage.py", "runserver", "0.0.0.0:8001"]},
+    "request": {
+        "url": "http://localhost:8001/divide-zero/",
+        "query_string": "",
+        "method": "GET",
+        "env": {"SERVER_NAME": "210ecca56d59", "SERVER_PORT": "8001"},
+        "headers": {
+            "Content-Length": "",
+            "Content-Type": "text/plain",
+            "Host": "localhost:8001",
+            "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0",
+            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
+            "Accept-Language": "en-US,en;q=0.5",
+            "Accept-Encoding": "gzip, deflate",
+            "Referer": "http://localhost:8001/",
+            "Connection": "keep-alive",
+            "Cookie": "",
+            "Upgrade-Insecure-Requests": "1",
+            "Dnt": "1",
+            "Cache-Control": "max-age=0",
+        },
+    },
+    "server_name": "210ecca56d59",
+    "sdk": {
+        "name": "sentry.python",
+        "version": "0.14.0",
+        "packages": [{"name": "pypi:sentry-sdk", "version": "0.14.0"}],
+        "integrations": [
+            "argv",
+            "atexit",
+            "dedupe",
+            "django",
+            "excepthook",
+            "logging",
+            "modules",
+            "stdlib",
+            "threading",
+        ],
+    },
+    "platform": "python",
+    "_meta": {
+        "request": {"headers": {"Cookie": {"": {"rem": [["!config", "x", 0, 315]]}}}}
+    },
+}
+
+database_error = {
+    "level": "error",
+    "exception": {
+        "values": [
+            {
+                "module": "django.contrib.auth.models",
+                "type": "User.DoesNotExist",
+                "value": "User matching query does not exist.",
+                "mechanism": {"type": "django", "handled": False},
+                "stacktrace": {
+                    "frames": [
+                        {
+                            "filename": "django/core/handlers/exception.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py",
+                            "function": "inner",
+                            "module": "django.core.handlers.exception",
+                            "lineno": 34,
+                            "pre_context": [
+                                "    can rely on getting a response instead of an exception.",
+                                '    """',
+                                "    @wraps(get_response)",
+                                "    def inner(request):",
+                                "        try:",
+                            ],
+                            "context_line": "            response = get_response(request)",
+                            "post_context": [
+                                "        except Exception as exc:",
+                                "            response = response_for_exception(request, exc)",
+                                "        return response",
+                                "    return inner",
+                                "",
+                            ],
+                            "vars": {
+                                "request": "<WSGIRequest: GET '/database-error/'>",
+                                "exc": "DoesNotExist('User matching query does not exist.')",
+                                "get_response": "<bound method BaseHandler._get_response of <django.core.handlers.wsgi.WSGIHandler object at 0x7f62d45864f0>>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/core/handlers/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py",
+                            "function": "_get_response",
+                            "module": "django.core.handlers.base",
+                            "lineno": 115,
+                            "pre_context": [
+                                "        if response is None:",
+                                "            wrapped_callback = self.make_view_atomic(callback)",
+                                "            try:",
+                                "                response = wrapped_callback(request, *callback_args, **callback_kwargs)",
+                                "            except Exception as e:",
+                            ],
+                            "context_line": "                response = self.process_exception_by_middleware(e, request)",
+                            "post_context": [
+                                "",
+                                "        # Complain if the view returned None (a common error).",
+                                "        if response is None:",
+                                "            if isinstance(callback, types.FunctionType):    # FBV",
+                                "                view_name = callback.__name__",
+                            ],
+                            "vars": {
+                                "self": "<django.core.handlers.wsgi.WSGIHandler object at 0x7f62d45864f0>",
+                                "request": "<WSGIRequest: GET '/database-error/'>",
+                                "response": "None",
+                                "resolver": "<URLResolver 'django_error_factory.urls' (None:None) '^/'>",
+                                "resolver_match": "ResolverMatch(func=errors.views.DatabaseErrorView, args=(), kwargs={}, url_name=database_error, app_names=[], namespaces=[], route=database-error/)",
+                                "callback": "<function DatabaseErrorView at 0x7f62d403d1f0>",
+                                "callback_args": [],
+                                "callback_kwargs": {},
+                                "middleware_method": "<function CsrfViewMiddleware.process_view at 0x7f62d3710040>",
+                                "wrapped_callback": "<function DatabaseErrorView at 0x7f62d403d1f0>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/core/handlers/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py",
+                            "function": "_get_response",
+                            "module": "django.core.handlers.base",
+                            "lineno": 113,
+                            "pre_context": [
+                                "                break",
+                                "",
+                                "        if response is None:",
+                                "            wrapped_callback = self.make_view_atomic(callback)",
+                                "            try:",
+                            ],
+                            "context_line": "                response = wrapped_callback(request, *callback_args, **callback_kwargs)",
+                            "post_context": [
+                                "            except Exception as e:",
+                                "                response = self.process_exception_by_middleware(e, request)",
+                                "",
+                                "        # Complain if the view returned None (a common error).",
+                                "        if response is None:",
+                            ],
+                            "vars": {
+                                "self": "<django.core.handlers.wsgi.WSGIHandler object at 0x7f62d45864f0>",
+                                "request": "<WSGIRequest: GET '/database-error/'>",
+                                "response": "None",
+                                "resolver": "<URLResolver 'django_error_factory.urls' (None:None) '^/'>",
+                                "resolver_match": "ResolverMatch(func=errors.views.DatabaseErrorView, args=(), kwargs={}, url_name=database_error, app_names=[], namespaces=[], route=database-error/)",
+                                "callback": "<function DatabaseErrorView at 0x7f62d403d1f0>",
+                                "callback_args": [],
+                                "callback_kwargs": {},
+                                "middleware_method": "<function CsrfViewMiddleware.process_view at 0x7f62d3710040>",
+                                "wrapped_callback": "<function DatabaseErrorView at 0x7f62d403d1f0>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/views/generic/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py",
+                            "function": "view",
+                            "module": "django.views.generic.base",
+                            "lineno": 71,
+                            "pre_context": [
+                                "            if not hasattr(self, 'request'):",
+                                "                raise AttributeError(",
+                                "                    \"%s instance has no 'request' attribute. Did you override \"",
+                                '                    "setup() and forget to call super()?" % cls.__name__',
+                                "                )",
+                            ],
+                            "context_line": "            return self.dispatch(request, *args, **kwargs)",
+                            "post_context": [
+                                "        view.view_class = cls",
+                                "        view.view_initkwargs = initkwargs",
+                                "",
+                                "        # take name and docstring from class",
+                                "        update_wrapper(view, cls, updated=())",
+                            ],
+                            "vars": {
+                                "request": "<WSGIRequest: GET '/database-error/'>",
+                                "args": [],
+                                "kwargs": {},
+                                "self": "<errors.views.DatabaseErrorView object at 0x7f62d2d24670>",
+                                "cls": "<class 'errors.views.DatabaseErrorView'>",
+                                "initkwargs": {},
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/views/generic/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py",
+                            "function": "dispatch",
+                            "module": "django.views.generic.base",
+                            "lineno": 97,
+                            "pre_context": [
+                                "        # request method isn't on the approved list.",
+                                "        if request.method.lower() in self.http_method_names:",
+                                "            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)",
+                                "        else:",
+                                "            handler = self.http_method_not_allowed",
+                            ],
+                            "context_line": "        return handler(request, *args, **kwargs)",
+                            "post_context": [
+                                "",
+                                "    def http_method_not_allowed(self, request, *args, **kwargs):",
+                                "        logger.warning(",
+                                "            'Method Not Allowed (%s): %s', request.method, request.path,",
+                                "            extra={'status_code': 405, 'request': request}",
+                            ],
+                            "vars": {
+                                "self": "<errors.views.DatabaseErrorView object at 0x7f62d2d24670>",
+                                "request": "<WSGIRequest: GET '/database-error/'>",
+                                "args": [],
+                                "kwargs": {},
+                                "handler": "<bound method DatabaseErrorView.get of <errors.views.DatabaseErrorView object at 0x7f62d2d24670>>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "errors/views.py",
+                            "abs_path": "/code/errors/views.py",
+                            "function": "get",
+                            "module": "errors.views",
+                            "lineno": 17,
+                            "pre_context": [
+                                "        0/0",
+                                "",
+                                "",
+                                "class DatabaseErrorView(View):",
+                                "    def get(self, request, *args, **kwargs):",
+                            ],
+                            "context_line": '        User.objects.get(id="9999999")',
+                            "post_context": [
+                                "",
+                                "",
+                                "class PostErrorView(View):",
+                                "    def post(self, request, *args, **kwargs):",
+                                '        request.POST["nope"]',
+                            ],
+                            "vars": {
+                                "self": "<errors.views.DatabaseErrorView object at 0x7f62d2d24670>",
+                                "request": "<WSGIRequest: GET '/database-error/'>",
+                                "args": [],
+                                "kwargs": {},
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/db/models/manager.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/db/models/manager.py",
+                            "function": "manager_method",
+                            "module": "django.db.models.manager",
+                            "lineno": 82,
+                            "pre_context": [
+                                "",
+                                "    @classmethod",
+                                "    def _get_queryset_methods(cls, queryset_class):",
+                                "        def create_method(name, method):",
+                                "            def manager_method(self, *args, **kwargs):",
+                            ],
+                            "context_line": "                return getattr(self.get_queryset(), name)(*args, **kwargs)",
+                            "post_context": [
+                                "            manager_method.__name__ = method.__name__",
+                                "            manager_method.__doc__ = method.__doc__",
+                                "            return manager_method",
+                                "",
+                                "        new_methods = {}",
+                            ],
+                            "vars": {
+                                "self": "<django.contrib.auth.models.UserManager object at 0x7f62d410be20>",
+                                "args": [],
+                                "kwargs": {"id": "'9999999'"},
+                                "name": "'get'",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/db/models/query.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/db/models/query.py",
+                            "function": "get",
+                            "module": "django.db.models.query",
+                            "lineno": 415,
+                            "pre_context": [
+                                "            clone.query.set_limits(high=limit)",
+                                "        num = len(clone)",
+                                "        if num == 1:",
+                                "            return clone._result_cache[0]",
+                                "        if not num:",
+                            ],
+                            "context_line": "            raise self.model.DoesNotExist(",
+                            "post_context": [
+                                '                "%s matching query does not exist." %',
+                                "                self.model._meta.object_name",
+                                "            )",
+                                "        raise self.model.MultipleObjectsReturned(",
+                                "            'get() returned more than one %s -- it returned %s!' % (",
+                            ],
+                            "vars": {
+                                "self": "<QuerySet from django.db.models.query at 0x7f62d2d24340>",
+                                "args": [],
+                                "kwargs": {"id": "'9999999'"},
+                                "clone": "<QuerySet from django.db.models.query at 0x7f62d2d24460>",
+                                "limit": "21",
+                                "num": "0",
+                            },
+                            "in_app": True,
+                        },
+                    ]
+                },
+            }
+        ]
+    },
+    "event_id": "f84f7731ece04760a43fe1d777734fef",
+    "timestamp": "2020-01-10T20:23:23.513387Z",
+    "breadcrumbs": [
+        {
+            "message": 'SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login", "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."date_joined" FROM "auth_user" WHERE "auth_user"."id" = %s LIMIT 21',
+            "category": "query",
+            "data": {},
+            "timestamp": "2020-01-10T20:23:23.508243Z",
+            "type": "default",
+        }
+    ],
+    "transaction": "/database-error/",
+    "contexts": {
+        "trace": {
+            "trace_id": "b56f4a334dbb4409bed0055b1c51d70a",
+            "span_id": "bceca618ab73060c",
+            "parent_span_id": "995f271d2ef32861",
+            "op": "django.middleware",
+            "description": "django.middleware.clickjacking.XFrameOptionsMiddleware.__call__",
+        },
+        "runtime": {
+            "name": "CPython",
+            "version": "3.8.1",
+            "build": "3.8.1 (default, Jan  3 2020, 22:55:55) \n[GCC 8.3.0]",
+        },
+    },
+    "modules": {
+        "wheel": "0.33.6",
+        "webencodings": "0.5.1",
+        "wcwidth": "0.1.8",
+        "urllib3": "1.25.7",
+        "typed-ast": "1.4.0",
+        "traitlets": "4.3.3",
+        "tomlkit": "0.5.8",
+        "toml": "0.10.0",
+        "sqlparse": "0.3.0",
+        "six": "1.13.0",
+        "shellingham": "1.3.1",
+        "setuptools": "44.0.0",
+        "sentry-sdk": "0.14.0",
+        "secretstorage": "3.1.1",
+        "requests": "2.22.0",
+        "requests-toolbelt": "0.8.0",
+        "regex": "2020.1.8",
+        "pytz": "2019.3",
+        "pyrsistent": "0.14.11",
+        "pyparsing": "2.4.6",
+        "pylev": "1.3.0",
+        "pygments": "2.5.2",
+        "pycparser": "2.19",
+        "ptyprocess": "0.6.0",
+        "prompt-toolkit": "3.0.2",
+        "poetry": "1.0.0",
+        "pkginfo": "1.5.0.1",
+        "pip": "19.3.1",
+        "pickleshare": "0.7.5",
+        "pexpect": "4.7.0",
+        "pathspec": "0.7.0",
+        "pastel": "0.1.1",
+        "parso": "0.5.2",
+        "msgpack": "0.6.2",
+        "lockfile": "0.12.2",
+        "keyring": "19.3.0",
+        "jsonschema": "3.2.0",
+        "jeepney": "0.4.2",
+        "jedi": "0.15.2",
+        "ipython": "7.11.1",
+        "ipython-genutils": "0.2.0",
+        "ipdb": "0.12.3",
+        "idna": "2.8",
+        "html5lib": "1.0.1",
+        "django": "3.0.2",
+        "decorator": "4.4.1",
+        "cryptography": "2.8",
+        "clikit": "0.4.1",
+        "click": "7.0",
+        "cleo": "0.7.6",
+        "chardet": "3.0.4",
+        "cffi": "1.13.2",
+        "certifi": "2019.11.28",
+        "cachy": "0.3.0",
+        "cachecontrol": "0.12.6",
+        "black": "19.10b0",
+        "backcall": "0.1.0",
+        "attrs": "19.3.0",
+        "asgiref": "3.2.3",
+        "appdirs": "1.4.3",
+    },
+    "extra": {"sys.argv": ["./manage.py", "runserver", "0.0.0.0:8001"]},
+    "request": {
+        "url": "http://localhost:8001/database-error/",
+        "query_string": "",
+        "method": "GET",
+        "env": {"SERVER_NAME": "210ecca56d59", "SERVER_PORT": "8001"},
+        "headers": {
+            "Content-Length": "",
+            "Content-Type": "text/plain",
+            "Host": "localhost:8001",
+            "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0",
+            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
+            "Accept-Language": "en-US,en;q=0.5",
+            "Accept-Encoding": "gzip, deflate",
+            "Connection": "keep-alive",
+            "Referer": "http://localhost:8001/",
+            "Cookie": "",
+            "Upgrade-Insecure-Requests": "1",
+            "Dnt": "1",
+        },
+    },
+    "server_name": "210ecca56d59",
+    "sdk": {
+        "name": "sentry.python",
+        "version": "0.14.0",
+        "packages": [{"name": "pypi:sentry-sdk", "version": "0.14.0"}],
+        "integrations": [
+            "argv",
+            "atexit",
+            "dedupe",
+            "django",
+            "excepthook",
+            "logging",
+            "modules",
+            "stdlib",
+            "threading",
+        ],
+    },
+    "platform": "python",
+    "_meta": {
+        "request": {"headers": {"Cookie": {"": {"rem": [["!config", "x", 0, 315]]}}}}
+    },
+}
+
+post_error = {
+    "level": "error",
+    "exception": {
+        "values": [
+            {
+                "module": None,
+                "type": "KeyError",
+                "value": "'nope'",
+                "mechanism": {"type": "django", "handled": False},
+                "stacktrace": {
+                    "frames": [
+                        {
+                            "filename": "django/utils/datastructures.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/utils/datastructures.py",
+                            "function": "__getitem__",
+                            "module": "django.utils.datastructures",
+                            "lineno": 76,
+                            "pre_context": [
+                                '        """',
+                                "        Return the last data value for this key, or [] if it's an empty list;",
+                                "        raise KeyError if not found.",
+                                '        """',
+                                "        try:",
+                            ],
+                            "context_line": "            list_ = super().__getitem__(key)",
+                            "post_context": [
+                                "        except KeyError:",
+                                "            raise MultiValueDictKeyError(key)",
+                                "        try:",
+                                "            return list_[-1]",
+                                "        except IndexError:",
+                            ],
+                            "vars": {
+                                "self": {
+                                    "csrfmiddlewaretoken": "'G1Cfr5bnrZz4ICfiDwPdGMY865fvWB1RN1RZD2vJCYxaEJsQfFX1QUdlKEwNfELr'",
+                                    "param1": "'val'",
+                                },
+                                "key": "'nope'",
+                                "__class__": "<class 'django.utils.datastructures.MultiValueDict'>",
+                            },
+                            "in_app": True,
+                        }
+                    ]
+                },
+            },
+            {
+                "module": "django.utils.datastructures",
+                "type": "MultiValueDictKeyError",
+                "value": "'nope'",
+                "mechanism": {"type": "django", "handled": False},
+                "stacktrace": {
+                    "frames": [
+                        {
+                            "filename": "django/core/handlers/exception.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py",
+                            "function": "inner",
+                            "module": "django.core.handlers.exception",
+                            "lineno": 34,
+                            "pre_context": [
+                                "    can rely on getting a response instead of an exception.",
+                                '    """',
+                                "    @wraps(get_response)",
+                                "    def inner(request):",
+                                "        try:",
+                            ],
+                            "context_line": "            response = get_response(request)",
+                            "post_context": [
+                                "        except Exception as exc:",
+                                "            response = response_for_exception(request, exc)",
+                                "        return response",
+                                "    return inner",
+                                "",
+                            ],
+                            "vars": {
+                                "request": "<WSGIRequest: POST '/post-error/'>",
+                                "exc": "MultiValueDictKeyError('nope')",
+                                "get_response": "<bound method BaseHandler._get_response of <django.core.handlers.wsgi.WSGIHandler object at 0x7f62d45864f0>>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/core/handlers/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py",
+                            "function": "_get_response",
+                            "module": "django.core.handlers.base",
+                            "lineno": 115,
+                            "pre_context": [
+                                "        if response is None:",
+                                "            wrapped_callback = self.make_view_atomic(callback)",
+                                "            try:",
+                                "                response = wrapped_callback(request, *callback_args, **callback_kwargs)",
+                                "            except Exception as e:",
+                            ],
+                            "context_line": "                response = self.process_exception_by_middleware(e, request)",
+                            "post_context": [
+                                "",
+                                "        # Complain if the view returned None (a common error).",
+                                "        if response is None:",
+                                "            if isinstance(callback, types.FunctionType):    # FBV",
+                                "                view_name = callback.__name__",
+                            ],
+                            "vars": {
+                                "self": "<django.core.handlers.wsgi.WSGIHandler object at 0x7f62d45864f0>",
+                                "request": "<WSGIRequest: POST '/post-error/'>",
+                                "response": "None",
+                                "resolver": "<URLResolver 'django_error_factory.urls' (None:None) '^/'>",
+                                "resolver_match": "ResolverMatch(func=errors.views.PostErrorView, args=(), kwargs={}, url_name=post_error, app_names=[], namespaces=[], route=post-error/)",
+                                "callback": "<function PostErrorView at 0x7f62d403d280>",
+                                "callback_args": [],
+                                "callback_kwargs": {},
+                                "middleware_method": "<function CsrfViewMiddleware.process_view at 0x7f62d3710040>",
+                                "wrapped_callback": "<function PostErrorView at 0x7f62d403d280>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/core/handlers/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py",
+                            "function": "_get_response",
+                            "module": "django.core.handlers.base",
+                            "lineno": 113,
+                            "pre_context": [
+                                "                break",
+                                "",
+                                "        if response is None:",
+                                "            wrapped_callback = self.make_view_atomic(callback)",
+                                "            try:",
+                            ],
+                            "context_line": "                response = wrapped_callback(request, *callback_args, **callback_kwargs)",
+                            "post_context": [
+                                "            except Exception as e:",
+                                "                response = self.process_exception_by_middleware(e, request)",
+                                "",
+                                "        # Complain if the view returned None (a common error).",
+                                "        if response is None:",
+                            ],
+                            "vars": {
+                                "self": "<django.core.handlers.wsgi.WSGIHandler object at 0x7f62d45864f0>",
+                                "request": "<WSGIRequest: POST '/post-error/'>",
+                                "response": "None",
+                                "resolver": "<URLResolver 'django_error_factory.urls' (None:None) '^/'>",
+                                "resolver_match": "ResolverMatch(func=errors.views.PostErrorView, args=(), kwargs={}, url_name=post_error, app_names=[], namespaces=[], route=post-error/)",
+                                "callback": "<function PostErrorView at 0x7f62d403d280>",
+                                "callback_args": [],
+                                "callback_kwargs": {},
+                                "middleware_method": "<function CsrfViewMiddleware.process_view at 0x7f62d3710040>",
+                                "wrapped_callback": "<function PostErrorView at 0x7f62d403d280>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/views/generic/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py",
+                            "function": "view",
+                            "module": "django.views.generic.base",
+                            "lineno": 71,
+                            "pre_context": [
+                                "            if not hasattr(self, 'request'):",
+                                "                raise AttributeError(",
+                                "                    \"%s instance has no 'request' attribute. Did you override \"",
+                                '                    "setup() and forget to call super()?" % cls.__name__',
+                                "                )",
+                            ],
+                            "context_line": "            return self.dispatch(request, *args, **kwargs)",
+                            "post_context": [
+                                "        view.view_class = cls",
+                                "        view.view_initkwargs = initkwargs",
+                                "",
+                                "        # take name and docstring from class",
+                                "        update_wrapper(view, cls, updated=())",
+                            ],
+                            "vars": {
+                                "request": "<WSGIRequest: POST '/post-error/'>",
+                                "args": [],
+                                "kwargs": {},
+                                "self": "<errors.views.PostErrorView object at 0x7f62d2227ac0>",
+                                "cls": "<class 'errors.views.PostErrorView'>",
+                                "initkwargs": {},
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/views/generic/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py",
+                            "function": "dispatch",
+                            "module": "django.views.generic.base",
+                            "lineno": 97,
+                            "pre_context": [
+                                "        # request method isn't on the approved list.",
+                                "        if request.method.lower() in self.http_method_names:",
+                                "            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)",
+                                "        else:",
+                                "            handler = self.http_method_not_allowed",
+                            ],
+                            "context_line": "        return handler(request, *args, **kwargs)",
+                            "post_context": [
+                                "",
+                                "    def http_method_not_allowed(self, request, *args, **kwargs):",
+                                "        logger.warning(",
+                                "            'Method Not Allowed (%s): %s', request.method, request.path,",
+                                "            extra={'status_code': 405, 'request': request}",
+                            ],
+                            "vars": {
+                                "self": "<errors.views.PostErrorView object at 0x7f62d2227ac0>",
+                                "request": "<WSGIRequest: POST '/post-error/'>",
+                                "args": [],
+                                "kwargs": {},
+                                "handler": "<bound method PostErrorView.post of <errors.views.PostErrorView object at 0x7f62d2227ac0>>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "errors/views.py",
+                            "abs_path": "/code/errors/views.py",
+                            "function": "post",
+                            "module": "errors.views",
+                            "lineno": 22,
+                            "pre_context": [
+                                '        User.objects.get(id="9999999")',
+                                "",
+                                "",
+                                "class PostErrorView(View):",
+                                "    def post(self, request, *args, **kwargs):",
+                            ],
+                            "context_line": '        request.POST["nope"]',
+                            "post_context": [
+                                "",
+                                "",
+                                "class DatabaseStackErrorView(View):",
+                                "    def get(self, request, *args, **kwargs):",
+                                "        users = User.objects.all()",
+                            ],
+                            "vars": {
+                                "self": "<errors.views.PostErrorView object at 0x7f62d2227ac0>",
+                                "request": "<WSGIRequest: POST '/post-error/'>",
+                                "args": [],
+                                "kwargs": {},
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/utils/datastructures.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/utils/datastructures.py",
+                            "function": "__getitem__",
+                            "module": "django.utils.datastructures",
+                            "lineno": 78,
+                            "pre_context": [
+                                "        raise KeyError if not found.",
+                                '        """',
+                                "        try:",
+                                "            list_ = super().__getitem__(key)",
+                                "        except KeyError:",
+                            ],
+                            "context_line": "            raise MultiValueDictKeyError(key)",
+                            "post_context": [
+                                "        try:",
+                                "            return list_[-1]",
+                                "        except IndexError:",
+                                "            return []",
+                                "",
+                            ],
+                            "vars": {
+                                "self": {
+                                    "csrfmiddlewaretoken": "'G1Cfr5bnrZz4ICfiDwPdGMY865fvWB1RN1RZD2vJCYxaEJsQfFX1QUdlKEwNfELr'",
+                                    "param1": "'val'",
+                                },
+                                "key": "'nope'",
+                                "__class__": "<class 'django.utils.datastructures.MultiValueDict'>",
+                            },
+                            "in_app": True,
+                        },
+                    ]
+                },
+            },
+        ]
+    },
+    "event_id": "33338bdfb1024cffa7a023c22e36436c",
+    "timestamp": "2020-01-10T20:24:16.721470Z",
+    "breadcrumbs": [],
+    "transaction": "/post-error/",
+    "contexts": {
+        "trace": {
+            "trace_id": "ac1605c393434f16834cffc98eb81240",
+            "span_id": "8c46c105c38431d3",
+            "parent_span_id": "8b01678e63255c9c",
+            "op": "django.middleware",
+            "description": "django.middleware.clickjacking.XFrameOptionsMiddleware.__call__",
+        },
+        "runtime": {
+            "name": "CPython",
+            "version": "3.8.1",
+            "build": "3.8.1 (default, Jan  3 2020, 22:55:55) \n[GCC 8.3.0]",
+        },
+    },
+    "modules": {
+        "wheel": "0.33.6",
+        "webencodings": "0.5.1",
+        "wcwidth": "0.1.8",
+        "urllib3": "1.25.7",
+        "typed-ast": "1.4.0",
+        "traitlets": "4.3.3",
+        "tomlkit": "0.5.8",
+        "toml": "0.10.0",
+        "sqlparse": "0.3.0",
+        "six": "1.13.0",
+        "shellingham": "1.3.1",
+        "setuptools": "44.0.0",
+        "sentry-sdk": "0.14.0",
+        "secretstorage": "3.1.1",
+        "requests": "2.22.0",
+        "requests-toolbelt": "0.8.0",
+        "regex": "2020.1.8",
+        "pytz": "2019.3",
+        "pyrsistent": "0.14.11",
+        "pyparsing": "2.4.6",
+        "pylev": "1.3.0",
+        "pygments": "2.5.2",
+        "pycparser": "2.19",
+        "ptyprocess": "0.6.0",
+        "prompt-toolkit": "3.0.2",
+        "poetry": "1.0.0",
+        "pkginfo": "1.5.0.1",
+        "pip": "19.3.1",
+        "pickleshare": "0.7.5",
+        "pexpect": "4.7.0",
+        "pathspec": "0.7.0",
+        "pastel": "0.1.1",
+        "parso": "0.5.2",
+        "msgpack": "0.6.2",
+        "lockfile": "0.12.2",
+        "keyring": "19.3.0",
+        "jsonschema": "3.2.0",
+        "jeepney": "0.4.2",
+        "jedi": "0.15.2",
+        "ipython": "7.11.1",
+        "ipython-genutils": "0.2.0",
+        "ipdb": "0.12.3",
+        "idna": "2.8",
+        "html5lib": "1.0.1",
+        "django": "3.0.2",
+        "decorator": "4.4.1",
+        "cryptography": "2.8",
+        "clikit": "0.4.1",
+        "click": "7.0",
+        "cleo": "0.7.6",
+        "chardet": "3.0.4",
+        "cffi": "1.13.2",
+        "certifi": "2019.11.28",
+        "cachy": "0.3.0",
+        "cachecontrol": "0.12.6",
+        "black": "19.10b0",
+        "backcall": "0.1.0",
+        "attrs": "19.3.0",
+        "asgiref": "3.2.3",
+        "appdirs": "1.4.3",
+    },
+    "extra": {"sys.argv": ["./manage.py", "runserver", "0.0.0.0:8001"]},
+    "request": {
+        "url": "http://localhost:8001/post-error/",
+        "query_string": "",
+        "method": "POST",
+        "env": {"SERVER_NAME": "210ecca56d59", "SERVER_PORT": "8001"},
+        "headers": {
+            "Content-Length": "95",
+            "Content-Type": "application/x-www-form-urlencoded",
+            "Host": "localhost:8001",
+            "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0",
+            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
+            "Accept-Language": "en-US,en;q=0.5",
+            "Accept-Encoding": "gzip, deflate",
+            "Origin": "http://localhost:8001",
+            "Connection": "keep-alive",
+            "Referer": "http://localhost:8001/",
+            "Cookie": "",
+            "Upgrade-Insecure-Requests": "1",
+            "Dnt": "1",
+        },
+        "data": {
+            "csrfmiddlewaretoken": "G1Cfr5bnrZz4ICfiDwPdGMY865fvWB1RN1RZD2vJCYxaEJsQfFX1QUdlKEwNfELr",
+            "param1": "val",
+        },
+    },
+    "server_name": "210ecca56d59",
+    "sdk": {
+        "name": "sentry.python",
+        "version": "0.14.0",
+        "packages": [{"name": "pypi:sentry-sdk", "version": "0.14.0"}],
+        "integrations": [
+            "argv",
+            "atexit",
+            "dedupe",
+            "django",
+            "excepthook",
+            "logging",
+            "modules",
+            "stdlib",
+            "threading",
+        ],
+    },
+    "platform": "python",
+    "_meta": {
+        "request": {"headers": {"Cookie": {"": {"rem": [["!config", "x", 0, 315]]}}}}
+    },
+}
+
+database_stack_error = {
+    "level": "error",
+    "exception": {
+        "values": [
+            {
+                "module": "django.contrib.auth.models",
+                "type": "User.DoesNotExist",
+                "value": "User matching query does not exist.",
+                "mechanism": {"type": "django", "handled": False},
+                "stacktrace": {
+                    "frames": [
+                        {
+                            "filename": "django/core/handlers/exception.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py",
+                            "function": "inner",
+                            "module": "django.core.handlers.exception",
+                            "lineno": 34,
+                            "pre_context": [
+                                "    can rely on getting a response instead of an exception.",
+                                '    """',
+                                "    @wraps(get_response)",
+                                "    def inner(request):",
+                                "        try:",
+                            ],
+                            "context_line": "            response = get_response(request)",
+                            "post_context": [
+                                "        except Exception as exc:",
+                                "            response = response_for_exception(request, exc)",
+                                "        return response",
+                                "    return inner",
+                                "",
+                            ],
+                            "vars": {
+                                "request": "<WSGIRequest: GET '/database-stack-error/'>",
+                                "exc": "DoesNotExist('User matching query does not exist.')",
+                                "get_response": "<bound method BaseHandler._get_response of <django.core.handlers.wsgi.WSGIHandler object at 0x7f62d45864f0>>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/core/handlers/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py",
+                            "function": "_get_response",
+                            "module": "django.core.handlers.base",
+                            "lineno": 115,
+                            "pre_context": [
+                                "        if response is None:",
+                                "            wrapped_callback = self.make_view_atomic(callback)",
+                                "            try:",
+                                "                response = wrapped_callback(request, *callback_args, **callback_kwargs)",
+                                "            except Exception as e:",
+                            ],
+                            "context_line": "                response = self.process_exception_by_middleware(e, request)",
+                            "post_context": [
+                                "",
+                                "        # Complain if the view returned None (a common error).",
+                                "        if response is None:",
+                                "            if isinstance(callback, types.FunctionType):    # FBV",
+                                "                view_name = callback.__name__",
+                            ],
+                            "vars": {
+                                "self": "<django.core.handlers.wsgi.WSGIHandler object at 0x7f62d45864f0>",
+                                "request": "<WSGIRequest: GET '/database-stack-error/'>",
+                                "response": "None",
+                                "resolver": "<URLResolver 'django_error_factory.urls' (None:None) '^/'>",
+                                "resolver_match": "ResolverMatch(func=errors.views.DatabaseStackErrorView, args=(), kwargs={}, url_name=database_stack_error, app_names=[], namespaces=[], route=database-stack-error/)",
+                                "callback": "<function DatabaseStackErrorView at 0x7f62d403d310>",
+                                "callback_args": [],
+                                "callback_kwargs": {},
+                                "middleware_method": "<function CsrfViewMiddleware.process_view at 0x7f62d3710040>",
+                                "wrapped_callback": "<function DatabaseStackErrorView at 0x7f62d403d310>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/core/handlers/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py",
+                            "function": "_get_response",
+                            "module": "django.core.handlers.base",
+                            "lineno": 113,
+                            "pre_context": [
+                                "                break",
+                                "",
+                                "        if response is None:",
+                                "            wrapped_callback = self.make_view_atomic(callback)",
+                                "            try:",
+                            ],
+                            "context_line": "                response = wrapped_callback(request, *callback_args, **callback_kwargs)",
+                            "post_context": [
+                                "            except Exception as e:",
+                                "                response = self.process_exception_by_middleware(e, request)",
+                                "",
+                                "        # Complain if the view returned None (a common error).",
+                                "        if response is None:",
+                            ],
+                            "vars": {
+                                "self": "<django.core.handlers.wsgi.WSGIHandler object at 0x7f62d45864f0>",
+                                "request": "<WSGIRequest: GET '/database-stack-error/'>",
+                                "response": "None",
+                                "resolver": "<URLResolver 'django_error_factory.urls' (None:None) '^/'>",
+                                "resolver_match": "ResolverMatch(func=errors.views.DatabaseStackErrorView, args=(), kwargs={}, url_name=database_stack_error, app_names=[], namespaces=[], route=database-stack-error/)",
+                                "callback": "<function DatabaseStackErrorView at 0x7f62d403d310>",
+                                "callback_args": [],
+                                "callback_kwargs": {},
+                                "middleware_method": "<function CsrfViewMiddleware.process_view at 0x7f62d3710040>",
+                                "wrapped_callback": "<function DatabaseStackErrorView at 0x7f62d403d310>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/views/generic/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py",
+                            "function": "view",
+                            "module": "django.views.generic.base",
+                            "lineno": 71,
+                            "pre_context": [
+                                "            if not hasattr(self, 'request'):",
+                                "                raise AttributeError(",
+                                "                    \"%s instance has no 'request' attribute. Did you override \"",
+                                '                    "setup() and forget to call super()?" % cls.__name__',
+                                "                )",
+                            ],
+                            "context_line": "            return self.dispatch(request, *args, **kwargs)",
+                            "post_context": [
+                                "        view.view_class = cls",
+                                "        view.view_initkwargs = initkwargs",
+                                "",
+                                "        # take name and docstring from class",
+                                "        update_wrapper(view, cls, updated=())",
+                            ],
+                            "vars": {
+                                "request": "<WSGIRequest: GET '/database-stack-error/'>",
+                                "args": [],
+                                "kwargs": {},
+                                "self": "<errors.views.DatabaseStackErrorView object at 0x7f62d2221d60>",
+                                "cls": "<class 'errors.views.DatabaseStackErrorView'>",
+                                "initkwargs": {},
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/views/generic/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py",
+                            "function": "dispatch",
+                            "module": "django.views.generic.base",
+                            "lineno": 97,
+                            "pre_context": [
+                                "        # request method isn't on the approved list.",
+                                "        if request.method.lower() in self.http_method_names:",
+                                "            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)",
+                                "        else:",
+                                "            handler = self.http_method_not_allowed",
+                            ],
+                            "context_line": "        return handler(request, *args, **kwargs)",
+                            "post_context": [
+                                "",
+                                "    def http_method_not_allowed(self, request, *args, **kwargs):",
+                                "        logger.warning(",
+                                "            'Method Not Allowed (%s): %s', request.method, request.path,",
+                                "            extra={'status_code': 405, 'request': request}",
+                            ],
+                            "vars": {
+                                "self": "<errors.views.DatabaseStackErrorView object at 0x7f62d2221d60>",
+                                "request": "<WSGIRequest: GET '/database-stack-error/'>",
+                                "args": [],
+                                "kwargs": {},
+                                "handler": "<bound method DatabaseStackErrorView.get of <errors.views.DatabaseStackErrorView object at 0x7f62d2221d60>>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "errors/views.py",
+                            "abs_path": "/code/errors/views.py",
+                            "function": "get",
+                            "module": "errors.views",
+                            "lineno": 28,
+                            "pre_context": [
+                                "",
+                                "",
+                                "class DatabaseStackErrorView(View):",
+                                "    def get(self, request, *args, **kwargs):",
+                                "        users = User.objects.all()",
+                            ],
+                            "context_line": "        self.make_error(users)",
+                            "post_context": [
+                                "",
+                                "    def make_error(self, users):",
+                                "        User.objects.get(id=users.count() + 10000)",
+                                "",
+                                "",
+                            ],
+                            "vars": {
+                                "self": "<errors.views.DatabaseStackErrorView object at 0x7f62d2221d60>",
+                                "request": "<WSGIRequest: GET '/database-stack-error/'>",
+                                "args": [],
+                                "kwargs": {},
+                                "users": "<QuerySet from django.db.models.query at 0x7f62d2221d00>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "errors/views.py",
+                            "abs_path": "/code/errors/views.py",
+                            "function": "make_error",
+                            "module": "errors.views",
+                            "lineno": 31,
+                            "pre_context": [
+                                "    def get(self, request, *args, **kwargs):",
+                                "        users = User.objects.all()",
+                                "        self.make_error(users)",
+                                "",
+                                "    def make_error(self, users):",
+                            ],
+                            "context_line": "        User.objects.get(id=users.count() + 10000)",
+                            "post_context": [
+                                "",
+                                "",
+                                "class TemplateErrorView(TemplateView):",
+                                '    template_name = "template_error.html"',
+                                "",
+                            ],
+                            "vars": {
+                                "self": "<errors.views.DatabaseStackErrorView object at 0x7f62d2221d60>",
+                                "users": "<QuerySet from django.db.models.query at 0x7f62d2221d00>",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/db/models/manager.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/db/models/manager.py",
+                            "function": "manager_method",
+                            "module": "django.db.models.manager",
+                            "lineno": 82,
+                            "pre_context": [
+                                "",
+                                "    @classmethod",
+                                "    def _get_queryset_methods(cls, queryset_class):",
+                                "        def create_method(name, method):",
+                                "            def manager_method(self, *args, **kwargs):",
+                            ],
+                            "context_line": "                return getattr(self.get_queryset(), name)(*args, **kwargs)",
+                            "post_context": [
+                                "            manager_method.__name__ = method.__name__",
+                                "            manager_method.__doc__ = method.__doc__",
+                                "            return manager_method",
+                                "",
+                                "        new_methods = {}",
+                            ],
+                            "vars": {
+                                "self": "<django.contrib.auth.models.UserManager object at 0x7f62d410be20>",
+                                "args": [],
+                                "kwargs": {"id": "10000"},
+                                "name": "'get'",
+                            },
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/db/models/query.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/db/models/query.py",
+                            "function": "get",
+                            "module": "django.db.models.query",
+                            "lineno": 415,
+                            "pre_context": [
+                                "            clone.query.set_limits(high=limit)",
+                                "        num = len(clone)",
+                                "        if num == 1:",
+                                "            return clone._result_cache[0]",
+                                "        if not num:",
+                            ],
+                            "context_line": "            raise self.model.DoesNotExist(",
+                            "post_context": [
+                                '                "%s matching query does not exist." %',
+                                "                self.model._meta.object_name",
+                                "            )",
+                                "        raise self.model.MultipleObjectsReturned(",
+                                "            'get() returned more than one %s -- it returned %s!' % (",
+                            ],
+                            "vars": {
+                                "self": "<QuerySet from django.db.models.query at 0x7f62d2221e20>",
+                                "args": [],
+                                "kwargs": {"id": "10000"},
+                                "clone": "<QuerySet from django.db.models.query at 0x7f62d3656c70>",
+                                "limit": "21",
+                                "num": "0",
+                            },
+                            "in_app": True,
+                        },
+                    ]
+                },
+            }
+        ]
+    },
+    "event_id": "5cdb58a7463f4cd8b7c57d3372fb5ae9",
+    "timestamp": "2020-01-10T20:24:52.681701Z",
+    "breadcrumbs": [
+        {
+            "message": 'SELECT COUNT(*) AS "__count" FROM "auth_user"',
+            "category": "query",
+            "data": {},
+            "timestamp": "2020-01-10T20:24:52.674131Z",
+            "type": "default",
+        },
+        {
+            "message": 'SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login", "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."date_joined" FROM "auth_user" WHERE "auth_user"."id" = %s LIMIT 21',
+            "category": "query",
+            "data": {},
+            "timestamp": "2020-01-10T20:24:52.677030Z",
+            "type": "default",
+        },
+    ],
+    "transaction": "/database-stack-error/",
+    "contexts": {
+        "trace": {
+            "trace_id": "8d5aef32be9f475baded433af937cd3a",
+            "span_id": "a6d8e8401c38eb7e",
+            "parent_span_id": "aa5ecbdf2bf83338",
+            "op": "django.middleware",
+            "description": "django.middleware.clickjacking.XFrameOptionsMiddleware.__call__",
+        },
+        "runtime": {
+            "name": "CPython",
+            "version": "3.8.1",
+            "build": "3.8.1 (default, Jan  3 2020, 22:55:55) \n[GCC 8.3.0]",
+        },
+    },
+    "modules": {
+        "wheel": "0.33.6",
+        "webencodings": "0.5.1",
+        "wcwidth": "0.1.8",
+        "urllib3": "1.25.7",
+        "typed-ast": "1.4.0",
+        "traitlets": "4.3.3",
+        "tomlkit": "0.5.8",
+        "toml": "0.10.0",
+        "sqlparse": "0.3.0",
+        "six": "1.13.0",
+        "shellingham": "1.3.1",
+        "setuptools": "44.0.0",
+        "sentry-sdk": "0.14.0",
+        "secretstorage": "3.1.1",
+        "requests": "2.22.0",
+        "requests-toolbelt": "0.8.0",
+        "regex": "2020.1.8",
+        "pytz": "2019.3",
+        "pyrsistent": "0.14.11",
+        "pyparsing": "2.4.6",
+        "pylev": "1.3.0",
+        "pygments": "2.5.2",
+        "pycparser": "2.19",
+        "ptyprocess": "0.6.0",
+        "prompt-toolkit": "3.0.2",
+        "poetry": "1.0.0",
+        "pkginfo": "1.5.0.1",
+        "pip": "19.3.1",
+        "pickleshare": "0.7.5",
+        "pexpect": "4.7.0",
+        "pathspec": "0.7.0",
+        "pastel": "0.1.1",
+        "parso": "0.5.2",
+        "msgpack": "0.6.2",
+        "lockfile": "0.12.2",
+        "keyring": "19.3.0",
+        "jsonschema": "3.2.0",
+        "jeepney": "0.4.2",
+        "jedi": "0.15.2",
+        "ipython": "7.11.1",
+        "ipython-genutils": "0.2.0",
+        "ipdb": "0.12.3",
+        "idna": "2.8",
+        "html5lib": "1.0.1",
+        "django": "3.0.2",
+        "decorator": "4.4.1",
+        "cryptography": "2.8",
+        "clikit": "0.4.1",
+        "click": "7.0",
+        "cleo": "0.7.6",
+        "chardet": "3.0.4",
+        "cffi": "1.13.2",
+        "certifi": "2019.11.28",
+        "cachy": "0.3.0",
+        "cachecontrol": "0.12.6",
+        "black": "19.10b0",
+        "backcall": "0.1.0",
+        "attrs": "19.3.0",
+        "asgiref": "3.2.3",
+        "appdirs": "1.4.3",
+    },
+    "extra": {"sys.argv": ["./manage.py", "runserver", "0.0.0.0:8001"]},
+    "request": {
+        "url": "http://localhost:8001/database-stack-error/",
+        "query_string": "",
+        "method": "GET",
+        "env": {"SERVER_NAME": "210ecca56d59", "SERVER_PORT": "8001"},
+        "headers": {
+            "Content-Length": "",
+            "Content-Type": "text/plain",
+            "Host": "localhost:8001",
+            "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0",
+            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
+            "Accept-Language": "en-US,en;q=0.5",
+            "Accept-Encoding": "gzip, deflate",
+            "Connection": "keep-alive",
+            "Referer": "http://localhost:8001/",
+            "Cookie": "",
+            "Upgrade-Insecure-Requests": "1",
+            "Dnt": "1",
+        },
+    },
+    "server_name": "210ecca56d59",
+    "sdk": {
+        "name": "sentry.python",
+        "version": "0.14.0",
+        "packages": [{"name": "pypi:sentry-sdk", "version": "0.14.0"}],
+        "integrations": [
+            "argv",
+            "atexit",
+            "dedupe",
+            "django",
+            "excepthook",
+            "logging",
+            "modules",
+            "stdlib",
+            "threading",
+        ],
+    },
+    "platform": "python",
+    "_meta": {
+        "request": {"headers": {"Cookie": {"": {"rem": [["!config", "x", 0, 315]]}}}}
+    },
+}
+
+template_error = {
+    "level": "error",
+    "exception": {
+        "values": [
+            {
+                "module": "django.urls.exceptions",
+                "type": "NoReverseMatch",
+                "value": "Reverse for 'nope' not found. 'nope' is not a valid view function or pattern name.",
+                "mechanism": {"type": "django", "handled": False},
+                "stacktrace": {
+                    "frames": [
+                        {
+                            "filename": "django/core/handlers/exception.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py",
+                            "function": "inner",
+                            "module": "django.core.handlers.exception",
+                            "lineno": 34,
+                            "pre_context": [
+                                "    can rely on getting a response instead of an exception.",
+                                '    """',
+                                "    @wraps(get_response)",
+                                "    def inner(request):",
+                                "        try:",
+                            ],
+                            "context_line": "            response = get_response(request)",
+                            "post_context": [
+                                "        except Exception as exc:",
+                                "            response = response_for_exception(request, exc)",
+                                "        return response",
+                                "    return inner",
+                                "",
+                            ],
+                            "vars": {
+                                "request": "<WSGIRequest: GET '/template-error/'>",
+                                "exc": "NoReverseMatch(\"Reverse for 'nope' not found. 'nope' is not a valid view function or pattern name.\")",
+                                "get_response": "<bound method BaseHandler._get_response of <django.core.handlers.wsgi.WSGIHandler object at 0x7f62d45864f0>>",
+                            },
+                        },
+                        {
+                            "filename": "django/core/handlers/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py",
+                            "function": "_get_response",
+                            "module": "django.core.handlers.base",
+                            "lineno": 145,
+                            "pre_context": [
+                                "                    )",
+                                "",
+                                "            try:",
+                                "                response = response.render()",
+                                "            except Exception as e:",
+                            ],
+                            "context_line": "                response = self.process_exception_by_middleware(e, request)",
+                            "post_context": [
+                                "",
+                                "        return response",
+                                "",
+                                "    def process_exception_by_middleware(self, exception, request):",
+                                '        """',
+                            ],
+                            "vars": {
+                                "self": "<django.core.handlers.wsgi.WSGIHandler object at 0x7f62d45864f0>",
+                                "request": "<WSGIRequest: GET '/template-error/'>",
+                                "response": '<TemplateResponse status_code=200, "text/html; charset=utf-8">',
+                                "resolver": "<URLResolver 'django_error_factory.urls' (None:None) '^/'>",
+                                "resolver_match": "ResolverMatch(func=errors.views.TemplateErrorView, args=(), kwargs={}, url_name=template_error, app_names=[], namespaces=[], route=template-error/)",
+                                "callback": "<function TemplateErrorView at 0x7f62d403d3a0>",
+                                "callback_args": [],
+                                "callback_kwargs": {},
+                                "middleware_method": "<function CsrfViewMiddleware.process_view at 0x7f62d3710040>",
+                                "wrapped_callback": "<function TemplateErrorView at 0x7f62d403d3a0>",
+                            },
+                        },
+                        {
+                            "filename": "django/core/handlers/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py",
+                            "function": "_get_response",
+                            "module": "django.core.handlers.base",
+                            "lineno": 143,
+                            "pre_context": [
+                                '                        "HttpResponse object. It returned None instead."',
+                                "                        % (middleware_method.__self__.__class__.__name__)",
+                                "                    )",
+                                "",
+                                "            try:",
+                            ],
+                            "context_line": "                response = response.render()",
+                            "post_context": [
+                                "            except Exception as e:",
+                                "                response = self.process_exception_by_middleware(e, request)",
+                                "",
+                                "        return response",
+                                "",
+                            ],
+                            "vars": {
+                                "self": "<django.core.handlers.wsgi.WSGIHandler object at 0x7f62d45864f0>",
+                                "request": "<WSGIRequest: GET '/template-error/'>",
+                                "response": '<TemplateResponse status_code=200, "text/html; charset=utf-8">',
+                                "resolver": "<URLResolver 'django_error_factory.urls' (None:None) '^/'>",
+                                "resolver_match": "ResolverMatch(func=errors.views.TemplateErrorView, args=(), kwargs={}, url_name=template_error, app_names=[], namespaces=[], route=template-error/)",
+                                "callback": "<function TemplateErrorView at 0x7f62d403d3a0>",
+                                "callback_args": [],
+                                "callback_kwargs": {},
+                                "middleware_method": "<function CsrfViewMiddleware.process_view at 0x7f62d3710040>",
+                                "wrapped_callback": "<function TemplateErrorView at 0x7f62d403d3a0>",
+                            },
+                        },
+                        {
+                            "filename": "django/template/response.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/template/response.py",
+                            "function": "render",
+                            "module": "django.template.response",
+                            "lineno": 105,
+                            "pre_context": [
+                                "",
+                                "        Return the baked response instance.",
+                                '        """',
+                                "        retval = self",
+                                "        if not self._is_rendered:",
+                            ],
+                            "context_line": "            self.content = self.rendered_content",
+                            "post_context": [
+                                "            for post_callback in self._post_render_callbacks:",
+                                "                newretval = post_callback(retval)",
+                                "                if newretval is not None:",
+                                "                    retval = newretval",
+                                "        return retval",
+                            ],
+                            "vars": {
+                                "self": '<TemplateResponse status_code=200, "text/html; charset=utf-8">',
+                                "retval": '<TemplateResponse status_code=200, "text/html; charset=utf-8">',
+                            },
+                        },
+                        {
+                            "filename": "django/template/response.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/template/response.py",
+                            "function": "rendered_content",
+                            "module": "django.template.response",
+                            "lineno": 83,
+                            "pre_context": [
+                                "        response content, you must either call render(), or set the",
+                                "        content explicitly using the value of this property.",
+                                '        """',
+                                "        template = self.resolve_template(self.template_name)",
+                                "        context = self.resolve_context(self.context_data)",
+                            ],
+                            "context_line": "        return template.render(context, self._request)",
+                            "post_context": [
+                                "",
+                                "    def add_post_render_callback(self, callback):",
+                                '        """Add a new post-rendering callback.',
+                                "",
+                                "        If the response has already been rendered,",
+                            ],
+                            "vars": {
+                                "self": '<TemplateResponse status_code=200, "text/html; charset=utf-8">',
+                                "template": "<django.template.backends.django.Template object at 0x7f62d224dbe0>",
+                                "context": {
+                                    "view": "<errors.views.TemplateErrorView object at 0x7f62d35bd430>"
+                                },
+                            },
+                        },
+                        {
+                            "filename": "django/template/backends/django.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/template/backends/django.py",
+                            "function": "render",
+                            "module": "django.template.backends.django",
+                            "lineno": 61,
+                            "pre_context": [
+                                "        return self.template.origin",
+                                "",
+                                "    def render(self, context=None, request=None):",
+                                "        context = make_context(context, request, autoescape=self.backend.engine.autoescape)",
+                                "        try:",
+                            ],
+                            "context_line": "            return self.template.render(context)",
+                            "post_context": [
+                                "        except TemplateDoesNotExist as exc:",
+                                "            reraise(exc, self.backend)",
+                                "",
+                                "",
+                                "def copy_exception(exc, backend=None):",
+                            ],
+                            "vars": {
+                                "self": "<django.template.backends.django.Template object at 0x7f62d224dbe0>",
+                                "context": "[{'True': True, 'False': False, 'None': None}, {}, {}, {'view': <errors.views.TemplateErrorView object at 0x7f62d35bd430>}]",
+                                "request": "<WSGIRequest: GET '/template-error/'>",
+                            },
+                        },
+                        {
+                            "filename": "django/template/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/template/base.py",
+                            "function": "render",
+                            "module": "django.template.base",
+                            "lineno": 171,
+                            "pre_context": [
+                                '        "Display stage -- can be called many times"',
+                                "        with context.render_context.push_state(self):",
+                                "            if context.template is None:",
+                                "                with context.bind_template(self):",
+                                "                    context.template_name = self.name",
+                            ],
+                            "context_line": "                    return self._render(context)",
+                            "post_context": [
+                                "            else:",
+                                "                return self._render(context)",
+                                "",
+                                "    def compile_nodelist(self):",
+                                '        """',
+                            ],
+                            "vars": {
+                                "self": "<django.template.base.Template object at 0x7f62d224dc10>",
+                                "context": "[{'True': True, 'False': False, 'None': None}, {}, {}, {'view': <errors.views.TemplateErrorView object at 0x7f62d35bd430>}]",
+                            },
+                        },
+                        {
+                            "filename": "django/template/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/template/base.py",
+                            "function": "_render",
+                            "module": "django.template.base",
+                            "lineno": 163,
+                            "pre_context": [
+                                "    def __iter__(self):",
+                                "        for node in self.nodelist:",
+                                "            yield from node",
+                                "",
+                                "    def _render(self, context):",
+                            ],
+                            "context_line": "        return self.nodelist.render(context)",
+                            "post_context": [
+                                "",
+                                "    def render(self, context):",
+                                '        "Display stage -- can be called many times"',
+                                "        with context.render_context.push_state(self):",
+                                "            if context.template is None:",
+                            ],
+                            "vars": {
+                                "self": "<django.template.base.Template object at 0x7f62d224dc10>",
+                                "context": "[{'True': True, 'False': False, 'None': None}, {}, {}, {'view': <errors.views.TemplateErrorView object at 0x7f62d35bd430>}]",
+                            },
+                        },
+                        {
+                            "filename": "django/template/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/template/base.py",
+                            "function": "render",
+                            "module": "django.template.base",
+                            "lineno": 936,
+                            "pre_context": [
+                                "",
+                                "    def render(self, context):",
+                                "        bits = []",
+                                "        for node in self:",
+                                "            if isinstance(node, Node):",
+                            ],
+                            "context_line": "                bit = node.render_annotated(context)",
+                            "post_context": [
+                                "            else:",
+                                "                bit = node",
+                                "            bits.append(str(bit))",
+                                "        return mark_safe(''.join(bits))",
+                                "",
+                            ],
+                            "vars": {
+                                "self": [
+                                    "<TextNode: '<a href=\"'>",
+                                    "<django.template.defaulttags.URLNode object at 0x7f62d224d160>",
+                                    "<TextNode: '\">'>",
+                                ],
+                                "context": "[{'True': True, 'False': False, 'None': None}, {}, {}, {'view': <errors.views.TemplateErrorView object at 0x7f62d35bd430>}]",
+                                "bits": ["'<a href=\"'"],
+                                "node": "<django.template.defaulttags.URLNode object at 0x7f62d224d160>",
+                                "bit": "'<a href=\"'",
+                            },
+                        },
+                        {
+                            "filename": "/code/errors/templates/template_error.html",
+                            "lineno": 1,
+                            "pre_context": [],
+                            "post_context": [],
+                            "context_line": "&lt;a href=&quot;{% url &#x27;nope&#x27; %}&quot;&gt;",
+                            "in_app": True,
+                        },
+                        {
+                            "filename": "django/template/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/template/base.py",
+                            "function": "render_annotated",
+                            "module": "django.template.base",
+                            "lineno": 903,
+                            "pre_context": [
+                                "        rendering, the exception is annotated with contextual line information",
+                                "        where it occurred in the template. For internal usage this method is",
+                                "        preferred over using the render method directly.",
+                                '        """',
+                                "        try:",
+                            ],
+                            "context_line": "            return self.render(context)",
+                            "post_context": [
+                                "        except Exception as e:",
+                                "            if context.template.engine.debug and not hasattr(e, 'template_debug'):",
+                                "                e.template_debug = context.render_context.template.get_exception_info(e, self.token)",
+                                "            raise",
+                                "",
+                            ],
+                            "vars": {
+                                "self": "<django.template.defaulttags.URLNode object at 0x7f62d224d160>",
+                                "context": "[{'True': True, 'False': False, 'None': None}, {}, {}, {'view': <errors.views.TemplateErrorView object at 0x7f62d35bd430>}]",
+                            },
+                        },
+                        {
+                            "filename": "django/template/defaulttags.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/template/defaulttags.py",
+                            "function": "render",
+                            "module": "django.template.defaulttags",
+                            "lineno": 443,
+                            "pre_context": [
+                                "                current_app = None",
+                                "        # Try to look up the URL. If it fails, raise NoReverseMatch unless the",
+                                "        # {% url ... as var %} construct is used, in which case return nothing.",
+                                "        url = ''",
+                                "        try:",
+                            ],
+                            "context_line": "            url = reverse(view_name, args=args, kwargs=kwargs, current_app=current_app)",
+                            "post_context": [
+                                "        except NoReverseMatch:",
+                                "            if self.asvar is None:",
+                                "                raise",
+                                "",
+                                "        if self.asvar:",
+                            ],
+                            "vars": {
+                                "self": "<django.template.defaulttags.URLNode object at 0x7f62d224d160>",
+                                "reverse": "<function reverse at 0x7f62d4757820>",
+                                "NoReverseMatch": "<class 'django.urls.exceptions.NoReverseMatch'>",
+                                "args": [],
+                                "kwargs": {},
+                                "view_name": "'nope'",
+                                "current_app": "''",
+                                "url": "''",
+                                "context": "[{'True': True, 'False': False, 'None': None}, {}, {}, {'view': <errors.views.TemplateErrorView object at 0x7f62d35bd430>}]",
+                            },
+                        },
+                        {
+                            "filename": "django/urls/base.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/urls/base.py",
+                            "function": "reverse",
+                            "module": "django.urls.base",
+                            "lineno": 87,
+                            "pre_context": [
+                                "                else:",
+                                '                    raise NoReverseMatch("%s is not a registered namespace" % key)',
+                                "        if ns_pattern:",
+                                "            resolver = get_ns_resolver(ns_pattern, resolver, tuple(ns_converters.items()))",
+                                "",
+                            ],
+                            "context_line": "    return iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs))",
+                            "post_context": [
+                                "",
+                                "",
+                                "reverse_lazy = lazy(reverse, str)",
+                                "",
+                                "",
+                            ],
+                            "vars": {
+                                "viewname": "'nope'",
+                                "urlconf": "'django_error_factory.urls'",
+                                "args": [],
+                                "kwargs": {},
+                                "current_app": "''",
+                                "resolver": "<URLResolver 'django_error_factory.urls' (None:None) '^/'>",
+                                "prefix": "'/'",
+                                "view": "'nope'",
+                                "path": [],
+                                "current_path": "None",
+                            },
+                        },
+                        {
+                            "filename": "django/urls/resolvers.py",
+                            "abs_path": "/usr/local/lib/python3.8/site-packages/django/urls/resolvers.py",
+                            "function": "_reverse_with_prefix",
+                            "module": "django.urls.resolvers",
+                            "lineno": 677,
+                            "pre_context": [
+                                "        else:",
+                                "            msg = (",
+                                "                \"Reverse for '%(view)s' not found. '%(view)s' is not \"",
+                                "                \"a valid view function or pattern name.\" % {'view': lookup_view_s}",
+                                "            )",
+                            ],
+                            "context_line": "        raise NoReverseMatch(msg)",
+                            "post_context": [],
+                            "vars": {
+                                "self": "<URLResolver 'django_error_factory.urls' (None:None) '^/'>",
+                                "lookup_view": "'nope'",
+                                "_prefix": "'/'",
+                                "args": [],
+                                "possibilities": [],
+                                "m": "None",
+                                "n": "None",
+                                "lookup_view_s": "'nope'",
+                                "patterns": [],
+                                "msg": "\"Reverse for 'nope' not found. 'nope' is not a valid view function or pattern name.\"",
+                            },
+                        },
+                    ]
+                },
+            }
+        ]
+    },
+    "event_id": "3ba2cf965903444fa9b16eabe7ac6332",
+    "timestamp": "2020-01-10T20:26:07.030933Z",
+    "breadcrumbs": [],
+    "transaction": "/template-error/",
+    "contexts": {
+        "trace": {
+            "trace_id": "3ef6422833ec48fdaa099327be2b599c",
+            "span_id": "a5a2e2e178134e55",
+            "parent_span_id": "8f3f4c16e1f4232c",
+            "op": "django.middleware",
+            "description": "django.middleware.clickjacking.XFrameOptionsMiddleware.__call__",
+        },
+        "runtime": {
+            "name": "CPython",
+            "version": "3.8.1",
+            "build": "3.8.1 (default, Jan  3 2020, 22:55:55) \n[GCC 8.3.0]",
+        },
+    },
+    "modules": {
+        "wheel": "0.33.6",
+        "webencodings": "0.5.1",
+        "wcwidth": "0.1.8",
+        "urllib3": "1.25.7",
+        "typed-ast": "1.4.0",
+        "traitlets": "4.3.3",
+        "tomlkit": "0.5.8",
+        "toml": "0.10.0",
+        "sqlparse": "0.3.0",
+        "six": "1.13.0",
+        "shellingham": "1.3.1",
+        "setuptools": "44.0.0",
+        "sentry-sdk": "0.14.0",
+        "secretstorage": "3.1.1",
+        "requests": "2.22.0",
+        "requests-toolbelt": "0.8.0",
+        "regex": "2020.1.8",
+        "pytz": "2019.3",
+        "pyrsistent": "0.14.11",
+        "pyparsing": "2.4.6",
+        "pylev": "1.3.0",
+        "pygments": "2.5.2",
+        "pycparser": "2.19",
+        "ptyprocess": "0.6.0",
+        "prompt-toolkit": "3.0.2",
+        "poetry": "1.0.0",
+        "pkginfo": "1.5.0.1",
+        "pip": "19.3.1",
+        "pickleshare": "0.7.5",
+        "pexpect": "4.7.0",
+        "pathspec": "0.7.0",
+        "pastel": "0.1.1",
+        "parso": "0.5.2",
+        "msgpack": "0.6.2",
+        "lockfile": "0.12.2",
+        "keyring": "19.3.0",
+        "jsonschema": "3.2.0",
+        "jeepney": "0.4.2",
+        "jedi": "0.15.2",
+        "ipython": "7.11.1",
+        "ipython-genutils": "0.2.0",
+        "ipdb": "0.12.3",
+        "idna": "2.8",
+        "html5lib": "1.0.1",
+        "django": "3.0.2",
+        "decorator": "4.4.1",
+        "cryptography": "2.8",
+        "clikit": "0.4.1",
+        "click": "7.0",
+        "cleo": "0.7.6",
+        "chardet": "3.0.4",
+        "cffi": "1.13.2",
+        "certifi": "2019.11.28",
+        "cachy": "0.3.0",
+        "cachecontrol": "0.12.6",
+        "black": "19.10b0",
+        "backcall": "0.1.0",
+        "attrs": "19.3.0",
+        "asgiref": "3.2.3",
+        "appdirs": "1.4.3",
+    },
+    "extra": {"sys.argv": ["./manage.py", "runserver", "0.0.0.0:8001"]},
+    "request": {
+        "url": "http://localhost:8001/template-error/",
+        "query_string": "",
+        "method": "GET",
+        "env": {"SERVER_NAME": "210ecca56d59", "SERVER_PORT": "8001"},
+        "headers": {
+            "Content-Length": "",
+            "Content-Type": "text/plain",
+            "Host": "localhost:8001",
+            "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0",
+            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
+            "Accept-Language": "en-US,en;q=0.5",
+            "Accept-Encoding": "gzip, deflate",
+            "Referer": "http://localhost:8001/",
+            "Connection": "keep-alive",
+            "Cookie": "",
+            "Upgrade-Insecure-Requests": "1",
+            "Dnt": "1",
+            "Cache-Control": "max-age=0",
+        },
+    },
+    "server_name": "210ecca56d59",
+    "sdk": {
+        "name": "sentry.python",
+        "version": "0.14.0",
+        "packages": [{"name": "pypi:sentry-sdk", "version": "0.14.0"}],
+        "integrations": [
+            "argv",
+            "atexit",
+            "dedupe",
+            "django",
+            "excepthook",
+            "logging",
+            "modules",
+            "stdlib",
+            "threading",
+        ],
+    },
+    "platform": "python",
+    "_meta": {
+        "exception": {
+            "values": {
+                "0": {
+                    "stacktrace": {
+                        "frames": {
+                            "12": {"vars": {"": {"len": 13}}},
+                            "13": {"vars": {"": {"len": 11}}},
+                        }
+                    }
+                }
+            }
+        },
+        "request": {"headers": {"Cookie": {"": {"rem": [["!config", "x", 0, 315]]}}}},
+    },
+}
+
+all_django_events = [
+    divide_zero,
+    database_error,
+    post_error,
+    database_stack_error,
+    template_error,
+]

+ 15 - 0
event_store/test_data/event_generator.py

@@ -0,0 +1,15 @@
+import uuid
+import random
+from . import django_error_factory
+
+
+def make_event_unique(event):
+    """ Assign event a random new event_id """
+    event["event_id"] = uuid.uuid4().hex
+    return event
+
+
+def generate_random_event():
+    """ Return a random event from library of samples with unique event id """
+    event = random.choice(django_error_factory.all_django_events)
+    return make_event_unique(event)

+ 0 - 0
issues/test_data/js_event.json → event_store/test_data/js_event.json


+ 0 - 0
issues/test_data/py_error.json → event_store/test_data/py_error.json


+ 0 - 0
issues/test_data/py_hi_event.json → event_store/test_data/py_hi_event.json


+ 35 - 0
event_store/tests.py

@@ -0,0 +1,35 @@
+import json
+from rest_framework.test import APITestCase
+from glitchtip.test_utils import generators
+from model_bakery import baker
+
+
+class EventStoreTestCase(APITestCase):
+    def setUp(self):
+        self.project = baker.make("projects.Project")
+        self.projectkey = self.project.projectkey_set.first()
+        self.url = (
+            f"/api/{self.project.id}/store/?sentry_key={self.projectkey.public_key}"
+        )
+
+    def test_store_api(self):
+        with open("event_store/test_data/py_hi_event.json") as json_file:
+            data = json.load(json_file)
+        res = self.client.post(self.url, data, format="json")
+        self.assertEqual(res.status_code, 200)
+
+    def test_store_api_auth_failure(self):
+        url = "/api/1/store/"
+        with open("event_store/test_data/py_hi_event.json") as json_file:
+            data = json.load(json_file)
+        res = self.client.post(url, data, format="json")
+        self.assertEqual(res.status_code, 403)
+
+    def test_error_event(self):
+        with open("event_store/test_data/py_error.json") as json_file:
+            data = json.load(json_file)
+        res = self.client.post(self.url, data, format="json")
+
+    def test_default_event(self):
+        pass
+

+ 7 - 0
event_store/urls.py

@@ -0,0 +1,7 @@
+from django.urls import path
+from .views import EventStoreAPIView, MakeSampleErrorView
+
+urlpatterns = [
+    path("<int:id>/store/", EventStoreAPIView.as_view()),
+    path("make-sample-error/", MakeSampleErrorView.as_view()),
+]

Some files were not shown because too many files changed in this diff