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
1 change: 1 addition & 0 deletions src/sentry/api/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ class Endpoint(APIView):
publish_status: dict[HTTP_METHOD_NAME, ApiPublishStatus] = {}
rate_limits: RateLimitConfig = DEFAULT_RATE_LIMIT_CONFIG
enforce_rate_limit: bool = settings.SENTRY_RATELIMITER_ENABLED
servers: list[dict[str, Any]] | None = None

def build_cursor_link(self, request: HttpRequest, name: str, cursor: Cursor) -> str:
if request.GET.get("cursor") is None:
Expand Down
4 changes: 4 additions & 0 deletions src/sentry/api/endpoints/seer_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class SeerModelsEndpoint(Endpoint):
}
owner = ApiOwner.ML_AI
permission_classes = ()
servers = [{"url": "https://{region}.sentry.io"}]

enforce_rate_limit = True
rate_limits = RateLimitConfig(
Expand All @@ -72,6 +73,9 @@ def get(self, request: Request) -> Response:

Returns the list of AI models that are currently used in production in Seer.
This endpoint does not require authentication and can be used to discover which models Seer uses.

Requests to this endpoint should use the region-specific domain
eg. `us.sentry.io` or `de.sentry.io`
"""
cached_data = cache.get(SEER_MODELS_CACHE_KEY)
if cached_data is not None:
Expand Down
17 changes: 17 additions & 0 deletions src/sentry/apidocs/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,21 @@ class CustomGenerator(SchemaGenerator):
endpoint_inspector_cls = CustomEndpointEnumerator


# Collected during preprocessing, used in postprocessing
_ENDPOINT_SERVERS: dict[str, list[dict[str, Any]]] = {}


def custom_preprocessing_hook(endpoints: Any) -> Any: # TODO: organize method, rename
_ENDPOINT_SERVERS.clear()

filtered = []
ownership_data: dict[ApiOwner, dict] = {}
for path, path_regex, method, callback in endpoints:
# Collect servers from endpoint class for postprocessing
endpoint_servers = getattr(callback.view_class, "servers", None)
if endpoint_servers is not None:
_ENDPOINT_SERVERS[path] = endpoint_servers

owner_team = callback.view_class.owner
if owner_team not in ownership_data:
ownership_data[owner_team] = {
Expand Down Expand Up @@ -222,6 +233,12 @@ def _validate_request_body(


def custom_postprocessing_hook(result: Any, generator: Any, **kwargs: Any) -> Any:
# Add servers override from endpoint class definitions
for path, servers in _ENDPOINT_SERVERS.items():
if path in result["paths"]:
for method_info in result["paths"][path].values():
method_info["servers"] = servers

_fix_issue_paths(result)

# Fetch schema component references
Expand Down
45 changes: 44 additions & 1 deletion tests/apidocs/test_hooks.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,49 @@
from unittest import TestCase

from sentry.apidocs.hooks import custom_postprocessing_hook
from sentry.apidocs.hooks import _ENDPOINT_SERVERS, custom_postprocessing_hook


class EndpointServersTest(TestCase):
def setUp(self) -> None:
_ENDPOINT_SERVERS.clear()

def tearDown(self) -> None:
_ENDPOINT_SERVERS.clear()

def test_servers_applied_to_endpoint(self) -> None:
"""Test that servers from _ENDPOINT_SERVERS are applied to matching paths."""
_ENDPOINT_SERVERS["/api/0/seer/models/"] = [{"url": "https://{region}.sentry.io"}]

result = {
"components": {"schemas": {}},
"paths": {
"/api/0/seer/models/": {
"get": {
"tags": ["Seer"],
"description": "Get models",
"operationId": "get models",
"parameters": [],
}
},
"/api/0/other/endpoint/": {
"get": {
"tags": ["Events"],
"description": "Other endpoint",
"operationId": "get other",
"parameters": [],
}
},
},
}

processed = custom_postprocessing_hook(result, None)

# Servers should be applied to the matching endpoint
assert processed["paths"]["/api/0/seer/models/"]["get"]["servers"] == [
{"url": "https://{region}.sentry.io"}
]
# Servers should NOT be applied to non-matching endpoint
assert "servers" not in processed["paths"]["/api/0/other/endpoint/"]["get"]


class FixIssueRoutesTest(TestCase):
Expand Down
Loading