Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/sentry/api/endpoints/project_transaction_threshold.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def post(self, request: Request, project) -> Response:
)
project_threshold.threshold = data.get("threshold") or project_threshold.threshold
project_threshold.metric = data.get("metric") or project_threshold.metric
project_threshold.edited_by = request.user
project_threshold.edited_by_id = request.user.id
project_threshold.save()

created = False
Expand All @@ -101,7 +101,7 @@ def post(self, request: Request, project) -> Response:
organization=project.organization,
threshold=data.get("threshold", 300),
metric=data.get("metric", TransactionMetric.DURATION.value),
edited_by=request.user,
edited_by_id=request.user.id,
)

created = True
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def post(self, request: Request, organization) -> Response:
defaults={
"threshold": data["threshold"],
"metric": data["metric"],
"edited_by": request.user,
"edited_by_id": request.user.id,
},
)

Expand Down
30 changes: 24 additions & 6 deletions src/sentry/api/helpers/group_index/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
from sentry.models.grouphistory import record_group_history_from_activity_type
from sentry.models.groupinbox import GroupInboxRemoveAction, add_group_to_inbox
from sentry.notifications.types import SUBSCRIPTION_REASON_MAP, GroupSubscriptionReason
from sentry.services.hybrid_cloud.user import RpcUser
from sentry.services.hybrid_cloud.user import RpcUser, user_service
from sentry.signals import (
issue_ignored,
issue_mark_reviewed,
Expand All @@ -61,7 +61,6 @@
from sentry.tasks.merge import merge_groups
from sentry.types.activity import ActivityType
from sentry.utils import metrics
from sentry.utils.functional import extract_lazy_object

from . import ACTIVITIES_COUNT, BULK_MUTATION_LIMIT, SearchFunction, delete_group_list
from .validators import GroupValidator, ValidationError
Expand Down Expand Up @@ -280,10 +279,15 @@ def update_groups(
# no version yet
"version": ""
}

serialized_user = user_service.serialize_many(
filter=dict(user_ids=[user.id]), as_user=user
)
status_details = {
"inNextRelease": True,
"actor": serialize(extract_lazy_object(user), user),
}
if serialized_user:
status_details["actor"] = serialized_user[0]
res_type = GroupResolution.Type.in_next_release
res_type_str = "in_next_release"
res_status = GroupResolution.Status.pending
Expand All @@ -301,10 +305,15 @@ def update_groups(
# no version yet
"version": release.version
}

serialized_user = user_service.serialize_many(
filter=dict(user_ids=[user.id]), as_user=user
)
status_details = {
"inRelease": release.version,
"actor": serialize(extract_lazy_object(user), user),
}
if serialized_user:
status_details["actor"] = serialized_user[0]
res_type = GroupResolution.Type.in_release
res_type_str = "in_release"
res_status = GroupResolution.Status.resolved
Expand All @@ -318,10 +327,15 @@ def update_groups(
commit = statusDetails["inCommit"]
activity_type = ActivityType.SET_RESOLVED_IN_COMMIT.value
activity_data = {"commit": commit.id}
serialized_user = user_service.serialize_many(
filter=dict(user_ids=[user.id]), as_user=user
)

status_details = {
"inCommit": serialize(commit, user),
"actor": serialize(extract_lazy_object(user), user),
}
if serialized_user:
status_details["actor"] = serialized_user[0]
res_type_str = "in_commit"
else:
res_type_str = "now"
Expand Down Expand Up @@ -572,14 +586,18 @@ def update_groups(
"actor_id": user.id if user.is_authenticated else None,
},
)
serialized_user = user_service.serialize_many(
filter=dict(user_ids=[user.id]), as_user=user
)
result["statusDetails"] = {
"ignoreCount": ignore_count,
"ignoreUntil": ignore_until,
"ignoreUserCount": ignore_user_count,
"ignoreUserWindow": ignore_user_window,
"ignoreWindow": ignore_window,
"actor": serialize(extract_lazy_object(user), user),
}
if serialized_user:
result["statusDetails"]["actor"] = serialized_user[0]
else:
GroupSnooze.objects.filter(group__in=group_ids).delete()
ignore_until = None
Expand Down
32 changes: 20 additions & 12 deletions src/sentry/api/serializers/models/alert_rule.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from collections import defaultdict
from typing import MutableMapping

from django.db.models import Max, prefetch_related_objects

Expand All @@ -23,6 +24,7 @@
fetch_actors_by_actor_ids,
)
from sentry.services.hybrid_cloud.app import app_service
from sentry.services.hybrid_cloud.user import RpcUser, user_service
from sentry.snuba.models import SnubaQueryEventType


Expand Down Expand Up @@ -70,26 +72,32 @@ def get_attrs(self, item_list, user, **kwargs):
rule_result = result[alert_rules[alert_rule_id]].setdefault("projects", [])
rule_result.append(project_slug)

for rule_activity in AlertRuleActivity.objects.filter(
alert_rule__in=item_list, type=AlertRuleActivityType.CREATED.value
).select_related("alert_rule", "user"):
if rule_activity.user:
user = {
"id": rule_activity.user.id,
"name": rule_activity.user.get_display_name(),
"email": rule_activity.user.email,
}
rule_activities = list(
AlertRuleActivity.objects.filter(
alert_rule__in=item_list, type=AlertRuleActivityType.CREATED.value
)
)

use_by_user_id: MutableMapping[int, RpcUser] = {
user.id: user
for user in user_service.get_many(
filter=dict(user_ids=[r.user_id for r in rule_activities])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we get around to building RPC calls we should make sure we only send unique userids in the request body.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, ideally our service interfaces use "wide" types like Sequence so that I don't have to force a list. This is on my mind, too.

)
}
for rule_activity in rule_activities:
rpc_user = use_by_user_id.get(rule_activity.user_id)
if rpc_user:
user = dict(id=rpc_user.id, name=rpc_user.get_display_name(), email=rpc_user.email)
else:
user = None
result[alert_rules[rule_activity.alert_rule_id]]["created_by"] = user

result[alert_rules[rule_activity.alert_rule.id]].update({"created_by": user})

resolved_actors = {}
owners_by_type = defaultdict(list)
for item in item_list:
if item.owner_id is not None:
owners_by_type[actor_type_to_string(item.owner.type)].append(item.owner_id)

resolved_actors = {}
for k, v in ACTOR_TYPES.items():
resolved_actors[k] = {
a.actor_id: a.id
Expand Down
30 changes: 12 additions & 18 deletions src/sentry/api/serializers/models/discoversavedquery.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,28 @@
from collections import defaultdict

from django.db.models.query import prefetch_related_objects

from sentry.api.serializers import Serializer, register, serialize
from sentry.api.serializers.models.user import UserSerializer
from sentry.api.serializers import Serializer, register
from sentry.constants import ALL_ACCESS_PROJECTS
from sentry.discover.models import DiscoverSavedQuery
from sentry.services.hybrid_cloud.user import user_service
from sentry.utils.dates import outside_retention_with_modified_start, parse_timestamp


@register(DiscoverSavedQuery)
class DiscoverSavedQuerySerializer(Serializer):
def get_attrs(self, item_list, user):
prefetch_related_objects(item_list, "created_by")

result = defaultdict(lambda: {"created_by": {}})

user_serializer = UserSerializer()
serialized_users = {
user["id"]: user
for user in serialize(
[
discover_saved_query.created_by
service_serialized = user_service.serialize_many(
filter={
"user_ids": [
discover_saved_query.created_by_id
for discover_saved_query in item_list
if discover_saved_query.created_by
],
user=user,
serializer=user_serializer,
)
}
if discover_saved_query.created_by_id
]
},
as_user=user,
)
serialized_users = {user["id"]: user for user in service_serialized}

for discover_saved_query in item_list:
result[discover_saved_query]["created_by"] = serialized_users.get(
Expand Down
32 changes: 19 additions & 13 deletions src/sentry/api/serializers/models/exporteddata.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
from sentry.api.serializers import Serializer, register, serialize
from sentry.api.serializers import Serializer, register
from sentry.data_export.base import ExportQueryType
from sentry.data_export.models import ExportedData
from sentry.models import User
from sentry.services.hybrid_cloud.user import user_service


@register(ExportedData)
class ExportedDataSerializer(Serializer):
def get_attrs(self, item_list, user, **kwargs):
attrs = {}
users = User.objects.filter(id__in={item.user_id for item in item_list})
user_lookup = {user.id: user for user in users}
serialized_users = {
u["id"]: u
for u in user_service.serialize_many(
filter=dict(user_ids=[item.user_id for item in item_list])
)
}
Comment on lines +11 to +16
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We seem to do this a bunch. Should the user_service expose a serialize_many_map method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In, general, I think the serialize_many should be returning a dictionary, so I agree. I had been thinking about making a larger refactor for this purpose.

I'd still prefer to keep the service interfaces thing (one way of doing things, not 3), but in this case returning by dictionary is actually preferable to the list, so I may replace it that way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll make some follow up work to consider refactoring all serialize_many call sites into this convention.

for item in item_list:
user = user_lookup[item.user_id]
serialized_user = serialize(user)
attrs[item] = {
"user": {
"id": serialized_user["id"],
"email": serialized_user["email"],
"username": serialized_user["username"],
if str(item.user_id) in serialized_users:
serialized_user = serialized_users[str(item.user_id)]
attrs[item] = {
"user": {
"id": serialized_user["id"],
"email": serialized_user["email"],
"username": serialized_user["username"],
}
}
}
else:
attrs[item] = {}
return attrs

def serialize(self, obj, attrs, user, **kwargs):
Expand All @@ -33,7 +39,7 @@ def serialize(self, obj, attrs, user, **kwargs):

return {
"id": obj.id,
"user": attrs["user"],
"user": attrs.get("user"),
"dateCreated": obj.date_added,
"dateFinished": obj.date_finished,
"dateExpired": obj.date_expired,
Expand Down
45 changes: 27 additions & 18 deletions src/sentry/api/serializers/models/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
Optional,
Protocol,
Sequence,
Set,
Tuple,
TypedDict,
Union,
Expand All @@ -35,7 +36,6 @@
from sentry.constants import LOG_LEVELS
from sentry.issues.grouptype import GroupCategory
from sentry.models import (
ActorTuple,
Commit,
Environment,
Group,
Expand Down Expand Up @@ -183,19 +183,32 @@ def __init__(
self.collapse = collapse
self.expand = expand

def _serialize_assigness(
self, actor_dict: Mapping[int, ActorTuple]
) -> Mapping[int, Union[Team, Any]]:
actors_by_type: MutableMapping[Any, List[ActorTuple]] = defaultdict(list)
for actor in actor_dict.values():
actors_by_type[actor.type].append(actor)
def _serialize_assignees(self, item_list: Sequence[Group]) -> Mapping[int, Union[Team, Any]]:
gas = GroupAssignee.objects.filter(group__in=item_list)
result: MutableMapping[int, Union[Team, Any]] = {}
all_team_ids: MutableMapping[int, Set[int]] = {}
all_user_ids: MutableMapping[int, Set[int]] = {}
Comment on lines +189 to +190
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: You could make these defaultdict() to save the conditions on 193 and 199


for g in gas:
if g.team_id:
if g.team_id not in all_team_ids:
all_team_ids[g.team_id] = {g.group_id}
else:
all_team_ids[g.team_id].add(g.group_id)
if g.user_id:
if g.team_id not in all_team_ids:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if g.team_id not in all_team_ids:
if g.user_id not in all_user_ids:

all_user_ids[g.user_id] = {g.group_id}
else:
all_user_ids[g.user_id].add(g.group_id)

for team in Team.objects.filter(id__in=all_team_ids.keys()):
for group_id in all_team_ids[team.id]:
result[group_id] = team
for user in user_service.get_many(filter=dict(user_ids=list(all_user_ids.keys()))):
for group_id in all_user_ids[user.id]:
result[group_id] = user

resolved_actors = {}
for t, actors in actors_by_type.items():
serializable = ActorTuple.resolve_many(actors)
resolved_actors[t] = {actor.id: actor for actor in serializable}

return {key: resolved_actors[value.type][value.id] for key, value in actor_dict.items()}
return result

def get_attrs(
self, item_list: Sequence[Group], user: Any, **kwargs: Any
Expand Down Expand Up @@ -223,11 +236,7 @@ def get_attrs(
seen_groups = {}
subscriptions = defaultdict(lambda: (False, False, None))

assignees: Mapping[int, ActorTuple] = {
a.group_id: a.assigned_actor()
for a in GroupAssignee.objects.filter(group__in=item_list)
}
resolved_assignees = self._serialize_assigness(assignees)
resolved_assignees = self._serialize_assignees(item_list)

ignore_items = {g.group_id: g for g in GroupSnooze.objects.filter(group__in=item_list)}

Expand Down
10 changes: 4 additions & 6 deletions src/sentry/api/serializers/models/incident.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@ def get_attrs(self, item_list, user, **kwargs):

if "seen_by" in self.expand:
incident_seen_list = list(
IncidentSeen.objects.filter(incident__in=item_list)
.select_related("user")
.order_by("-last_seen")
IncidentSeen.objects.filter(incident__in=item_list).order_by("-last_seen")
)
incident_seen_dict = defaultdict(list)
for incident_seen, serialized_seen_by in zip(
Expand Down Expand Up @@ -108,9 +106,9 @@ def get_attrs(self, item_list, user, **kwargs):
subscribed_incidents = set()
if user.is_authenticated:
subscribed_incidents = set(
IncidentSubscription.objects.filter(incident__in=item_list, user=user).values_list(
"incident_id", flat=True
)
IncidentSubscription.objects.filter(
incident__in=item_list, user_id=user.id
).values_list("incident_id", flat=True)
)

for item in item_list:
Expand Down
12 changes: 4 additions & 8 deletions src/sentry/api/serializers/models/incidentactivity.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
from django.db.models import prefetch_related_objects

from sentry.api.serializers import Serializer, register, serialize
from sentry.api.serializers.models.user import UserSerializer
from sentry.api.serializers import Serializer, register
from sentry.incidents.models import IncidentActivity
from sentry.services.hybrid_cloud.user import user_service


@register(IncidentActivity)
class IncidentActivitySerializer(Serializer):
def get_attrs(self, item_list, user, **kwargs):
prefetch_related_objects(item_list, "incident__organization")
prefetch_related_objects(item_list, "user")
user_serializer = UserSerializer()
serialized_users = serialize(
{item.user for item in item_list if item.user_id},
user=user,
serializer=user_serializer,
serialized_users = user_service.serialize_many(
filter={"user_ids": [i.user_id for i in item_list if i.user_id]}, as_user=user
)
user_lookup = {user["id"]: user for user in serialized_users}
return {item: {"user": user_lookup.get(str(item.user_id))} for item in item_list}
Expand Down
Loading