Skip to content
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

feat(workflow_engine): Endpoint for GET Organization Workflows #83894

Merged
merged 8 commits into from
Jan 23, 2025
3 changes: 2 additions & 1 deletion src/sentry/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2172,6 +2172,7 @@ def create_group_urls(name_prefix: str) -> list[URLPattern | URLResolver]:
OrganizationUptimeAlertIndexEndpoint.as_view(),
name="sentry-api-0-organization-uptime-alert-index",
),
*workflow_urls.organization_urlpatterns,
]

PROJECT_URLS: list[URLPattern | URLResolver] = [
Expand Down Expand Up @@ -2763,7 +2764,7 @@ def create_group_urls(name_prefix: str) -> list[URLPattern | URLResolver]:
TempestCredentialsDetailsEndpoint.as_view(),
name="sentry-api-0-project-tempest-credentials-details",
),
*workflow_urls.urlpatterns,
*workflow_urls.project_urlpatterns,
]

TEAM_URLS = [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from drf_spectacular.utils import extend_schema

from sentry.api.api_owners import ApiOwner
from sentry.api.api_publish_status import ApiPublishStatus
from sentry.api.base import region_silo_endpoint
from sentry.api.bases import OrganizationEndpoint
from sentry.api.serializers import serialize
from sentry.apidocs.constants import (
RESPONSE_BAD_REQUEST,
RESPONSE_FORBIDDEN,
RESPONSE_NOT_FOUND,
RESPONSE_UNAUTHORIZED,
)
from sentry.apidocs.parameters import GlobalParams
from sentry.workflow_engine.endpoints.serializers import WorkflowSerializer
from sentry.workflow_engine.models import Workflow


@region_silo_endpoint
class OrganizationWorkflowIndexEndpoint(OrganizationEndpoint):
publish_status = {
"POST": ApiPublishStatus.EXPERIMENTAL,
"GET": ApiPublishStatus.EXPERIMENTAL,
}
owner = ApiOwner.ISSUES

@extend_schema(
operation_id="Fetch Workflows",
parameters=[
GlobalParams.ORG_ID_OR_SLUG,
],
responses={
201: WorkflowSerializer,
400: RESPONSE_BAD_REQUEST,
401: RESPONSE_UNAUTHORIZED,
403: RESPONSE_FORBIDDEN,
404: RESPONSE_NOT_FOUND,
},
)
def get(self, request, organization):
"""
Returns a list of workflows for a given org
"""
# TODO add additonal filters and ordering
queryset = Workflow.objects.filter(organization_id=organization.id).order_by("id")

return self.paginate(
request=request,
queryset=queryset,
order_by="id",
on_results=lambda x: serialize(x, request.user),
)

def post(self, request, organization):
"""
Creates a workflow for an organization
"""
pass
4 changes: 2 additions & 2 deletions src/sentry/workflow_engine/endpoints/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ def serialize(self, obj: Detector, attrs: Mapping[str, Any], user, **kwargs) ->

@register(Workflow)
class WorkflowSerializer(Serializer):
def get_attrs(self, item_list, user, **kwargs):
def get_attrs(self, item_list, user, **kwargs) -> MutableMapping[Workflow, dict[str, Any]]:
attrs: MutableMapping[Workflow, dict[str, Any]] = defaultdict(dict)
trigger_conditions = list(
DataConditionGroup.objects.filter(
Expand Down Expand Up @@ -233,7 +233,6 @@ def get_attrs(self, item_list, user, **kwargs):
return attrs

def serialize(self, obj: Workflow, attrs: Mapping[str, Any], user, **kwargs) -> dict[str, Any]:
# WHAT TO DO ABOUT CONFIG?
return {
"id": str(obj.id),
"organizationId": str(obj.organization_id),
Expand All @@ -242,4 +241,5 @@ def serialize(self, obj: Workflow, attrs: Mapping[str, Any], user, **kwargs) ->
"triggerConditionGroup": attrs.get("trigger_condition_group"),
"dataConditionGroups": attrs.get("data_condition_groups"),
"environment": obj.environment.name if obj.environment else None,
"config": obj.config,
}
26 changes: 25 additions & 1 deletion src/sentry/workflow_engine/endpoints/urls.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,35 @@
from django.urls import re_path

from .organization_workflow_index import OrganizationWorkflowIndexEndpoint
from .project_detector_index import ProjectDetectorIndexEndpoint

urlpatterns = [
# TODO @saponifi3d - Add the remaining API endpoints

# Remaining Detector Endpoints
# - GET /detector w/ filters
# - GET /detector/:id
# - PUT /detector/:id
# - DELETE /detector/:id

# Remaining Workflows Endpoints
# - GET /workflow w/ filters
# - POST /workflow
# - GET /workflow/:id
# - PUT /workflow/:id
# - DELETE /workflow/:id

project_urlpatterns = [
re_path(
r"^(?P<organization_id_or_slug>[^\/]+)/(?P<project_id_or_slug>[^\/]+)/detectors/$",
ProjectDetectorIndexEndpoint.as_view(),
name="sentry-api-0-project-detector-index",
),
]

organization_urlpatterns = [
re_path(
r"^(?P<organization_id_or_slug>[^\/]+)/workflows/$",
OrganizationWorkflowIndexEndpoint.as_view(),
name="sentry-api-0-organization-workflow-index",
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from sentry.api.serializers import serialize
from sentry.testutils.cases import APITestCase
from sentry.testutils.silo import region_silo_test


class OrganizationWorkflowAPITestCase(APITestCase):
endpoint = "sentry-api-0-organization-workflow-index"

def setUp(self):
super().setUp()
self.login_as(user=self.user)


@region_silo_test
class OrganizationWorkflowIndexBaseTest(OrganizationWorkflowAPITestCase):
def test_simple(self):
workflow = self.create_workflow(organization_id=self.organization.id, name="Test Workflow")

workflow_two = self.create_workflow(
organization_id=self.organization.id, name="Test Workflow 2"
)

response = self.get_success_response(self.organization.slug)
assert response.data == serialize([workflow, workflow_two])

def test_empty_result(self):
response = self.get_success_response(self.organization.slug)
assert response.data == []
8 changes: 5 additions & 3 deletions tests/sentry/workflow_engine/endpoints/test_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ def test_serialize_simple(self):
assert result == {
"id": str(workflow.id),
"organizationId": str(self.organization.id),
"config": {},
"dateCreated": workflow.date_added,
"dateUpdated": workflow.date_updated,
"triggerConditionGroup": None,
Expand Down Expand Up @@ -339,12 +340,13 @@ def test_serialize_full(self):
assert result == {
"id": str(workflow.id),
"organizationId": str(self.organization.id),
"config": {},
"dateCreated": workflow.date_added,
"dateUpdated": workflow.date_updated,
"triggerConditionGroup": {
"id": str(when_condition_group.id),
"organizationId": str(self.organization.id),
"logicType": DataConditionGroup.Type.ANY,
"logicType": DataConditionGroup.Type.ANY.value,
"conditions": [
{
"id": str(trigger_condition.id),
Expand All @@ -359,13 +361,13 @@ def test_serialize_full(self):
{
"id": str(condition_group.id),
"organizationId": str(self.organization.id),
"logicType": DataConditionGroup.Type.ALL,
"logicType": DataConditionGroup.Type.ALL.value,
"conditions": [
{
"id": str(condition.id),
"condition": "gt",
"comparison": 100,
"result": DetectorPriorityLevel.HIGH,
"result": DetectorPriorityLevel.HIGH.value,
}
],
"actions": [
Expand Down
Loading