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
Empty file.
19 changes: 19 additions & 0 deletions backend/src/baserow/contrib/automation/api/history/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from rest_framework.status import HTTP_404_NOT_FOUND

ERROR_AUTOMATION_WORKFLOW_HISTORY_DOES_NOT_EXIST = (
"ERROR_AUTOMATION_WORKFLOW_HISTORY_DOES_NOT_EXIST",
HTTP_404_NOT_FOUND,
"The automation workflow history does not exist.",
)

ERROR_AUTOMATION_NODE_HISTORY_DOES_NOT_EXIST = (
"ERROR_AUTOMATION_NODE_HISTORY_DOES_NOT_EXIST",
HTTP_404_NOT_FOUND,
"The automation node history does not exist.",
)

ERROR_AUTOMATION_NODE_RESULT_DOES_NOT_EXIST = (
"ERROR_AUTOMATION_NODE_RESULT_DOES_NOT_EXIST",
HTTP_404_NOT_FOUND,
"The automation node result does not exist.",
)
78 changes: 78 additions & 0 deletions backend/src/baserow/contrib/automation/api/history/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers

from baserow.contrib.automation.history.models import (
AutomationNodeHistory,
AutomationNodeResult,
)


class AutomationNodeHistorySerializer(serializers.ModelSerializer):
node_type = serializers.SerializerMethodField()
node_label = serializers.SerializerMethodField()
parent_node_id = serializers.SerializerMethodField()
iteration = serializers.SerializerMethodField()
iteration_path = serializers.SerializerMethodField()
edge_label = serializers.SerializerMethodField()

class Meta:
model = AutomationNodeHistory
fields = (
"id",
"started_on",
"completed_on",
"message",
"status",
"workflow_history",
"node",
"node_type",
"node_label",
"parent_node_id",
"iteration",
"iteration_path",
"edge_label",
)

@extend_schema_field(OpenApiTypes.STR)
def get_node_type(self, obj):
return obj.node.get_type().type

@extend_schema_field(OpenApiTypes.STR)
def get_node_label(self, obj):
return obj.node.label

@extend_schema_field(OpenApiTypes.INT)
def get_parent_node_id(self, obj):
parent_nodes = obj.node.get_parent_nodes()
if not parent_nodes:
return None
return parent_nodes[-1].id

def _get_first_node_result(self, obj):
results = obj.node_results.all()
return results[0] if results else None

@extend_schema_field(OpenApiTypes.INT)
def get_iteration(self, obj):
result = self._get_first_node_result(obj)
if result is None:
return None
if result.iteration_path:
return int(result.iteration_path.rsplit(".", 1)[-1])
return 0

@extend_schema_field(OpenApiTypes.STR)
def get_iteration_path(self, obj):
result = self._get_first_node_result(obj)
return result.iteration_path if result else ""

@extend_schema_field(OpenApiTypes.STR)
def get_edge_label(self, obj):
return self.context.get("edge_labels", {}).get(obj.id, "")


class AutomationNodeResultSerializer(serializers.ModelSerializer):
class Meta:
model = AutomationNodeResult
fields = ("result",)
21 changes: 21 additions & 0 deletions backend/src/baserow/contrib/automation/api/history/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from django.urls import re_path

from baserow.contrib.automation.api.history.views import (
AutomationNodeHistoriesView,
AutomationNodeResultView,
)

app_name = "baserow.contrib.automation.api.history"

urlpatterns = [
re_path(
r"workflow_histories/(?P<workflow_history_id>[0-9]+)/node_histories/$",
AutomationNodeHistoriesView.as_view(),
name="node_histories",
),
re_path(
r"node_histories/(?P<node_history_id>[0-9]+)/result/$",
AutomationNodeResultView.as_view(),
name="node_result",
),
]
109 changes: 109 additions & 0 deletions backend/src/baserow/contrib/automation/api/history/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter, extend_schema
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from baserow.api.decorators import map_exceptions
from baserow.api.schemas import CLIENT_SESSION_ID_SCHEMA_PARAMETER, get_error_schema
from baserow.contrib.automation.api.history.errors import (
ERROR_AUTOMATION_NODE_HISTORY_DOES_NOT_EXIST,
ERROR_AUTOMATION_NODE_RESULT_DOES_NOT_EXIST,
ERROR_AUTOMATION_WORKFLOW_HISTORY_DOES_NOT_EXIST,
)
from baserow.contrib.automation.api.history.serializers import (
AutomationNodeHistorySerializer,
AutomationNodeResultSerializer,
)
from baserow.contrib.automation.history.exceptions import (
AutomationNodeHistoryDoesNotExist,
AutomationWorkflowHistoryDoesNotExist,
AutomationWorkflowHistoryNodeResultDoesNotExist,
)
from baserow.contrib.automation.history.service import AutomationHistoryService

AUTOMATION_HISTORY_TAG = "Automation history"


class AutomationNodeHistoriesView(APIView):
permission_classes = (IsAuthenticated,)

@extend_schema(
parameters=[
OpenApiParameter(
name="workflow_history_id",
location=OpenApiParameter.PATH,
type=OpenApiTypes.INT,
description="The id of the workflow history.",
),
CLIENT_SESSION_ID_SCHEMA_PARAMETER,
],
tags=[AUTOMATION_HISTORY_TAG],
operation_id="get_automation_node_histories",
description="Returns all node histories for the given workflow history.",
responses={
200: AutomationNodeHistorySerializer(many=True),
404: get_error_schema(["ERROR_AUTOMATION_WORKFLOW_HISTORY_DOES_NOT_EXIST"]),
},
)
@map_exceptions(
{
AutomationWorkflowHistoryDoesNotExist: (
ERROR_AUTOMATION_WORKFLOW_HISTORY_DOES_NOT_EXIST
),
}
)
def get(self, request, workflow_history_id: int):
service = AutomationHistoryService()
node_histories = service.get_node_histories(request.user, workflow_history_id)
edge_labels = service.get_edge_labels(request.user, node_histories)
serializer = AutomationNodeHistorySerializer(
node_histories,
many=True,
context={"edge_labels": edge_labels},
)
return Response(serializer.data)


class AutomationNodeResultView(APIView):
permission_classes = (IsAuthenticated,)

@extend_schema(
parameters=[
OpenApiParameter(
name="node_history_id",
location=OpenApiParameter.PATH,
type=OpenApiTypes.INT,
description="The id of the node history.",
),
CLIENT_SESSION_ID_SCHEMA_PARAMETER,
],
tags=[AUTOMATION_HISTORY_TAG],
operation_id="get_automation_node_result",
description="Returns the node history's result JSON.",
responses={
200: AutomationNodeResultSerializer,
404: get_error_schema(
[
"ERROR_AUTOMATION_NODE_HISTORY_DOES_NOT_EXIST",
"ERROR_AUTOMATION_NODE_RESULT_DOES_NOT_EXIST",
]
),
},
)
@map_exceptions(
{
AutomationNodeHistoryDoesNotExist: (
ERROR_AUTOMATION_NODE_HISTORY_DOES_NOT_EXIST
),
AutomationWorkflowHistoryNodeResultDoesNotExist: (
ERROR_AUTOMATION_NODE_RESULT_DOES_NOT_EXIST
),
}
)
def get(self, request, node_history_id: int):
node_result = AutomationHistoryService().get_node_history_result(
request.user, node_history_id
)
serializer = AutomationNodeResultSerializer(node_result)
return Response(serializer.data)
8 changes: 8 additions & 0 deletions backend/src/baserow/contrib/automation/api/urls.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.urls import include, path, re_path

from baserow.contrib.automation.api.history import urls as history_urls
from baserow.contrib.automation.api.nodes import urls as node_urls
from baserow.contrib.automation.api.workflows import urls as workflow_urls

Expand Down Expand Up @@ -30,6 +31,13 @@
namespace="nodes",
),
),
path(
"",
include(
history_urls,
namespace="history",
),
),
]

urlpatterns = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from baserow.api.pagination import PageNumberPagination
from baserow.contrib.automation.models import (
AutomationHistory,
AutomationNodeHistory,
AutomationWorkflow,
AutomationWorkflowHistory,
)
Expand Down Expand Up @@ -118,70 +117,12 @@ class Meta:
)


class AutomationNodeHistorySerializer(AutomationHistorySerializer):
parent_node_id = serializers.SerializerMethodField()
iteration = serializers.SerializerMethodField()
result = serializers.SerializerMethodField()
node_type = serializers.SerializerMethodField()
node_label = serializers.SerializerMethodField()

class Meta:
model = AutomationNodeHistory
fields = AutomationHistorySerializer.Meta.fields + (
"workflow_history",
"node",
"node_type",
"node_label",
"parent_node_id",
"iteration",
"result",
)

def _get_first_node_result(self, obj):
results = obj.node_results.all()
return results[0] if results else None

@extend_schema_field(OpenApiTypes.STR)
def get_node_type(self, obj):
return obj.node.get_type().type

@extend_schema_field(OpenApiTypes.STR)
def get_node_label(self, obj):
return obj.node.label

@extend_schema_field(OpenApiTypes.INT)
def get_parent_node_id(self, obj):
parent_nodes = obj.node.get_parent_nodes()
if not parent_nodes:
return None
return parent_nodes[-1].id

@extend_schema_field(OpenApiTypes.INT)
def get_iteration(self, obj):
result = self._get_first_node_result(obj)
if result is None:
return None

if result.iteration_path:
return int(result.iteration_path.rsplit(".", 1)[-1])

return 0

def get_result(self, obj):
result = self._get_first_node_result(obj)
return result.result if result else {}


class AutomationWorkflowHistorySerializer(AutomationHistorySerializer):
node_histories = AutomationNodeHistorySerializer(read_only=True, many=True)

class Meta:
model = AutomationWorkflowHistory
fields = AutomationHistorySerializer.Meta.fields + (
"is_test_run",
"event_payload",
"simulate_until_node",
"node_histories",
)


Expand Down
12 changes: 12 additions & 0 deletions backend/src/baserow/contrib/automation/history/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ def __init__(self, history_id=None, *args, **kwargs):

class AutomationWorkflowHistoryNodeResultDoesNotExist(AutomationWorkflowHistoryError):
"""When the result entry doesn't exist for the given node/history."""


class AutomationNodeHistoryDoesNotExist(AutomationWorkflowHistoryError):
"""When the node history entry doesn't exist."""

def __init__(self, node_history_id=None, *args, **kwargs):
self.node_history_id = node_history_id
super().__init__(
f"The automation node history {node_history_id} does not exist.",
*args,
**kwargs,
)
Loading
Loading