serializers.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. from rest_framework import serializers
  2. from projects.serializers.base_serializers import ProjectReferenceSerializer
  3. from user_reports.serializers import UserReportSerializer
  4. from sentry.interfaces.stacktrace import get_context
  5. from glitchtip.serializers import FlexibleDateTimeField
  6. from releases.serializers import ReleaseSerializer
  7. from .models import Issue, Event, EventTag, EventType, EventStatus
  8. class EventTagSerializer(serializers.ModelSerializer):
  9. key = serializers.StringRelatedField()
  10. class Meta:
  11. model = EventTag
  12. fields = ("key", "value")
  13. class EventUserSerializer(serializers.Serializer):
  14. username = serializers.CharField(allow_null=True)
  15. name = serializers.CharField(allow_null=True)
  16. ip_address = serializers.IPAddressField(allow_null=True)
  17. email = serializers.EmailField(allow_null=True)
  18. data = serializers.JSONField(default={})
  19. id = serializers.CharField(allow_null=True)
  20. class BaseBreadcrumbsSerializer(serializers.Serializer):
  21. category = serializers.CharField()
  22. level = serializers.CharField(default="info")
  23. event_id = serializers.CharField(required=False)
  24. data = serializers.JSONField(required=False)
  25. message = serializers.CharField(required=False)
  26. type = serializers.CharField(default="default")
  27. class BreadcrumbsSerializer(BaseBreadcrumbsSerializer):
  28. timestamp = FlexibleDateTimeField()
  29. message = serializers.CharField(default=None)
  30. event_id = serializers.CharField(default=None)
  31. data = serializers.JSONField(default=None)
  32. class EventEntriesSerializer(serializers.Serializer):
  33. def to_representation(self, instance):
  34. def get_has_system_frames(frames):
  35. return any(frame.in_app for frame in frames)
  36. entries = []
  37. exception = instance.get("exception")
  38. # Some, but not all, keys are made more JS camel case like
  39. if exception and exception.get("values"):
  40. # https://gitlab.com/glitchtip/sentry-open-source/sentry/-/blob/master/src/sentry/interfaces/stacktrace.py#L487
  41. # if any frame is "in_app" set this to True
  42. exception["hasSystemFrames"] = False
  43. for value in exception["values"]:
  44. if "stacktrace" in value and "frames" in value["stacktrace"]:
  45. for frame in value["stacktrace"]["frames"]:
  46. if frame.get("in_app") == True:
  47. exception["hasSystemFrames"] = True
  48. if "in_app" in frame:
  49. frame["inApp"] = frame.pop("in_app")
  50. if "abs_path" in frame:
  51. frame["absPath"] = frame.pop("abs_path")
  52. if "colno" in frame:
  53. frame["colNo"] = frame.pop("colno")
  54. if "lineno" in frame:
  55. frame["lineNo"] = frame.pop("lineno")
  56. pre_context = frame.pop("pre_context", None)
  57. post_context = frame.pop("post_context", None)
  58. frame["context"] = get_context(
  59. frame["lineNo"],
  60. frame.get("context_line"),
  61. pre_context,
  62. post_context,
  63. )
  64. entries.append({"type": "exception", "data": exception})
  65. breadcrumbs = instance.get("breadcrumbs")
  66. if breadcrumbs:
  67. breadcrumbs_serializer = BreadcrumbsSerializer(
  68. data=breadcrumbs.get("values"), many=True
  69. )
  70. if breadcrumbs_serializer.is_valid():
  71. entries.append(
  72. {
  73. "type": "breadcrumbs",
  74. "data": {"values": breadcrumbs_serializer.validated_data},
  75. }
  76. )
  77. request = instance.get("request")
  78. if request:
  79. request["inferredContentType"] = request.pop("inferred_content_type")
  80. entries.append({"type": "request", "data": request})
  81. message = instance.get("message")
  82. if message:
  83. entries.append({"type": "message", "data": {"formatted": message}})
  84. csp = instance.get("csp")
  85. if csp:
  86. entries.append({"type": EventType.CSP.label, "data": csp})
  87. return entries
  88. class EventSerializer(serializers.ModelSerializer):
  89. eventID = serializers.CharField(source="event_id_hex")
  90. id = serializers.CharField(source="event_id_hex")
  91. dateCreated = serializers.DateTimeField(source="timestamp")
  92. dateReceived = serializers.DateTimeField(source="created")
  93. entries = EventEntriesSerializer(source="data")
  94. tags = EventTagSerializer(many=True)
  95. user = EventUserSerializer()
  96. class Meta:
  97. model = Event
  98. fields = (
  99. "eventID",
  100. "id",
  101. "issue",
  102. "context",
  103. "contexts",
  104. "culprit",
  105. "dateCreated",
  106. "dateReceived",
  107. "entries",
  108. # "errors",
  109. # "location",
  110. "message",
  111. "metadata",
  112. "packages",
  113. "platform",
  114. "sdk",
  115. "tags",
  116. "title",
  117. "type",
  118. "user",
  119. )
  120. class EventDetailSerializer(EventSerializer):
  121. projectID = serializers.IntegerField(source="issue.project_id")
  122. userReport = UserReportSerializer(source="user_report")
  123. nextEventID = serializers.SerializerMethodField()
  124. previousEventID = serializers.SerializerMethodField()
  125. release = ReleaseSerializer()
  126. class Meta(EventSerializer.Meta):
  127. fields = EventSerializer.Meta.fields + (
  128. "projectID",
  129. "userReport",
  130. "nextEventID",
  131. "previousEventID",
  132. "release",
  133. )
  134. def get_next_or_previous(self, obj, is_next):
  135. kwargs = self.context["view"].kwargs
  136. filter_kwargs = {}
  137. if kwargs.get("issue_pk"):
  138. filter_kwargs["issue"] = kwargs["issue_pk"]
  139. if is_next:
  140. result = obj.next(**filter_kwargs)
  141. else:
  142. result = obj.previous(**filter_kwargs)
  143. if result:
  144. return str(result)
  145. def get_nextEventID(self, obj):
  146. return self.get_next_or_previous(obj, True)
  147. def get_previousEventID(self, obj):
  148. return self.get_next_or_previous(obj, False)
  149. class DisplayChoiceField(serializers.ChoiceField):
  150. """
  151. ChoiceField that represents choice only as display value
  152. Useful if the API should only deal with display values
  153. """
  154. def to_representation(self, value):
  155. return self.choices[value]
  156. def to_internal_value(self, data):
  157. if data == "" and self.allow_blank:
  158. return ""
  159. choice_strings_to_values = {value: key for key, value in self.choices.items()}
  160. try:
  161. return choice_strings_to_values[str(data)]
  162. except KeyError:
  163. self.fail("invalid_choice", input=data)
  164. class IssueSerializer(serializers.ModelSerializer):
  165. annotations = serializers.JSONField(default=list, read_only=True)
  166. assignedTo = serializers.CharField(default=None, read_only=True)
  167. firstSeen = serializers.DateTimeField(source="created", read_only=True)
  168. hasSeen = serializers.BooleanField(source="has_seen", read_only=True)
  169. isBookmarked = serializers.BooleanField(default=False, read_only=True)
  170. isPublic = serializers.BooleanField(source="is_public", read_only=True)
  171. isSubscribed = serializers.BooleanField(default=False, read_only=True)
  172. lastSeen = serializers.DateTimeField(source="last_seen", read_only=True)
  173. level = serializers.CharField(source="get_level_display", read_only=True)
  174. logger = serializers.CharField(default=None, read_only=True)
  175. metadata = serializers.JSONField(default=dict, read_only=True)
  176. numComments = serializers.IntegerField(default=0, read_only=True)
  177. permalink = serializers.CharField(default="Not implemented", read_only=True)
  178. project = ProjectReferenceSerializer(read_only=True)
  179. shareId = serializers.IntegerField(default=None, read_only=True)
  180. shortId = serializers.CharField(source="short_id_display", read_only=True)
  181. stats = serializers.JSONField(default=dict, read_only=True)
  182. status = DisplayChoiceField(choices=EventStatus.choices)
  183. statusDetails = serializers.JSONField(default=dict, read_only=True)
  184. subscriptionDetails = serializers.CharField(default=None, read_only=True)
  185. type = serializers.CharField(source="get_type_display", read_only=True)
  186. userReportCount = serializers.IntegerField(
  187. source="userreport_set.count", read_only=True
  188. )
  189. userCount = serializers.IntegerField(default=0, read_only=True)
  190. class Meta:
  191. model = Issue
  192. fields = (
  193. "annotations",
  194. "assignedTo",
  195. "count",
  196. "culprit",
  197. "firstSeen",
  198. "hasSeen",
  199. "id",
  200. "isBookmarked",
  201. "isPublic",
  202. "isSubscribed",
  203. "lastSeen",
  204. "level",
  205. "logger",
  206. "metadata",
  207. "numComments",
  208. "permalink",
  209. "project",
  210. "shareId",
  211. "shortId",
  212. "stats",
  213. "status",
  214. "statusDetails",
  215. "subscriptionDetails",
  216. "title",
  217. "type",
  218. "userReportCount",
  219. "userCount",
  220. )
  221. read_only_fields = (
  222. "annotations",
  223. "assignedTo",
  224. "count",
  225. "culprit",
  226. "firstSeen",
  227. "hasSeen",
  228. "id",
  229. "isBookmarked",
  230. "isPublic",
  231. "isSubscribed",
  232. "lastSeen",
  233. "level",
  234. "logger",
  235. "metadata",
  236. "numComments",
  237. "permalink",
  238. "project",
  239. "shareId",
  240. "shortId",
  241. "stats",
  242. "subscriptionDetails",
  243. "title",
  244. "type",
  245. "userCount",
  246. )
  247. def to_representation(self, obj):
  248. """ Workaround for a field called "type" """
  249. primitive_repr = super().to_representation(obj)
  250. primitive_repr["type"] = obj.get_type_display()
  251. return primitive_repr