-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
feat(seer-infra-telemetry): Implement monitoring provider connection API endpoints #117854
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
118f34f
bbed6c0
e01cffa
aed2996
f08d22a
8243e7e
d852e40
2d63fb1
3d3c079
7b5072f
4c57e55
80f7b07
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import logging | ||
|
|
||
| from django.http import HttpResponseRedirect | ||
| from rest_framework.request import Request | ||
| from rest_framework.response import Response | ||
|
|
||
| from sentry import features | ||
| from sentry.api.api_owners import ApiOwner | ||
| from sentry.api.api_publish_status import ApiPublishStatus | ||
| from sentry.api.base import control_silo_endpoint | ||
| from sentry.api.bases.organization import ControlSiloOrganizationEndpoint | ||
| from sentry.api.endpoints.organization_monitoring_provider_index import ( | ||
| MONITORING_PROVIDERS, | ||
| MonitoringProviderPermission, | ||
| ) | ||
| from sentry.identity import default_manager as identity_manager | ||
| from sentry.identity.pipeline import IdentityPipeline | ||
| from sentry.organizations.services.organization.model import RpcOrganization | ||
| from sentry.users.models.identity import Identity, IdentityProvider | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| @control_silo_endpoint | ||
| class OrganizationMonitoringProviderDetailsEndpoint(ControlSiloOrganizationEndpoint): | ||
| owner = ApiOwner.CODING_WORKFLOWS | ||
| publish_status = { | ||
| "POST": ApiPublishStatus.PRIVATE, | ||
| "DELETE": ApiPublishStatus.PRIVATE, | ||
| } | ||
| permission_classes = (MonitoringProviderPermission,) | ||
|
|
||
| def post( | ||
| self, request: Request, organization: RpcOrganization, provider_key: str, **kwargs: object | ||
| ) -> Response: | ||
| if not features.has("organizations:seer-infra-telemetry", organization, actor=request.user): | ||
| return Response(status=404) | ||
|
|
||
| if provider_key not in MONITORING_PROVIDERS: | ||
| return Response({"detail": "Unknown monitoring provider."}, status=400) | ||
|
|
||
| provider_type = identity_manager.get(provider_key) | ||
| try: | ||
| config = provider_type.get_pipeline_config(request.data) | ||
| except ValueError as e: | ||
| return Response({"detail": str(e)}, status=400) | ||
|
|
||
| idp: IdentityProvider | None = None | ||
| if not provider_type.auto_create_provider_model: | ||
| idp, _ = IdentityProvider.objects.get_or_create(type=provider_key, external_id="") | ||
|
|
||
| pipeline = IdentityPipeline( | ||
| request=request._request, | ||
|
shashjar marked this conversation as resolved.
|
||
| provider_key=provider_key, | ||
| organization=organization, | ||
| provider_model=idp, | ||
| config=config, | ||
| ) | ||
| pipeline.initialize() | ||
|
|
||
| response = pipeline.current_step() | ||
|
Check notice on line 63 in src/sentry/api/endpoints/organization_monitoring_provider_details.py
|
||
|
|
||
| if isinstance(response, HttpResponseRedirect): | ||
| return Response({"redirectUrl": response.url}) | ||
|
|
||
| logger.error( | ||
| "monitoring_provider.connect.unexpected_response", | ||
| extra={"provider": provider_key, "response_type": type(response).__name__}, | ||
| ) | ||
| return Response({"detail": "Failed to start OAuth flow."}, status=500) | ||
|
|
||
| def delete( | ||
| self, request: Request, organization: RpcOrganization, provider_key: str, **kwargs: object | ||
| ) -> Response: | ||
| if not features.has("organizations:seer-infra-telemetry", organization, actor=request.user): | ||
| return Response(status=404) | ||
|
|
||
| if provider_key not in MONITORING_PROVIDERS: | ||
| return Response({"detail": "Unknown monitoring provider."}, status=400) | ||
|
|
||
| identities = list( | ||
| Identity.objects.filter( | ||
| idp__type=provider_key, | ||
| user_id=request.user.id, # type: ignore[misc] | ||
| ) | ||
| ) | ||
|
|
||
| if not identities: | ||
| return Response({"detail": "Not connected to this provider."}, status=404) | ||
|
|
||
| for identity in identities: | ||
| identity.delete() | ||
|
|
||
| return Response(status=204) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from rest_framework.request import Request | ||
| from rest_framework.response import Response | ||
|
|
||
| from sentry import features | ||
| from sentry.api.api_owners import ApiOwner | ||
| from sentry.api.api_publish_status import ApiPublishStatus | ||
| from sentry.api.base import control_silo_endpoint | ||
| from sentry.api.bases.organization import ( | ||
| ControlSiloOrganizationEndpoint, | ||
| OrganizationPermission, | ||
| ) | ||
| from sentry.organizations.services.organization.model import RpcOrganization | ||
| from sentry.users.models.identity import Identity | ||
|
|
||
| MONITORING_PROVIDERS: dict[str, dict[str, str]] = { | ||
| "datadog": {"name": "Datadog"}, | ||
| "gcp": {"name": "Google Cloud Platform"}, | ||
| } | ||
|
|
||
|
|
||
| class MonitoringProviderPermission(OrganizationPermission): | ||
| scope_map = { | ||
| "GET": ["org:read", "org:write", "org:admin"], | ||
| "POST": ["org:read", "org:write", "org:admin"], | ||
| "DELETE": ["org:read", "org:write", "org:admin"], | ||
| } | ||
|
|
||
|
Check notice on line 29 in src/sentry/api/endpoints/organization_monitoring_provider_index.py
|
||
|
|
||
| @control_silo_endpoint | ||
| class OrganizationMonitoringProviderIndexEndpoint(ControlSiloOrganizationEndpoint): | ||
| owner = ApiOwner.CODING_WORKFLOWS | ||
| publish_status = { | ||
| "GET": ApiPublishStatus.PRIVATE, | ||
| } | ||
| permission_classes = (MonitoringProviderPermission,) | ||
|
|
||
| def get(self, request: Request, organization: RpcOrganization, **kwargs: object) -> Response: | ||
| if not features.has("organizations:seer-infra-telemetry", organization, actor=request.user): | ||
| return Response(status=404) | ||
|
|
||
| connected_identities = { | ||
| identity.idp.type: identity | ||
| for identity in Identity.objects.filter( | ||
| idp__type__in=MONITORING_PROVIDERS.keys(), | ||
| user_id=request.user.id, # type: ignore[misc] | ||
| ).select_related("idp") | ||
| } | ||
|
|
||
| providers = [] | ||
| for key, meta in MONITORING_PROVIDERS.items(): | ||
| identity = connected_identities.get(key) | ||
| providers.append( | ||
| { | ||
| "provider": key, | ||
| "name": meta["name"], | ||
| "connected": identity is not None, | ||
| } | ||
| ) | ||
|
|
||
| return Response({"providers": providers}) | ||
Uh oh!
There was an error while loading. Please reload this page.