mesh-sandbox 1.0.26__py3-none-any.whl → 1.0.28__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mesh_sandbox/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "1.0.26"
1
+ __version__ = "1.0.28"
@@ -45,6 +45,8 @@ class Headers:
45
45
  Mex_AddressType = "mex-AddressType"
46
46
 
47
47
 
48
+ MESSAGE_IN_INBOX_EXPIRY_IN_DAYS = 5
49
+
48
50
  # Error codes
49
51
  ERROR_INVALID_FROM_ADDRESS: Final = "Invalid From Address"
50
52
  ERROR_MISSING_TO_ADDRESS: Final = "TO_DTS missing"
@@ -1,5 +1,8 @@
1
1
  from typing import Any, Optional, Union
2
2
 
3
+ from mesh_sandbox.views.admin import CreateReportRequest
4
+
5
+ from ..models.message import MessageEvent, MessageMetadata, MessageStatus
3
6
  from . import constants
4
7
 
5
8
 
@@ -44,3 +47,38 @@ def try_parse_error(detail: Union[str, dict, None] = None, message_id: Optional[
44
47
  if isinstance(detail, dict):
45
48
  return detail
46
49
  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
+ )
@@ -3,6 +3,8 @@ from uuid import uuid4
3
3
 
4
4
  from fastapi import BackgroundTasks, Depends, HTTPException, status
5
5
 
6
+ from mesh_sandbox.common.exceptions import create_ndr_event, create_ndr_metadata
7
+
6
8
  from ..common.messaging import Messaging
7
9
  from ..dependencies import get_messaging
8
10
  from ..models.mailbox import Mailbox
@@ -50,16 +52,27 @@ class AdminHandler:
50
52
 
51
53
  assert request.status in (MessageStatus.UNDELIVERABLE, MessageStatus.ERROR)
52
54
 
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
+
53
72
  message = Message(
54
73
  events=[
55
74
  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,
63
76
  ],
64
77
  message_id=uuid4().hex.upper(),
65
78
  sender=MessageParty(
@@ -81,11 +94,7 @@ class AdminHandler:
81
94
  total_chunks=0,
82
95
  message_type=MessageType.REPORT,
83
96
  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,
89
98
  )
90
99
 
91
100
  await self.messaging.send_message(message=message, body=b"", background_tasks=background_tasks)
@@ -85,7 +85,7 @@ def _deserialise_value(field_type, value):
85
85
  return value
86
86
 
87
87
  if is_dataclass(field_type):
88
- return deserialise_model(value, field_type)
88
+ return deserialise_model(value, field_type) # type: ignore
89
89
 
90
90
  if field_type in (int, float):
91
91
  return field_type(value)
@@ -447,3 +447,39 @@ def test_get_message_report_type(app: TestClient, root_path: str):
447
447
  def test_get_message_not_found(app: TestClient, root_path: str):
448
448
  res = app.get(f"{root_path}/notfound")
449
449
  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"
@@ -74,6 +74,7 @@ def ensure_client_installed(java_path: str, base_dir: str, version: str): # pyl
74
74
 
75
75
  with httpx.Client() as client:
76
76
  res = client.get(installer_uri)
77
+ res.raise_for_status()
77
78
  with open(installer_rar, "wb+") as f:
78
79
  f.write(res.read())
79
80
 
@@ -26,8 +26,8 @@ class SendMessageV2(BaseModel):
26
26
 
27
27
 
28
28
  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")
31
31
 
32
32
  class Config:
33
33
  title = "upload_chunk"
@@ -1,19 +1,17 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mesh-sandbox
3
- Version: 1.0.26
3
+ Version: 1.0.28
4
4
  Summary: NHSDigital mesh sandbox, a locally testable version of the MESH api
5
5
  License: MIT
6
6
  Author: spinecore
7
- Requires-Python: >=3.9,<4.0
7
+ Requires-Python: >=3.11,<3.14.0
8
8
  Classifier: License :: OSI Approved :: MIT License
9
9
  Classifier: Programming Language :: Python :: 3
10
- Classifier: Programming Language :: Python :: 3.9
11
- Classifier: Programming Language :: Python :: 3.10
12
10
  Classifier: Programming Language :: Python :: 3.11
13
- Requires-Dist: cryptography (>=42.0.0,<43.0.0)
14
- Requires-Dist: fastapi (>=0.104.1,<0.112.0)
15
- Requires-Dist: gunicorn (>=21.2,<23.0)
16
- Requires-Dist: mesh-client (>=3.2.3,<4.0.0)
11
+ Requires-Dist: cryptography (>=42,<44)
12
+ Requires-Dist: fastapi (>=0.104.1,<0.116.0)
13
+ Requires-Dist: gunicorn (>=21.2,<24.0)
14
+ Requires-Dist: mesh-client (>=4.0.1,<5.0.0)
17
15
  Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
18
16
  Requires-Dist: types-python-dateutil (>=2.8.9,<3.0.0)
19
17
  Requires-Dist: uvicorn (>=0.23.2,<0.31.0)
@@ -43,8 +41,6 @@ docker compose
43
41
  --------------
44
42
 
45
43
  ```yaml
46
- version: '3.9'
47
-
48
44
 
49
45
  services:
50
46
 
@@ -1,8 +1,8 @@
1
- mesh_sandbox/__init__.py,sha256=qsWFNiLm8PRshbacOTrlIH_bB-tODfzJKwdZTTLVp20,23
1
+ mesh_sandbox/__init__.py,sha256=pC0eKGXF1PH-qw7ZFRVVeFacCzuUE-x6jPc3T4w6jNc,23
2
2
  mesh_sandbox/api.py,sha256=N6k5RH6C9nXkVU4VqK9Cb4Zm0y0btCkzY7vgf4aM-Vg,3924
3
3
  mesh_sandbox/common/__init__.py,sha256=r_QcbsmkiCFrE04QVputqAF2TPIlixiXBcGKgWR4SvE,3430
4
- mesh_sandbox/common/constants.py,sha256=ADa23j-2FL-u4618N5s9IjerXVoGHSVav4S4k-UTpCs,6612
5
- mesh_sandbox/common/exceptions.py,sha256=YQII8w6DQQoKuW0cukEr6PIE9j0rwrqDpCn5lapgmkQ,1481
4
+ mesh_sandbox/common/constants.py,sha256=5D2YKE74r0NkA-gACblg_uws02VlokuqPGGXe-eBdAA,6649
5
+ mesh_sandbox/common/exceptions.py,sha256=8A1zDPDSQN3lIHs7rukuQrPv8hf9zV9-oZOaCj2O6Ls,2603
6
6
  mesh_sandbox/common/fernet.py,sha256=f8yygkVnK1cmQLBgcPifoB4PwGpgiOrG8Yk-6OMDy8o,551
7
7
  mesh_sandbox/common/handler_helpers.py,sha256=Eg00Tide2mL87EoMFw83fDuxiTL5DgIwxHs2RaJasgE,392
8
8
  mesh_sandbox/common/messaging.py,sha256=g0IxoHfgs6EKpKUnMq3WKouFW_N2FXQrf7hPhICmhuQ,14772
@@ -10,7 +10,7 @@ mesh_sandbox/common/mex_headers.py,sha256=DGtbS2Xd010JKLw4W3f1qAhKfXBJ2BsauStleG
10
10
  mesh_sandbox/conftest.py,sha256=nNCRnGABKSByhUVmozHOUE_pRM8Ay30drxsehljGdbw,2193
11
11
  mesh_sandbox/dependencies.py,sha256=GXbb9DMaHcbgGg8v-XCTEQU8wxIaXEDmo1o-xvMPoBE,4078
12
12
  mesh_sandbox/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- mesh_sandbox/handlers/admin.py,sha256=icZsZG1_JKrOU2e2IG6DrOuSzIVBG9P6eZX79zOjrd4,4485
13
+ mesh_sandbox/handlers/admin.py,sha256=Pi2OWkvhyGfSwy7ybDWeuEGqx8gHF09kNz7_ysWkSWY,4820
14
14
  mesh_sandbox/handlers/handshake.py,sha256=XSSWLfjs2lCVQCaEou8k3yMUI1X_YmsesF-WECYJUls,603
15
15
  mesh_sandbox/handlers/inbox.py,sha256=KFEo3E-y66CErwxxw8W1DYbBJdCQp9psVbQo7be4vek,15913
16
16
  mesh_sandbox/handlers/lookup.py,sha256=wDbcI_uKFPNE6udkAvWYOVyR7FOMfLwTh8It924PcqQ,1390
@@ -45,16 +45,16 @@ mesh_sandbox/store/data/mailboxes.jsonl,sha256=ADXpImNo9UH6rY6T9bqgI-BvnbziFaGwc
45
45
  mesh_sandbox/store/data/workflows.jsonl,sha256=RFvycuqVmEkImeXlFD2wjuJFjt6dw581D_raPWstxpU,292
46
46
  mesh_sandbox/store/file_store.py,sha256=FcgqeHQI0fFySIl9NUFSjQ3MdXBDYk_SwcOUf8P8Mak,2398
47
47
  mesh_sandbox/store/memory_store.py,sha256=VJTsPAnwvPAVFTEo-mO2A__lcc7jMnyqiq0JerYmrKY,1643
48
- mesh_sandbox/store/serialisation.py,sha256=SsiZwblvTteIha1T4BIiuqrRo3SVF7sVuoURjSGDYyY,3608
48
+ mesh_sandbox/store/serialisation.py,sha256=EoohT56auv6cZRZoYlnrZUUmassXu76kq8rpHnymh8U,3624
49
49
  mesh_sandbox/test_plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
50
  mesh_sandbox/test_plugin/example_plugin.py,sha256=807XB2_u6Kin8fEuk8PLLDnIzt32YHoKQcI-LIpZNKM,987
51
51
  mesh_sandbox/test_plugin/example_plugin.txt,sha256=aUePU6UUVhpxeP37Pdz5Zxyn_nxI6HCY3qtQBo1iUEI,24
52
52
  mesh_sandbox/tests/__init__.py,sha256=wPjH0Ka1334a0OR9VMUfeYxsr03JqWjHTBnAH_1SB7I,106
53
- mesh_sandbox/tests/admin.py,sha256=f1PhADciDivIs-6rTbMNC2ifW2HAG1CyeOe7P_ve0ws,19234
53
+ mesh_sandbox/tests/admin.py,sha256=vwLFnqCDIaRnYrYc96eBt5cZs_yu9wZqyshjh-_KWsE,20631
54
54
  mesh_sandbox/tests/docker_tests.py,sha256=LTV_N4m6uAtn2Ygol0OVWz81tdO2FgK4vqD7PKtsZbA,1596
55
55
  mesh_sandbox/tests/exceptions.py,sha256=tXRYe0s1AM1HPDKO8obdQZOX63NkkhpgohjkPBYF2Vk,739
56
56
  mesh_sandbox/tests/handshake.py,sha256=DMz8KFNJVd8A9sR9J4-aQWY-eIK5FAiJJwv0UPZcNGY,6263
57
- mesh_sandbox/tests/helpers.py,sha256=8XWurm72Vpykc6dv9v4T1p9qkfboPq7T3j4d9ed3u2o,6882
57
+ mesh_sandbox/tests/helpers.py,sha256=AytRIzf93NLLa6KHvXrBVOkBmz8wagJdhAwMOqhLFT8,6917
58
58
  mesh_sandbox/tests/inbox.py,sha256=lK-T-XRACWUz073osdoJWrf-f4bHe5DSdMh4H7WXP5s,11670
59
59
  mesh_sandbox/tests/java_client_tests.py,sha256=hpcZ4gaSS7wSmz2DVxCT5KVRqy-pPVhh-NbdVoqHlcg,6526
60
60
  mesh_sandbox/tests/lookup.py,sha256=rXNhnLDukZ5woxVB2Uu281hRaS2X1Hx6h0_d7cw_lxs,2780
@@ -68,9 +68,9 @@ mesh_sandbox/views/admin.py,sha256=IUiYp_nb1JZbFVa9k2Bts4a8XvuJsCvgxsvizcJopQ8,8
68
68
  mesh_sandbox/views/error.py,sha256=NC-EgfbirqFa7JxSwOlPvtLzTYrpvBY8gvKtJ-KhnxA,3901
69
69
  mesh_sandbox/views/inbox.py,sha256=zdKB2ulJaDRVnhnagyBYF2nFmN88wslAx3bq7d7feLo,5775
70
70
  mesh_sandbox/views/lookup.py,sha256=c_UtaJMCfBacsV692FQs4_AyDKjPNxIQKu2wgluDSGw,2815
71
- mesh_sandbox/views/outbox.py,sha256=JrJk-R3fTjjcfVFW5Q_Fvxj8oasNRuH7HNJiMiTiD80,4907
71
+ mesh_sandbox/views/outbox.py,sha256=tVDH3yctLGOuJiN0LRjwatxXxsvph5LBZuhFyRrxtp4,4933
72
72
  mesh_sandbox/views/tracking.py,sha256=WEswJT_wLJPieN5CgJ-CGXjDSRF5BdUzVy5XHqzGHAE,8914
73
- mesh_sandbox-1.0.26.dist-info/LICENSE,sha256=usgzIvDUpVX5pYZepJTRXQJqIaz0mdd32GuS5a3PFlY,1051
74
- mesh_sandbox-1.0.26.dist-info/METADATA,sha256=Q5CKOr-drWCqnkFT0EM7AYXM6dOEQIfPEKXNYGoVceE,2413
75
- mesh_sandbox-1.0.26.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
76
- mesh_sandbox-1.0.26.dist-info/RECORD,,
73
+ mesh_sandbox-1.0.28.dist-info/LICENSE,sha256=usgzIvDUpVX5pYZepJTRXQJqIaz0mdd32GuS5a3PFlY,1051
74
+ mesh_sandbox-1.0.28.dist-info/METADATA,sha256=VSoC3iG2y1BPBn4cP_zzvF-VrZJWzcljgii5ElevM_s,2292
75
+ mesh_sandbox-1.0.28.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
76
+ mesh_sandbox-1.0.28.dist-info/RECORD,,