Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
2 changes: 1 addition & 1 deletion .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ jobs:

- name: archive reports
if: success() || failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: reports
path: reports/**/*
Expand Down
1,856 changes: 976 additions & 880 deletions poetry.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ lint.select = [
src = ["."]
lint.ignore = [
"PT004",
"UP007"
"UP007",
"UP045"
]
exclude = [
".git",
Expand Down
2 changes: 2 additions & 0 deletions src/mesh_sandbox/common/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class Headers:
Mex_AddressType = "mex-AddressType"


MESSAGE_IN_INBOX_EXPIRY_IN_DAYS = 5

# Error codes
ERROR_INVALID_FROM_ADDRESS: Final = "Invalid From Address"
ERROR_MISSING_TO_ADDRESS: Final = "TO_DTS missing"
Expand Down
66 changes: 66 additions & 0 deletions src/mesh_sandbox/common/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from typing import Any, Optional, Union
from uuid import uuid4

from ..models.message import Message, MessageEvent, MessageMetadata, MessageParty, MessageStatus, MessageType
from . import constants


Expand Down Expand Up @@ -44,3 +46,67 @@ def try_parse_error(detail: Union[str, dict, None] = None, message_id: Optional[
if isinstance(detail, dict):
return detail
return {"errorDescription": str(detail)}


def get_ndr_error() -> dict:
expiry_period = 5
Comment thread
johnwilliams57-nhs marked this conversation as resolved.
Outdated
error_description = parse_error(
detail=constants.ERROR_UNDELIVERED_MESSAGE,
format_params=(expiry_period,),
)

return error_description


def create_ndr(request, recipient) -> Message:
Comment thread
johnwilliams57-nhs marked this conversation as resolved.
Outdated
error_description = get_ndr_error()
report = create_error_report(request, error_description, recipient)
return report


def create_error_report(request, error_description: dict, recipient) -> Message:
Comment thread
johnwilliams57-nhs marked this conversation as resolved.
Outdated

error_code = error_description.get("errorCode")
error_event = error_description.get("errorEvent")
error_message = error_description.get("errorDescription")

subject = "NDR" if not request.subject else f"NDR: {request.subject}"

metadata = MessageMetadata(
subject=subject,
local_id=request.local_id,
)

return Message(
events=[
MessageEvent(status=MessageStatus.ACCEPTED),
MessageEvent(
status=MessageStatus.ERROR,
code=error_code,
event=error_event,
description=error_message,
linked_message_id=request.linked_message_id,
),
],
message_id=uuid4().hex.upper(),
sender=MessageParty(
mailbox_id="",
mailbox_name="Central System Mailbox",
ods_code="X26",
org_code="X26",
org_name="NHS England",
billing_entity="England",
),
recipient=MessageParty(
mailbox_id=recipient.mailbox_id,
mailbox_name=recipient.mailbox_name,
ods_code=recipient.ods_code,
org_code=recipient.org_code,
org_name=recipient.org_name,
billing_entity=recipient.billing_entity,
),
total_chunks=0,
message_type=MessageType.REPORT,
workflow_id=request.workflow_id,
metadata=metadata,
)
77 changes: 41 additions & 36 deletions src/mesh_sandbox/handlers/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

from fastapi import BackgroundTasks, Depends, HTTPException, status

from mesh_sandbox.common.exceptions import create_ndr

from ..common.messaging import Messaging
from ..dependencies import get_messaging
from ..models.mailbox import Mailbox
Expand Down Expand Up @@ -50,43 +52,46 @@ async def create_report(self, request: CreateReportRequest, background_tasks: Ba

assert request.status in (MessageStatus.UNDELIVERABLE, MessageStatus.ERROR)

message = Message(
events=[
MessageEvent(status=MessageStatus.ACCEPTED),
MessageEvent(
status=request.status,
event="TRANSFER",
code=request.code,
description=request.description,
linked_message_id=request.linked_message_id,
if request.status == MessageStatus.UNDELIVERABLE:
message = create_ndr(request, recipient)
else:
message = Message(
events=[
MessageEvent(status=MessageStatus.ACCEPTED),
MessageEvent(
status=request.status,
event="TRANSFER",
code=request.code,
description=request.description,
linked_message_id=request.linked_message_id,
),
],
message_id=uuid4().hex.upper(),
sender=MessageParty(
mailbox_id="",
mailbox_name="Central System Mailbox",
ods_code="X26",
org_code="X26",
org_name="NHS England",
billing_entity="England",
),
],
message_id=uuid4().hex.upper(),
sender=MessageParty(
mailbox_id="",
mailbox_name="Central System Mailbox",
ods_code="X26",
org_code="X26",
org_name="NHS England",
billing_entity="England",
),
recipient=MessageParty(
mailbox_id=recipient.mailbox_id,
mailbox_name=recipient.mailbox_name,
ods_code=recipient.ods_code,
org_code=recipient.org_code,
org_name=recipient.org_name,
billing_entity=recipient.billing_entity,
),
total_chunks=0,
message_type=MessageType.REPORT,
workflow_id=request.workflow_id,
metadata=MessageMetadata(
subject=request.subject,
local_id=request.local_id,
file_name=request.file_name,
),
)
recipient=MessageParty(
mailbox_id=recipient.mailbox_id,
mailbox_name=recipient.mailbox_name,
ods_code=recipient.ods_code,
org_code=recipient.org_code,
org_name=recipient.org_name,
billing_entity=recipient.billing_entity,
),
total_chunks=0,
message_type=MessageType.REPORT,
workflow_id=request.workflow_id,
metadata=MessageMetadata(
subject=request.subject,
local_id=request.local_id,
file_name=request.file_name,
),
)

await self.messaging.send_message(message=message, body=b"", background_tasks=background_tasks)

Expand Down
36 changes: 36 additions & 0 deletions src/mesh_sandbox/tests/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,3 +447,39 @@ def test_get_message_report_type(app: TestClient, root_path: str):
def test_get_message_not_found(app: TestClient, root_path: str):
res = app.get(f"{root_path}/notfound")
assert res.status_code == status.HTTP_404_NOT_FOUND


@pytest.mark.parametrize("root_path", ["/messageexchange/admin/message"])
def test_generate_ndr(app: TestClient, root_path: str):

expected_response = {
"status": MessageStatus.UNDELIVERABLE,
"workflow_id": uuid4().hex,
"code": "21",
"description": "non delivery reason",
"subject": f"my subject {uuid4().hex}",
"local_id": f"my local id {uuid4().hex}",
"file_name": f"my filename {uuid4().hex}",
"linked_message_id": uuid4().hex,
}

request = CreateReportRequest(
mailbox_id=_CANNED_MAILBOX1,
code=expected_response["code"],
description=expected_response["description"],
workflow_id=expected_response["workflow_id"],
subject=expected_response["subject"],
local_id=expected_response["local_id"],
status=expected_response["status"],
file_name=expected_response["file_name"],
linked_message_id=expected_response["linked_message_id"],
)

res = app.post("/messageexchange/admin/report", json=request.model_dump())
assert res.status_code == status.HTTP_200_OK
msg_id = res.json()["message_id"]

res = app.get(f"{root_path}/{msg_id}")
non_delivery_report = res.json()
assert non_delivery_report["status"] == "Accepted"
assert non_delivery_report["status_description"] == "Message not collected by recipient after 5 days"
1 change: 1 addition & 0 deletions src/mesh_sandbox/tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ def ensure_client_installed(java_path: str, base_dir: str, version: str): # pyl

with httpx.Client() as client:
res = client.get(installer_uri)
res.raise_for_status()
with open(installer_rar, "wb+") as f:
f.write(res.read())

Expand Down
Loading