Browse Source

Add device to events (refs GH-2605)

David Cramer 9 years ago
parent
commit
9841683705

+ 8 - 1
src/sentry/api/serializers/models/event.py

@@ -9,7 +9,7 @@ from sentry.models import Event, EventError
 
 @register(Event)
 class EventSerializer(Serializer):
-    _reserved_keys = frozenset(['sentry.interfaces.User', 'sdk'])
+    _reserved_keys = frozenset(['sentry.interfaces.User', 'sdk', 'device'])
 
     def _get_entries(self, event, user, is_public=False):
         # XXX(dcramer): These are called entries for future-proofing
@@ -38,6 +38,11 @@ class EventSerializer(Serializer):
                 user_data = user_interface.to_json()
             else:
                 user_data = None
+            device_interface = item.interfaces.get('device')
+            if device_interface:
+                device_data = device_interface.to_json()
+            else:
+                device_data = None
 
             sdk_interface = item.interfaces.get('sdk')
             if sdk_interface:
@@ -49,6 +54,7 @@ class EventSerializer(Serializer):
                 'entries': self._get_entries(item, user, is_public=is_public),
                 'user': user_data,
                 'sdk': sdk_data,
+                'device': device_data,
             }
         return results
 
@@ -98,6 +104,7 @@ class EventSerializer(Serializer):
             'message': obj.message,
             'user': attrs['user'],
             'sdk': attrs['sdk'],
+            'device': attrs['device'],
             'context': obj.data.get('extra', {}),
             'packages': obj.data.get('modules', {}),
             'tags': tags,

+ 4 - 3
src/sentry/conf/server.py

@@ -687,15 +687,16 @@ SENTRY_SMTP_HOST = 'localhost'
 SENTRY_SMTP_PORT = 1025
 
 SENTRY_INTERFACES = {
+    'csp': 'sentry.interfaces.csp.Csp',
+    'device': 'sentry.interfaces.device.Device',
     'exception': 'sentry.interfaces.exception.Exception',
     'logentry': 'sentry.interfaces.message.Message',
+    'query': 'sentry.interfaces.query.Query',
     'request': 'sentry.interfaces.http.Http',
+    'sdk': 'sentry.interfaces.sdk.Sdk',
     'stacktrace': 'sentry.interfaces.stacktrace.Stacktrace',
     'template': 'sentry.interfaces.template.Template',
-    'query': 'sentry.interfaces.query.Query',
     'user': 'sentry.interfaces.user.User',
-    'csp': 'sentry.interfaces.csp.Csp',
-    'sdk': 'sentry.interfaces.sdk.Sdk',
     'applecrashreport': 'sentry.interfaces.applecrash.AppleCrashReport',
     'breadcrumbs': 'sentry.interfaces.breadcrumbs.Breadcrumbs',
     'sentry.interfaces.Exception': 'sentry.interfaces.exception.Exception',

+ 61 - 0
src/sentry/interfaces/device.py

@@ -0,0 +1,61 @@
+from __future__ import absolute_import
+
+__all__ = ('Device',)
+
+from sentry.interfaces.base import Interface, InterfaceValidationError
+from sentry.utils.safe import trim, trim_dict
+
+
+class Device(Interface):
+    """
+    An interface which describes the device.
+
+    >>> {
+    >>>     "name": "Windows",
+    >>>     "version": "95",
+    >>>     "build": "95.0.134.1651",
+    >>>     "arbitrary": "data"
+    >>> }
+    """
+    @classmethod
+    def to_python(cls, data):
+        data = data.copy()
+
+        extra_data = data.pop('data', data)
+        if not isinstance(extra_data, dict):
+            extra_data = {}
+
+        try:
+            name = trim(data.pop('name'), 64)
+        except KeyError:
+            raise InterfaceValidationError("Missing or invalid value for 'name'")
+
+        try:
+            version = trim(data.pop('version'), 64)
+        except KeyError:
+            raise InterfaceValidationError("Missing or invalid value for 'version'")
+
+        build = trim(data.pop('build', None), 64)
+
+        kwargs = {
+            'name': name,
+            'version': version,
+            'build': build,
+            'data': trim_dict(data),
+        }
+        kwargs['data'] = trim_dict(data)
+        return cls(**kwargs)
+
+    def get_api_context(self, is_public=False):
+        return {
+            'name': self.name,
+            'version': self.version,
+            'build': self.build,
+            'data': self.data,
+        }
+
+    def get_path(self):
+        return 'device'
+
+    def get_hash(self):
+        return []

+ 35 - 0
tests/sentry/interfaces/test_device.py

@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import
+
+import pytest
+
+from sentry.interfaces.base import InterfaceValidationError
+from sentry.interfaces.device import Device
+from sentry.testutils import TestCase
+
+
+class DeviceTest(TestCase):
+    def test_serialize_behavior(self):
+        assert Device.to_python({
+            'name': 'Windows',
+            'version': '95',
+        }).to_json() == {
+            'name': 'Windows',
+            'version': '95',
+        }
+
+    def test_missing_name(self):
+        with pytest.raises(InterfaceValidationError):
+            assert Device.to_python({
+                'version': '95',
+            })
+
+    def test_missing_version(self):
+        with pytest.raises(InterfaceValidationError):
+            assert Device.to_python({
+                'name': 'Windows',
+            })
+
+    def test_path(self):
+        assert Device().get_path() == 'device'