Skip to content

Commit

Permalink
Merge pull request #1828 from uktrade/uat
Browse files Browse the repository at this point in the history
Production release
  • Loading branch information
kevincarrogan authored Feb 15, 2024
2 parents 2cadb0b + 68e9236 commit 5bd8614
Show file tree
Hide file tree
Showing 38 changed files with 1,072 additions and 71 deletions.
6 changes: 6 additions & 0 deletions api/appeals/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from rest_framework import filters


class AppealFilter(filters.BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
return queryset.filter(appeal_id=view.kwargs["pk"])
89 changes: 89 additions & 0 deletions api/appeals/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from unittest import mock

from moto import mock_aws

from django.http import FileResponse
from django.urls import reverse
from django.utils.timezone import now

Expand Down Expand Up @@ -180,3 +183,89 @@ def test_get_document_different_organisation(self):
response.status_code,
status.HTTP_403_FORBIDDEN,
)


@mock_aws
class TestAppealDocumentStream(DataTestClient):
def setUp(self):
super().setUp()
self.appeal = AppealFactory()
application = self.create_standard_application_case(
organisation=self.exporter_user.organisation,
)
application.appeal = self.appeal
application.save()

self.create_default_bucket()
self.put_object_in_default_bucket("thisisakey", b"test")

def test_get_document_stream(self):
appeal_document = AppealDocumentFactory(
appeal=self.appeal,
s3_key="thisisakey",
safe=True,
)

url = reverse(
"appeals:document_stream",
kwargs={
"pk": str(self.appeal.pk),
"document_pk": str(appeal_document.pk),
},
)
response = self.client.get(url, **self.exporter_headers)

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIsInstance(response, FileResponse)
self.assertEqual(b"".join(response.streaming_content), b"test")

def test_get_document_stream_invalid_appeal_pk(self):
appeal_document = AppealDocumentFactory(appeal=self.appeal)

url = reverse(
"appeals:document_stream",
kwargs={
"pk": "0f415f8a-e3e8-4c49-b053-ef03b1c477d5",
"document_pk": str(appeal_document.pk),
},
)
response = self.client.get(url, **self.exporter_headers)

self.assertEqual(
response.status_code,
status.HTTP_404_NOT_FOUND,
)

def test_get_document_stream_invalid_document_pk(self):
url = reverse(
"appeals:document_stream",
kwargs={
"pk": str(self.appeal.pk),
"document_pk": "0b551122-1ac2-4ea2-82b3-f1aaf0bf4923",
},
)
response = self.client.get(url, **self.exporter_headers)

self.assertEqual(
response.status_code,
status.HTTP_404_NOT_FOUND,
)

def test_get_document_stream_different_organisation(self):
self.appeal.baseapplication.organisation = self.create_organisation_with_exporter_user()[0]
self.appeal.baseapplication.save()
appeal_document = AppealDocumentFactory(appeal=self.appeal)

url = reverse(
"appeals:document_stream",
kwargs={
"pk": str(self.appeal.pk),
"document_pk": str(appeal_document.pk),
},
)
response = self.client.get(url, **self.exporter_headers)

self.assertEqual(
response.status_code,
status.HTTP_403_FORBIDDEN,
)
5 changes: 5 additions & 0 deletions api/appeals/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,9 @@
views.AppealDocumentAPIView.as_view(),
name="document",
),
path(
"<uuid:pk>/documents/<uuid:document_pk>/stream/",
views.AppealDocumentStreamAPIView.as_view(),
name="document_stream",
),
]
16 changes: 12 additions & 4 deletions api/appeals/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

from api.core.authentication import ExporterAuthentication
from api.core.permissions import IsExporterInOrganisation
from api.core.views import DocumentStreamAPIView

from .filters import AppealFilter
from .models import (
Appeal,
AppealDocument,
Expand Down Expand Up @@ -36,9 +38,15 @@ def get_serializer_context(self):


class AppealDocumentAPIView(BaseAppealDocumentAPIView, RetrieveAPIView):
filter_backends = (AppealFilter,)
lookup_url_kwarg = "document_pk"
queryset = AppealDocument.objects.all()

def get_queryset(self):
return AppealDocument.objects.filter(
appeal_id=self.kwargs["pk"],
)

class AppealDocumentStreamAPIView(BaseAppealDocumentAPIView, DocumentStreamAPIView):
filter_backends = (AppealFilter,)
lookup_url_kwarg = "document_pk"
queryset = AppealDocument.objects.all()

def get_document(self, instance):
return instance
25 changes: 24 additions & 1 deletion api/cases/helpers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from datetime import timedelta

from api.audit_trail.enums import AuditType
from api.common.dates import is_bank_holiday, is_weekend
from api.cases.enums import CaseTypeReferenceEnum
from api.staticdata.statuses.enums import CaseStatusEnum
from api.users.models import GovUser, GovNotification
from api.users.models import BaseUser, GovUser, GovNotification
from api.users.enums import SystemUser


def get_assigned_to_user_case_ids(user: GovUser, queue_id=None):
Expand Down Expand Up @@ -81,3 +83,24 @@ def can_set_status(case, status):
def working_days_in_range(start_date, end_date):
dates_in_range = [start_date + timedelta(n) for n in range((end_date - start_date).days)]
return len([date for date in dates_in_range if (not is_bank_holiday(date) and not is_weekend(date))])


def create_system_mention(case, case_note_text, mention_user):
"""
Create a LITE system mention e.g. exporter responded to an ECJU query
"""
# to avoid circular import ImportError these must be imported here
from api.cases.models import CaseNote, CaseNoteMentions
from api.audit_trail import service as audit_trail_service

case_note = CaseNote(text=case_note_text, case=case, user=BaseUser.objects.get(id=SystemUser.id))
case_note.save()
case_note_mentions = CaseNoteMentions(user=mention_user, case_note=case_note)
case_note_mentions.save()
audit_payload = {
"mention_users": [f"{mention_user.full_name} ({mention_user.team.name})"],
"additional_text": case_note_text,
}
audit_trail_service.create_system_user_audit(
verb=AuditType.CREATED_CASE_NOTE_WITH_MENTIONS, action_object=case_note, target=case, payload=audit_payload
)
1 change: 0 additions & 1 deletion api/cases/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from queryable_properties.managers import QueryablePropertiesManager
from queryable_properties.properties import queryable_property


from api.audit_trail.enums import AuditType
from api.cases.enums import (
AdviceType,
Expand Down
65 changes: 57 additions & 8 deletions api/cases/tests/test_case_ecju_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from api.audit_trail.serializers import AuditSerializer
from api.cases.enums import ECJUQueryType
from api.cases.models import EcjuQuery
from api.core.exceptions import NotFoundError
from api.compliance.tests.factories import ComplianceSiteCaseFactory
from api.licences.enums import LicenceStatus
from api.licences.tests.factories import StandardLicenceFactory
Expand All @@ -23,6 +24,7 @@
from api.staticdata.statuses.libraries.get_case_status import get_case_status_by_status
from test_helpers.clients import DataTestClient
from api.users.tests.factories import ExporterUserFactory
from api.cases.models import CaseNoteMentions

faker = Faker()

Expand Down Expand Up @@ -465,7 +467,7 @@ def _test_exporter_responds_to_query(self, add_documents, query_type):
query_response_url = reverse("cases:case_ecju_query", kwargs={"pk": case.id, "ecju_pk": ecju_query.id})
data = {"response": "Attached the requested documents"}
response = self.client.put(query_response_url, data, **self.exporter_headers)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = response.json()["ecju_query"]
self.assertEqual(response["response"], data["response"])

Expand Down Expand Up @@ -498,7 +500,7 @@ def test_caseworker_manually_closes_query(self):
self.assertEqual(1, BaseNotification.objects.filter(object_id=ecju_query.id).count())

response = self.client.put(query_response_url, data, **self.gov_headers)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = response.json()["ecju_query"]
self.assertEqual(response["response"], data["response"])

Expand All @@ -520,7 +522,7 @@ def test_close_query_has_optional_response_exporter(self):

data = {"response": ""}
response = self.client.put(query_response_url, data, **self.exporter_headers)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.status_code, status.HTTP_200_OK)

response_ecju_query = response.json()["ecju_query"]
self.assertIsNone(response_ecju_query["response"])
Expand Down Expand Up @@ -550,7 +552,7 @@ def test_caseworker_manually_closes_query_exporter_responds_raises_error(self):
data = {"response": "exporter provided details"}

response = self.client.put(query_response_url, data, **self.gov_headers)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = response.json()["ecju_query"]
self.assertEqual(response["response"], data["response"])

Expand All @@ -567,7 +569,7 @@ def test_caseworker_manually_closes_query_already_closed_raises_error(self):
data = {"response": "exporter provided details"}

response = self.client.put(query_response_url, data, **self.exporter_headers)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = response.json()["ecju_query"]
self.assertEqual(response["response"], data["response"])

Expand All @@ -594,7 +596,7 @@ def test_exporter_cannot_respond_to_same_ecju_query_twice(self):
url = reverse("cases:case_ecju_query", kwargs={"pk": case.id, "ecju_pk": ecju_query.id})
data = {"response": "Additional details included"}
response = self.client.put(url, data, **self.exporter_headers)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = response.json()["ecju_query"]
self.assertEqual(response["response"], data["response"])

Expand Down Expand Up @@ -630,7 +632,7 @@ def test_exporter_cannot_add_documents_to_closed_query(self):
query_response_url = reverse("cases:case_ecju_query", kwargs={"pk": case.id, "ecju_pk": ecju_query.id})
data = {"response": "Attached the requested documents"}
response = self.client.put(query_response_url, data, **self.exporter_headers)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = response.json()["ecju_query"]
self.assertEqual(response["response"], data["response"])
self.assertEqual(len(response["documents"]), 1)
Expand Down Expand Up @@ -659,7 +661,7 @@ def test_exporter_cannot_delete_documents_of_closed_query(self):
url = reverse("cases:case_ecju_query", kwargs={"pk": case.id, "ecju_pk": ecju_query.id})
data = {"response": "Additional details included"}
response = self.client.put(url, data, **self.exporter_headers)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = response.json()["ecju_query"]
self.assertEqual(response["response"], data["response"])
self.assertEqual(len(response["documents"]), 1)
Expand All @@ -675,3 +677,50 @@ def test_exporter_cannot_delete_documents_of_closed_query(self):
self.assertEqual(response.status_code, status.HTTP_200_OK)
response = response.json()
self.assertIsNotNone(response["document"]["id"])

@parameterized.expand(["this is some response text", ""])
def test_exporter_responding_to_query_creates_case_note_mention_for_caseworker(self, response_text):
case = self.create_standard_application_case(self.organisation)

# caseworker raises a query
url = reverse("cases:case_ecju_queries", kwargs={"pk": case.id})
question_text = "this is the question text"
data = {"question": question_text, "query_type": ECJUQueryType.ECJU}

response = self.client.post(url, data, **self.gov_headers)
response_data = response.json()
ecju_query = EcjuQuery.objects.get(case=case)

self.assertFalse(ecju_query.is_query_closed)
self.assertEqual(status.HTTP_201_CREATED, response.status_code)
self.assertEqual(response_data["ecju_query_id"], str(ecju_query.id))
self.assertEqual(question_text, ecju_query.question)
self.assertIsNone(ecju_query.response)

# exporter responds to the query
url = reverse("cases:case_ecju_query", kwargs={"pk": case.id, "ecju_pk": ecju_query.id})
data = {"response": response_text}

response = self.client.put(url, data, **self.exporter_headers)
ecju_query = EcjuQuery.objects.get(case=case)

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(ecju_query.is_query_closed)

# check case note mention is created
case_note_mentions = CaseNoteMentions.objects.first()
case_note = case_note_mentions.case_note
audit_object = Audit.objects.first()

expected_gov_user = ecju_query.raised_by_user
expected_exporter_user = ecju_query.responded_by_user
expected_mention_users_text = f"{expected_gov_user.full_name} ({expected_gov_user.team.name})"
expected_case_note_text = f"{expected_exporter_user.get_full_name()} has responded to a query."
expected_audit_payload = {
"mention_users": [expected_mention_users_text],
"additional_text": expected_case_note_text,
}

self.assertEqual(case_note_mentions.user, expected_gov_user)
self.assertEqual(case_note.text, expected_case_note_text)
self.assertEqual(audit_object.payload, expected_audit_payload)
Loading

0 comments on commit 5bd8614

Please sign in to comment.