Skip to content

Commit 5080662

Browse files
Merge pull request #184 from NHSDigital/mesh-2433-support-ndrs-in-admin-enpoint
mesh-2433 changes
2 parents 9282e95 + c6978c4 commit 5080662

10 files changed

Lines changed: 1080 additions & 897 deletions

File tree

.github/workflows/pull-request.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ jobs:
127127

128128
- name: archive reports
129129
if: success() || failure()
130-
uses: actions/upload-artifact@v3
130+
uses: actions/upload-artifact@v4
131131
with:
132132
name: reports
133133
path: reports/**/*

poetry.lock

Lines changed: 976 additions & 880 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ lint.select = [
133133
src = ["."]
134134
lint.ignore = [
135135
"PT004",
136-
"UP007"
136+
"UP007",
137+
"UP045"
137138
]
138139
exclude = [
139140
".git",

src/mesh_sandbox/common/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ class Headers:
4545
Mex_AddressType = "mex-AddressType"
4646

4747

48+
MESSAGE_IN_INBOX_EXPIRY_IN_DAYS = 5
49+
4850
# Error codes
4951
ERROR_INVALID_FROM_ADDRESS: Final = "Invalid From Address"
5052
ERROR_MISSING_TO_ADDRESS: Final = "TO_DTS missing"

src/mesh_sandbox/common/exceptions.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
from typing import Any, Optional, Union
22

3+
from mesh_sandbox.views.admin import CreateReportRequest
4+
5+
from ..models.message import MessageEvent, MessageMetadata, MessageStatus
36
from . import constants
47

58

@@ -44,3 +47,38 @@ def try_parse_error(detail: Union[str, dict, None] = None, message_id: Optional[
4447
if isinstance(detail, dict):
4548
return detail
4649
return {"errorDescription": str(detail)}
50+
51+
52+
def get_ndr_error() -> dict:
53+
expiry_period = constants.MESSAGE_IN_INBOX_EXPIRY_IN_DAYS
54+
error_description = parse_error(
55+
detail=constants.ERROR_UNDELIVERED_MESSAGE,
56+
format_params=(expiry_period,),
57+
)
58+
59+
return error_description
60+
61+
62+
def create_ndr_event(request: CreateReportRequest) -> MessageEvent:
63+
error_description = get_ndr_error()
64+
65+
error_code = error_description.get("errorCode")
66+
error_event = error_description.get("errorEvent")
67+
error_message = error_description.get("errorDescription")
68+
69+
return MessageEvent(
70+
status=MessageStatus.ERROR,
71+
code=error_code,
72+
event=error_event,
73+
description=error_message,
74+
linked_message_id=request.linked_message_id,
75+
)
76+
77+
78+
def create_ndr_metadata(request: CreateReportRequest) -> MessageMetadata:
79+
subject = "NDR" if not request.subject else f"NDR: {request.subject}"
80+
81+
return MessageMetadata(
82+
subject=subject,
83+
local_id=request.local_id,
84+
)

src/mesh_sandbox/handlers/admin.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
from fastapi import BackgroundTasks, Depends, HTTPException, status
55

6+
from mesh_sandbox.common.exceptions import create_ndr_event, create_ndr_metadata
7+
68
from ..common.messaging import Messaging
79
from ..dependencies import get_messaging
810
from ..models.mailbox import Mailbox
@@ -50,16 +52,27 @@ async def create_report(self, request: CreateReportRequest, background_tasks: Ba
5052

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

55+
if request.status == MessageStatus.UNDELIVERABLE:
56+
message_event = create_ndr_event(request)
57+
message_metadata = create_ndr_metadata(request)
58+
else:
59+
message_event = MessageEvent(
60+
status=request.status,
61+
event="TRANSFER",
62+
code=request.code,
63+
description=request.description,
64+
linked_message_id=request.linked_message_id,
65+
)
66+
message_metadata = MessageMetadata(
67+
subject=request.subject,
68+
local_id=request.local_id,
69+
file_name=request.file_name,
70+
)
71+
5372
message = Message(
5473
events=[
5574
MessageEvent(status=MessageStatus.ACCEPTED),
56-
MessageEvent(
57-
status=request.status,
58-
event="TRANSFER",
59-
code=request.code,
60-
description=request.description,
61-
linked_message_id=request.linked_message_id,
62-
),
75+
message_event,
6376
],
6477
message_id=uuid4().hex.upper(),
6578
sender=MessageParty(
@@ -81,11 +94,7 @@ async def create_report(self, request: CreateReportRequest, background_tasks: Ba
8194
total_chunks=0,
8295
message_type=MessageType.REPORT,
8396
workflow_id=request.workflow_id,
84-
metadata=MessageMetadata(
85-
subject=request.subject,
86-
local_id=request.local_id,
87-
file_name=request.file_name,
88-
),
97+
metadata=message_metadata,
8998
)
9099

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

src/mesh_sandbox/store/serialisation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def _deserialise_value(field_type, value):
8585
return value
8686

8787
if is_dataclass(field_type):
88-
return deserialise_model(value, field_type)
88+
return deserialise_model(value, field_type) # type: ignore
8989

9090
if field_type in (int, float):
9191
return field_type(value)

src/mesh_sandbox/tests/admin.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,3 +447,39 @@ def test_get_message_report_type(app: TestClient, root_path: str):
447447
def test_get_message_not_found(app: TestClient, root_path: str):
448448
res = app.get(f"{root_path}/notfound")
449449
assert res.status_code == status.HTTP_404_NOT_FOUND
450+
451+
452+
@pytest.mark.parametrize("root_path", ["/messageexchange/admin/message"])
453+
def test_generate_ndr(app: TestClient, root_path: str):
454+
455+
expected_response = {
456+
"status": MessageStatus.UNDELIVERABLE,
457+
"workflow_id": uuid4().hex,
458+
"code": "21",
459+
"description": "non delivery reason",
460+
"subject": f"my subject {uuid4().hex}",
461+
"local_id": f"my local id {uuid4().hex}",
462+
"file_name": f"my filename {uuid4().hex}",
463+
"linked_message_id": uuid4().hex,
464+
}
465+
466+
request = CreateReportRequest(
467+
mailbox_id=_CANNED_MAILBOX1,
468+
code=expected_response["code"],
469+
description=expected_response["description"],
470+
workflow_id=expected_response["workflow_id"],
471+
subject=expected_response["subject"],
472+
local_id=expected_response["local_id"],
473+
status=expected_response["status"],
474+
file_name=expected_response["file_name"],
475+
linked_message_id=expected_response["linked_message_id"],
476+
)
477+
478+
res = app.post("/messageexchange/admin/report", json=request.model_dump())
479+
assert res.status_code == status.HTTP_200_OK
480+
msg_id = res.json()["message_id"]
481+
482+
res = app.get(f"{root_path}/{msg_id}")
483+
non_delivery_report = res.json()
484+
assert non_delivery_report["status"] == "Accepted"
485+
assert non_delivery_report["status_description"] == "Message not collected by recipient after 5 days"

src/mesh_sandbox/tests/helpers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def ensure_client_installed(java_path: str, base_dir: str, version: str): # pyl
7474

7575
with httpx.Client() as client:
7676
res = client.get(installer_uri)
77+
res.raise_for_status()
7778
with open(installer_rar, "wb+") as f:
7879
f.write(res.read())
7980

src/mesh_sandbox/views/outbox.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ class Config:
2626

2727

2828
class UploadChunkV1(BaseModel):
29-
messageID: str = Field(default=None, description="message identifier, as supplied in the request url")
30-
blockID: int = Field(default=None, description="chunk number, as supplied in the request url")
29+
messageID: Union[str, None] = Field(default=None, description="message identifier, as supplied in the request url")
30+
blockID: Union[int, None] = Field(default=None, description="chunk number, as supplied in the request url")
3131

3232
class Config:
3333
title = "upload_chunk"

0 commit comments

Comments
 (0)