From d6111ea08845ed64526bd4dc03df432b34b5734b Mon Sep 17 00:00:00 2001 From: MJedr Date: Sun, 3 Dec 2023 22:57:12 +0100 Subject: [PATCH] update workflow models * add ticket type to workflow_status table * modify workflow statuses --- backoffice/workflows/api/serializers.py | 8 +- backoffice/workflows/api/views.py | 23 ++++- backoffice/workflows/constants.py | 23 +++++ ...icket_ticket_type_alter_workflow_status.py | 38 +++++++++ backoffice/workflows/models.py | 24 +++--- backoffice/workflows/tests/test_views.py | 85 ++++++++++++++----- config/api_router.py | 4 +- 7 files changed, 166 insertions(+), 39 deletions(-) create mode 100644 backoffice/workflows/constants.py create mode 100644 backoffice/workflows/migrations/0005_workflowticket_ticket_type_alter_workflow_status.py diff --git a/backoffice/workflows/api/serializers.py b/backoffice/workflows/api/serializers.py index eba2eb78..19770f4e 100644 --- a/backoffice/workflows/api/serializers.py +++ b/backoffice/workflows/api/serializers.py @@ -1,9 +1,15 @@ from rest_framework import serializers -from backoffice.workflows.models import Workflow +from backoffice.workflows.models import Workflow, WorkflowTicket class WorkflowSerializer(serializers.ModelSerializer): class Meta: model = Workflow fields = "__all__" + + +class WorkflowTicketSerializer(serializers.ModelSerializer): + class Meta: + model = WorkflowTicket + fields = "__all__" diff --git a/backoffice/workflows/api/views.py b/backoffice/workflows/api/views.py index a63925a3..518969d8 100644 --- a/backoffice/workflows/api/views.py +++ b/backoffice/workflows/api/views.py @@ -1,10 +1,11 @@ from django.shortcuts import get_object_or_404 from rest_framework import status, viewsets from rest_framework.response import Response +from rest_framework.views import APIView -from backoffice.workflows.models import Workflow +from backoffice.workflows.models import Workflow, WorkflowTicket -from .serializers import WorkflowSerializer +from .serializers import WorkflowSerializer, WorkflowTicketSerializer class WorkflowViewSet(viewsets.ModelViewSet): @@ -27,3 +28,21 @@ def partial_update(self, request, pk=None): serializer.save() return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +class WorkflowTicketViewSet(viewsets.ViewSet): + def retrieve(self, request, *args, **kwargs): + workflow_id = kwargs.get("pk") + ticket_type = request.query_params.get("ticket_type") + + if not workflow_id or not ticket_type: + return Response( + {"error": "Both workflow_id and ticket_type are required."}, status=status.HTTP_400_BAD_REQUEST + ) + + try: + workflow_ticket = WorkflowTicket.objects.get(workflow_id=workflow_id, ticket_type=ticket_type) + serializer = WorkflowTicketSerializer(workflow_ticket) + return Response(serializer.data) + except WorkflowTicket.DoesNotExist: + return Response({"error": "Workflow ticket not found."}, status=status.HTTP_404_NOT_FOUND) diff --git a/backoffice/workflows/constants.py b/backoffice/workflows/constants.py new file mode 100644 index 00000000..0a8de579 --- /dev/null +++ b/backoffice/workflows/constants.py @@ -0,0 +1,23 @@ +# tickets +TICKET_TYPES = ( + ("author_create_curation", "Author create curation"), + ("author_create_user", "Author create user"), +) +DEFAULT_TICKET_TYPE = "author_create_curation" + +# workflows +DEFAULT_STATUS_CHOICE = "running" +DEFAULT_WORKFLOW_TYPE = "HEP_create" +STATUS_CHOICES = ( + ("running", "Running"), + ("approval", "Waiting for approval"), + ("completed", "Completed"), + ("error", "Error"), +) + +WORKFLOW_TYPES = ( + ("HEP_CREATE", "HEP create"), + ("HEP_UPDATE", "HEP update"), + ("AUTHOR_CREATE", "Author create"), + ("AUTHOR_UPDATE", "Author update"), +) diff --git a/backoffice/workflows/migrations/0005_workflowticket_ticket_type_alter_workflow_status.py b/backoffice/workflows/migrations/0005_workflowticket_ticket_type_alter_workflow_status.py new file mode 100644 index 00000000..79dc03fc --- /dev/null +++ b/backoffice/workflows/migrations/0005_workflowticket_ticket_type_alter_workflow_status.py @@ -0,0 +1,38 @@ +# Generated by Django 4.2.6 on 2023-12-07 14:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("workflows", "0004_workflow_workflow_type"), + ] + + operations = [ + migrations.AddField( + model_name="workflowticket", + name="ticket_type", + field=models.CharField( + choices=[ + ("author_create_curation", "Author create curation"), + ("author_create_user", "Author create user"), + ], + default="author_create_curation", + max_length=30, + ), + ), + migrations.AlterField( + model_name="workflow", + name="status", + field=models.CharField( + choices=[ + ("running", "Running"), + ("approval", "Waiting for approval"), + ("completed", "Completed"), + ("error", "Error"), + ], + default="running", + max_length=30, + ), + ), + ] diff --git a/backoffice/workflows/models.py b/backoffice/workflows/models.py index 9e2bd110..18a76ded 100644 --- a/backoffice/workflows/models.py +++ b/backoffice/workflows/models.py @@ -2,22 +2,17 @@ from django.db import models +from .constants import ( + DEFAULT_STATUS_CHOICE, + DEFAULT_TICKET_TYPE, + DEFAULT_WORKFLOW_TYPE, + STATUS_CHOICES, + TICKET_TYPES, + WORKFLOW_TYPES, +) -class Workflow(models.Model): - DEFAULT_STATUS_CHOICE = "PREPROCESSING" - DEFAULT_WORKFLOW_TYPE = "HEP_create" - STATUS_CHOICES = ( - ("PREPROCESSING", "Preprocessing"), - ("APPROVAL", "Approval"), - ("POSTPROCESSING", "Postprocessing"), - ) - WORKFLOW_TYPES = ( - ("HEP_CREATE", "HEP create"), - ("HEP_UPDATE", "HEP update"), - ("AUTHOR_CREATE", "Author create"), - ("AUTHOR_UPDATE", "Author update"), - ) +class Workflow(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) workflow_type = models.CharField( @@ -38,3 +33,4 @@ class Workflow(models.Model): class WorkflowTicket(models.Model): workflow_id = models.ForeignKey(Workflow, on_delete=models.CASCADE) ticket_id = models.CharField(max_length=32) # in SNOW it's GUID + ticket_type = models.CharField(max_length=30, choices=TICKET_TYPES, default=DEFAULT_TICKET_TYPE) diff --git a/backoffice/workflows/tests/test_views.py b/backoffice/workflows/tests/test_views.py index 3fd3455e..5ccc3ab3 100644 --- a/backoffice/workflows/tests/test_views.py +++ b/backoffice/workflows/tests/test_views.py @@ -1,15 +1,18 @@ +import pytest from django.apps import apps from django.contrib.auth import get_user_model from django.contrib.auth.models import Group from django.test import TransactionTestCase from rest_framework.test import APIClient +from backoffice.workflows.api.serializers import WorkflowTicketSerializer +from backoffice.workflows.models import WorkflowTicket + User = get_user_model() Workflow = apps.get_model(app_label="workflows", model_name="Workflow") -class TestWorkflowViewSet(TransactionTestCase): - endpoint = "/api/workflows/" +class BaseTransactionTestCase(TransactionTestCase): reset_sequences = True fixtures = ["backoffice/fixtures/groups.json"] @@ -25,7 +28,16 @@ def setUp(self): self.admin.groups.add(self.admin_group) self.api_client = APIClient() - self.workflow = Workflow.objects.create(data={}, status="APPROVAL", core=True, is_update=False) + + +class TestWorkflowViewSet(BaseTransactionTestCase): + endpoint = "/api/workflows/" + reset_sequences = True + fixtures = ["backoffice/fixtures/groups.json"] + + def setUp(self): + super().setUp() + self.workflow = Workflow.objects.create(data={}, status="approval", core=True, is_update=False) def test_list_curator(self): self.api_client.force_authenticate(user=self.curator) @@ -48,24 +60,14 @@ def test_list_anonymous(self): self.assertEqual(response.status_code, 403) -class WorkflowPartialUpdateViewSet(TransactionTestCase): +class TestWorkflowPartialUpdateViewSet(BaseTransactionTestCase): endpoint_base_url = "/api/workflow-update" reset_sequences = True fixtures = ["backoffice/fixtures/groups.json"] def setUp(self): - self.curator_group = Group.objects.get(name="curator") - self.admin_group = Group.objects.get(name="admin") - - self.curator = User.objects.create_user(email="curator@test.com", password="12345") - self.admin = User.objects.create_user(email="admin@test.com", password="12345") - self.user = User.objects.create_user(email="testuser@test.com", password="12345") - - self.curator.groups.add(self.curator_group) - self.admin.groups.add(self.admin_group) - - self.api_client = APIClient() - self.workflow = Workflow.objects.create(data={}, status="APPROVAL", core=True, is_update=False) + super().setUp() + self.workflow = Workflow.objects.create(data={}, status="approval", core=True, is_update=False) @property def endpoint(self): @@ -73,21 +75,21 @@ def endpoint(self): def test_patch_curator(self): self.api_client.force_authenticate(user=self.curator) - response = self.api_client.patch(self.endpoint, format="json", data={"status": "POSTPROCESSING"}) + response = self.api_client.patch(self.endpoint, format="json", data={"status": "running"}) self.assertEqual(response.status_code, 200) workflow = Workflow.objects.filter(id=str(self.workflow.id))[0] - assert workflow.status == "POSTPROCESSING" + assert workflow.status == "running" def test_patch_admin(self): self.api_client.force_authenticate(user=self.admin) response = self.api_client.patch( - self.endpoint, format="json", data={"status": "PREPROCESSING", "data": {"test": "test"}} + self.endpoint, format="json", data={"status": "approval", "data": {"test": "test"}} ) workflow = Workflow.objects.filter(id=str(self.workflow.id))[0] self.assertEqual(response.status_code, 200) - self.assertEquals(workflow.status, "PREPROCESSING") + self.assertEquals(workflow.status, "approval") self.assertEquals(workflow.data, {"test": "test"}) def test_patch_anonymous(self): @@ -95,3 +97,46 @@ def test_patch_anonymous(self): response = self.api_client.get(self.endpoint, format="json") self.assertEqual(response.status_code, 403) + + +class TestWorkflowTicketViewSet(BaseTransactionTestCase): + endpoint = "/api/workflow-ticket" + reset_sequences = True + fixtures = ["backoffice/fixtures/groups.json"] + + def setUp(self): + super().setUp() + self.workflow = Workflow.objects.create(data={}, status="running", core=True, is_update=False) + self.workflow_ticket = WorkflowTicket.objects.create( + workflow_id=self.workflow, ticket_id="123", ticket_type="author_create_user" + ) + + def test_get_missing_params(self): + self.api_client.force_authenticate(user=self.curator) + response = self.api_client.get( + f"{TestWorkflowTicketViewSet.endpoint}/{self.workflow.id}/", format="json", data={} + ) + + assert response.status_code == 400 + assert response.data == {"error": "Both workflow_id and ticket_type are required."} + + def test_get_ticket_not_found(self): + query_params = {"ticket_type": "test"} + self.api_client.force_authenticate(user=self.curator) + response = self.api_client.get( + f"{TestWorkflowTicketViewSet.endpoint}/{self.workflow.id}/", format="json", data=query_params + ) + + assert response.status_code == 404 + assert response.data == {"error": "Workflow ticket not found."} + + def test_get_ticket_happy_flow(self): + self.api_client.force_authenticate(user=self.curator) + + query_params = {"ticket_type": self.workflow_ticket.ticket_type} + response = self.api_client.get( + f"{TestWorkflowTicketViewSet.endpoint}/{self.workflow.id}/", format="json", data=query_params + ) + + assert response.status_code == 200 + assert response.data == WorkflowTicketSerializer(self.workflow_ticket).data diff --git a/config/api_router.py b/config/api_router.py index 9787f820..6bbe0047 100644 --- a/config/api_router.py +++ b/config/api_router.py @@ -2,7 +2,7 @@ from rest_framework.routers import DefaultRouter, SimpleRouter from backoffice.users.api.views import UserViewSet -from backoffice.workflows.api.views import WorkflowPartialUpdateViewSet, WorkflowViewSet +from backoffice.workflows.api.views import WorkflowPartialUpdateViewSet, WorkflowTicketViewSet, WorkflowViewSet if settings.DEBUG: router = DefaultRouter() @@ -14,6 +14,6 @@ # Workflows router.register("workflows", WorkflowViewSet, basename="workflows") router.register("workflow-update", WorkflowPartialUpdateViewSet, basename="workflow-update") - +router.register("workflow-ticket", WorkflowTicketViewSet, basename="workflow-ticket"), app_name = "api" urlpatterns = router.urls