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
8 changes: 6 additions & 2 deletions src/sentry/api/endpoints/organization_releases.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
return queryset.filter(date__gte=filter_params["start"], date__lte=filter_params["end"])
return queryset


Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on a
user-provided value
may run slow on strings with many repetitions of ' '.
This
regular expression
that depends on a
user-provided value
may run slow on strings starting with ' (' and with many repetitions of ' (('.
This
regular expression
that depends on a
user-provided value
may run slow on strings with many repetitions of ' '.
This
regular expression
that depends on a
user-provided value
may run slow on strings starting with ' (' and with many repetitions of ' (('.
def _filter_releases_by_query(queryset, organization, query, filter_params):
search_filters = parse_search_query(query)
for search_filter in search_filters:
Expand Down Expand Up @@ -362,7 +362,9 @@
},
examples=ReleaseExamples.LIST_ORGANIZATION_RELEASES,
)
def get(self, request: Request, organization: Organization) -> Response:
def get(
self, request: Request, organization: Organization
) -> Response[list[ReleaseSerializerResponse]]:
"""
Return a list of releases for a given organization, sorted by most recent.
"""
Expand Down Expand Up @@ -934,7 +936,9 @@
},
examples=ReleaseExamples.LIST_RELEASE_TIMESERIES,
)
def get(self, request: Request, organization: Organization) -> Response:
def get(
self, request: Request, organization: Organization
) -> Response[list[OrganizationReleaseTimeseriesData]]:
"""
Return a minimal list of an organization's releases (version and date only),
sorted by most recent. Intended for building release timeseries, such as
Expand Down
11 changes: 8 additions & 3 deletions src/sentry/discover/endpoints/discover_saved_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
GlobalParams,
VisibilityParams,
)
from sentry.apidocs.response_types import ValidationErrorResponse, as_validation_errors
from sentry.apidocs.utils import inline_sentry_response_serializer
from sentry.discover.endpoints.bases import (
DiscoverSavedQueryPermission,
Expand Down Expand Up @@ -170,7 +171,9 @@ def data_fn(offset, limit):
},
examples=DiscoverExamples.DISCOVER_SAVED_QUERY_POST_RESPONSE,
)
def post(self, request: Request, organization) -> Response:
def post(
self, request: Request, organization
) -> Response[DiscoverSavedQueryResponse] | Response[ValidationErrorResponse]:
"""
Create a new saved query for the given organization.
"""
Expand All @@ -190,7 +193,7 @@ def post(self, request: Request, organization) -> Response:
)

if not serializer.is_valid():
return Response(serializer.errors, status=400)
return Response(as_validation_errors(serializer), status=400)

data = serializer.validated_data
user_selected_dataset = data["query_dataset"] != DiscoverSavedQueryTypes.DISCOVER
Expand All @@ -211,4 +214,6 @@ def post(self, request: Request, organization) -> Response:

model.set_projects(data["project_ids"])

return Response(serialize(model), status=201)
return Response(
serialize(model, serializer=DiscoverSavedQueryModelSerializer()), status=201
)
24 changes: 18 additions & 6 deletions src/sentry/discover/endpoints/discover_saved_query_detail.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
from sentry.api.bases import NoProjects, OrganizationEndpoint
from sentry.api.exceptions import ResourceDoesNotExist
from sentry.api.serializers import serialize
from sentry.api.serializers.models.discoversavedquery import DiscoverSavedQueryModelSerializer
from sentry.api.serializers.models.discoversavedquery import (
DiscoverSavedQueryModelSerializer,
DiscoverSavedQueryResponse,
)
from sentry.apidocs.constants import (
RESPONSE_BAD_REQUEST,
RESPONSE_FORBIDDEN,
Expand All @@ -21,6 +24,7 @@
)
from sentry.apidocs.examples.discover_saved_query_examples import DiscoverExamples
from sentry.apidocs.parameters import DiscoverSavedQueryParams, GlobalParams
from sentry.apidocs.response_types import ValidationErrorResponse, as_validation_errors
from sentry.discover.endpoints.bases import DiscoverSavedQueryPermission
from sentry.discover.endpoints.serializers import DiscoverSavedQuerySerializer
from sentry.discover.models import DatasetSourcesTypes, DiscoverSavedQuery, DiscoverSavedQueryTypes
Expand Down Expand Up @@ -72,7 +76,9 @@ def has_feature(self, organization, request):
},
examples=DiscoverExamples.DISCOVER_SAVED_QUERY_GET_RESPONSE,
)
def get(self, request: Request, organization, query) -> Response:
def get(
self, request: Request, organization, query: DiscoverSavedQuery
) -> Response[DiscoverSavedQueryResponse]:
"""
Retrieve a saved query.
"""
Expand All @@ -81,7 +87,9 @@ def get(self, request: Request, organization, query) -> Response:

self.check_object_permissions(request, query)

return Response(serialize(query), status=200)
return Response(
serialize(query, serializer=DiscoverSavedQueryModelSerializer()), status=200
)

@extend_schema(
operation_id="Edit an Organization's Discover Saved Query",
Expand All @@ -95,7 +103,9 @@ def get(self, request: Request, organization, query) -> Response:
},
examples=DiscoverExamples.DISCOVER_SAVED_QUERY_GET_RESPONSE,
)
def put(self, request: Request, organization: Organization, query) -> Response:
def put(
self, request: Request, organization: Organization, query: DiscoverSavedQuery
) -> Response[DiscoverSavedQueryResponse] | Response[ValidationErrorResponse]:
"""
Modify a saved query.
"""
Expand All @@ -116,7 +126,7 @@ def put(self, request: Request, organization: Organization, query) -> Response:
context={"params": params, "organization": organization, "user": request.user},
)
if not serializer.is_valid():
return Response(serializer.errors, status=400)
return Response(as_validation_errors(serializer), status=400)

data = serializer.validated_data
user_selected_dataset = data["query_dataset"] != DiscoverSavedQueryTypes.DISCOVER
Expand All @@ -136,7 +146,9 @@ def put(self, request: Request, organization: Organization, query) -> Response:

query.set_projects(data["project_ids"])

return Response(serialize(query), status=200)
return Response(
serialize(query, serializer=DiscoverSavedQueryModelSerializer()), status=200
)

@extend_schema(
operation_id="Delete an Organization's Discover Saved Query",
Expand Down
19 changes: 14 additions & 5 deletions src/sentry/integrations/api/endpoints/external_team_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@
from sentry.apidocs.constants import RESPONSE_BAD_REQUEST, RESPONSE_FORBIDDEN, RESPONSE_NO_CONTENT
from sentry.apidocs.examples.integration_examples import IntegrationExamples
from sentry.apidocs.parameters import GlobalParams, OrganizationParams
from sentry.apidocs.response_types import ValidationErrorResponse, as_validation_errors
from sentry.integrations.api.bases.external_actor import (
ExternalActorEndpointMixin,
ExternalTeamSerializer,
)
from sentry.integrations.api.serializers.models.external_actor import ExternalActorSerializer
from sentry.integrations.api.serializers.models.external_actor import (
ExternalActorResponse,
ExternalActorSerializer,
)
from sentry.integrations.models.external_actor import ExternalActor
from sentry.models.team import Team

Expand Down Expand Up @@ -66,7 +70,9 @@ def convert_args(
},
examples=IntegrationExamples.EXTERNAL_TEAM_CREATE,
)
def put(self, request: Request, team: Team, external_team: ExternalActor) -> Response:
def put(
self, request: Request, team: Team, external_team: ExternalActor
) -> Response[ExternalActorResponse] | Response[ValidationErrorResponse]:
"""
Update a team in an external provider that is currently linked to a Sentry team.
"""
Expand All @@ -82,13 +88,16 @@ def put(self, request: Request, team: Team, external_team: ExternalActor) -> Res
context={"organization": team.organization},
)
if serializer.is_valid():
updated_external_team = serializer.save()
updated_external_team: ExternalActor = serializer.save()

return Response(
serialize(updated_external_team, request.user), status=status.HTTP_200_OK
serialize(
updated_external_team, request.user, serializer=ExternalActorSerializer()
),
status=status.HTTP_200_OK,
)

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
return Response(as_validation_errors(serializer), status=status.HTTP_400_BAD_REQUEST)

@extend_schema(
operation_id="Delete an External Team",
Expand Down
22 changes: 18 additions & 4 deletions src/sentry/integrations/api/endpoints/external_team_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,16 @@
from sentry.apidocs.constants import RESPONSE_BAD_REQUEST, RESPONSE_FORBIDDEN
from sentry.apidocs.examples.integration_examples import IntegrationExamples
from sentry.apidocs.parameters import GlobalParams
from sentry.apidocs.response_types import ValidationErrorResponse, as_validation_errors
from sentry.integrations.api.bases.external_actor import (
ExternalActorEndpointMixin,
ExternalTeamSerializer,
)
from sentry.integrations.api.serializers.models.external_actor import ExternalActorSerializer
from sentry.integrations.api.serializers.models.external_actor import (
ExternalActorResponse,
ExternalActorSerializer,
)
from sentry.integrations.models.external_actor import ExternalActor
from sentry.models.team import Team

logger = logging.getLogger(__name__)
Expand All @@ -43,7 +48,9 @@ class ExternalTeamEndpoint(TeamEndpoint, ExternalActorEndpointMixin):
},
examples=IntegrationExamples.EXTERNAL_TEAM_CREATE,
)
def post(self, request: Request, team: Team) -> Response:
def post(
self, request: Request, team: Team
) -> Response[ExternalActorResponse] | Response[ValidationErrorResponse]:
"""
Link a team from an external provider to a Sentry team.
"""
Expand All @@ -56,8 +63,15 @@ def post(self, request: Request, team: Team) -> Response:
data={**request.data, "team_id": team.id}, context={"organization": team.organization}
)
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
return Response(as_validation_errors(serializer), status=status.HTTP_400_BAD_REQUEST)

external_team: ExternalActor
created: bool
external_team, created = serializer.save()
status_code = status.HTTP_201_CREATED if created else status.HTTP_200_OK
return Response(serialize(external_team, request.user, key="team"), status=status_code)
return Response(
serialize(
external_team, request.user, key="team", serializer=ExternalActorSerializer()
),
status=status_code,
)
17 changes: 12 additions & 5 deletions src/sentry/integrations/api/endpoints/external_user_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@
from sentry.apidocs.constants import RESPONSE_BAD_REQUEST, RESPONSE_FORBIDDEN, RESPONSE_NO_CONTENT
from sentry.apidocs.examples.integration_examples import IntegrationExamples
from sentry.apidocs.parameters import GlobalParams, OrganizationParams
from sentry.apidocs.response_types import ValidationErrorResponse, as_validation_errors
from sentry.integrations.api.bases.external_actor import (
ExternalActorEndpointMixin,
ExternalUserPermission,
ExternalUserSerializer,
)
from sentry.integrations.api.serializers.models.external_actor import ExternalActorSerializer
from sentry.integrations.api.serializers.models.external_actor import (
ExternalActorResponse,
ExternalActorSerializer,
)
from sentry.integrations.models.external_actor import ExternalActor
from sentry.models.organization import Organization

Expand Down Expand Up @@ -67,7 +71,7 @@ def convert_args(
)
def put(
self, request: Request, organization: Organization, external_user: ExternalActor
) -> Response:
) -> Response[ExternalActorResponse] | Response[ValidationErrorResponse]:
"""
Update a user in an external provider that is currently linked to a Sentry user.
"""
Expand All @@ -80,13 +84,16 @@ def put(
partial=True,
)
if serializer.is_valid():
updated_external_user = serializer.save()
updated_external_user: ExternalActor = serializer.save()

return Response(
serialize(updated_external_user, request.user), status=status.HTTP_200_OK
serialize(
updated_external_user, request.user, serializer=ExternalActorSerializer()
),
status=status.HTTP_200_OK,
)

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
return Response(as_validation_errors(serializer), status=status.HTTP_400_BAD_REQUEST)

@extend_schema(
operation_id="Delete an External User",
Expand Down
22 changes: 18 additions & 4 deletions src/sentry/integrations/api/endpoints/external_user_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,17 @@
from sentry.apidocs.constants import RESPONSE_BAD_REQUEST, RESPONSE_FORBIDDEN
from sentry.apidocs.examples.integration_examples import IntegrationExamples
from sentry.apidocs.parameters import GlobalParams
from sentry.apidocs.response_types import ValidationErrorResponse, as_validation_errors
from sentry.integrations.api.bases.external_actor import (
ExternalActorEndpointMixin,
ExternalUserPermission,
ExternalUserSerializer,
)
from sentry.integrations.api.serializers.models.external_actor import ExternalActorSerializer
from sentry.integrations.api.serializers.models.external_actor import (
ExternalActorResponse,
ExternalActorSerializer,
)
from sentry.integrations.models.external_actor import ExternalActor
from sentry.models.organization import Organization

logger = logging.getLogger(__name__)
Expand All @@ -45,7 +50,9 @@ class ExternalUserEndpoint(OrganizationEndpoint, ExternalActorEndpointMixin):
},
examples=IntegrationExamples.EXTERNAL_USER_CREATE,
)
def post(self, request: Request, organization: Organization) -> Response:
def post(
self, request: Request, organization: Organization
) -> Response[ExternalActorResponse] | Response[ValidationErrorResponse]:
"""
Link a user from an external provider to a Sentry user.
"""
Expand All @@ -55,8 +62,15 @@ def post(self, request: Request, organization: Organization) -> Response:
data=request.data, context={"organization": organization}
)
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
return Response(as_validation_errors(serializer), status=status.HTTP_400_BAD_REQUEST)

external_user: ExternalActor
created: bool
external_user, created = serializer.save()
status_code = status.HTTP_201_CREATED if created else status.HTTP_200_OK
return Response(serialize(external_user, request.user, key="user"), status=status_code)
return Response(
serialize(
external_user, request.user, key="user", serializer=ExternalActorSerializer()
),
status=status_code,
)
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ExternalActorResponse(ExternalActorResponseOptional):


@register(ExternalActor)
class ExternalActorSerializer(Serializer):
class ExternalActorSerializer(Serializer[ExternalActorResponse]):
def get_attrs(
self,
item_list: Sequence[ExternalActor],
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/issues/endpoints/organization_group_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ def get(self, request: Request, organization: Organization) -> Response:
examples=IssueExamples.ORGANIZATION_GROUP_INDEX_PUT,
)
@track_slo_response("workflow")
def put(self, request: Request, organization: Organization) -> Response:
def put(self, request: Request, organization: Organization) -> Response[MutateIssueResponse]:
projects = self.get_projects(request, organization)

search_fn = functools.partial(
Expand Down
18 changes: 13 additions & 5 deletions src/sentry/issues/endpoints/project_ownership.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@
from sentry.api.base import cell_silo_endpoint
from sentry.api.bases.project import ProjectEndpoint, ProjectOwnershipPermission
from sentry.api.serializers import serialize
from sentry.api.serializers.models.projectownership import ProjectOwnershipSerializer
from sentry.api.serializers.models.projectownership import (
ProjectOwnershipResponse,
ProjectOwnershipSerializer,
)
from sentry.apidocs.constants import RESPONSE_BAD_REQUEST
from sentry.apidocs.examples import ownership_examples
from sentry.apidocs.parameters import GlobalParams
from sentry.apidocs.response_types import ValidationErrorResponse, as_validation_errors
from sentry.issues.ownership.grammar import CODEOWNERS, create_schema_from_issue_owners
from sentry.models.organizationmemberteam import OrganizationMemberTeam
from sentry.models.project import Project
Expand Down Expand Up @@ -302,7 +306,9 @@ def get(self, request: Request, project) -> Response:
},
examples=ownership_examples.UPDATE_PROJECT_OWNERSHIP,
)
def put(self, request: Request, project) -> Response:
def put(
self, request: Request, project: Project
) -> Response[ProjectOwnershipResponse] | Response[ValidationErrorResponse]:
"""
Updates ownership configurations for a project. Note that only the
attributes submitted are modified.
Expand All @@ -323,7 +329,7 @@ def put(self, request: Request, project) -> Response:
context={"ownership": self.get_ownership(project), "request": request},
)
if serializer.is_valid():
ownership = serializer.save()
ownership: ProjectOwnership = serializer.save()

change_data = {**serializer.validated_data}
# Ownership rules can be large (3 MB) and we don't want to store them in the audit log
Expand All @@ -341,5 +347,7 @@ def put(self, request: Request, project) -> Response:
data={**change_data, **project.get_audit_log_data()},
)
ownership_rule_created.send_robust(project=project, sender=self.__class__)
return Response(serialize(ownership, request.user))
return Response(serializer.errors, status=400)
return Response(
serialize(ownership, request.user, serializer=ProjectOwnershipSerializer())
)
return Response(as_validation_errors(serializer), status=400)
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,9 @@ def get(
404: RESPONSE_NOT_FOUND,
},
)
def post(self, request: AuthenticatedHttpRequest, organization) -> Response:
def post(
self, request: AuthenticatedHttpRequest, organization
) -> Response[MonitorSerializerResponse]:
"""
Create a new monitor.
"""
Expand Down
Loading
Loading