mesh-sandbox 1.0.10__py3-none-any.whl → 1.0.12__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 +1 -1
- mesh_sandbox/api.py +0 -1
- mesh_sandbox/common/__init__.py +1 -3
- mesh_sandbox/common/messaging.py +20 -32
- mesh_sandbox/common/mex_headers.py +0 -1
- mesh_sandbox/conftest.py +2 -6
- mesh_sandbox/dependencies.py +5 -6
- mesh_sandbox/handlers/admin.py +0 -2
- mesh_sandbox/handlers/handshake.py +0 -2
- mesh_sandbox/handlers/inbox.py +1 -10
- mesh_sandbox/handlers/lookup.py +0 -2
- mesh_sandbox/handlers/outbox.py +4 -8
- mesh_sandbox/handlers/tracking.py +0 -2
- mesh_sandbox/models/mailbox.py +0 -2
- mesh_sandbox/models/message.py +2 -5
- mesh_sandbox/models/workflow.py +0 -2
- mesh_sandbox/routers/inbox.py +5 -5
- mesh_sandbox/routers/inbox_count.py +2 -2
- mesh_sandbox/routers/lookup.py +3 -4
- mesh_sandbox/routers/outbox.py +3 -3
- mesh_sandbox/routers/tracking.py +2 -2
- mesh_sandbox/store/base.py +0 -1
- mesh_sandbox/store/canned_store.py +9 -15
- mesh_sandbox/store/file_store.py +0 -1
- mesh_sandbox/store/serialisation.py +2 -3
- mesh_sandbox/test_plugin/example_plugin.py +3 -4
- mesh_sandbox/tests/admin.py +0 -26
- mesh_sandbox/tests/docker_tests.py +0 -2
- mesh_sandbox/tests/exceptions.py +0 -1
- mesh_sandbox/tests/handshake.py +3 -11
- mesh_sandbox/tests/helpers.py +0 -3
- mesh_sandbox/tests/inbox.py +0 -13
- mesh_sandbox/tests/java_client_tests.py +1 -6
- mesh_sandbox/tests/lookup.py +2 -4
- mesh_sandbox/tests/mesh_api_helpers.py +1 -8
- mesh_sandbox/tests/mesh_client_tests.py +7 -12
- mesh_sandbox/tests/messaging_tests.py +13 -23
- mesh_sandbox/tests/outbox.py +1 -15
- mesh_sandbox/tests/serialisation.py +0 -1
- mesh_sandbox/views/error.py +3 -7
- mesh_sandbox/views/inbox.py +4 -7
- mesh_sandbox/views/lookup.py +6 -6
- mesh_sandbox/views/outbox.py +21 -26
- mesh_sandbox/views/tracking.py +0 -4
- {mesh_sandbox-1.0.10.dist-info → mesh_sandbox-1.0.12.dist-info}/METADATA +16 -41
- mesh_sandbox-1.0.12.dist-info/RECORD +76 -0
- mesh_sandbox-1.0.10.dist-info/RECORD +0 -76
- {mesh_sandbox-1.0.10.dist-info → mesh_sandbox-1.0.12.dist-info}/LICENSE +0 -0
- {mesh_sandbox-1.0.10.dist-info → mesh_sandbox-1.0.12.dist-info}/WHEEL +0 -0
|
@@ -41,36 +41,32 @@ def _mesh_client_track_message_by_message_id(base_uri: str, sender_mailbox_id: s
|
|
|
41
41
|
with MeshClient(
|
|
42
42
|
url=base_uri, mailbox=sender_mailbox_id, password=_PASSWORD, shared_key=_SHARED_KEY, max_chunk_size=100
|
|
43
43
|
) as sender:
|
|
44
|
-
tracking = sender.
|
|
44
|
+
tracking = sender.track_message(message_id)
|
|
45
45
|
assert tracking
|
|
46
46
|
return tracking
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
def test_app_health(base_uri: str):
|
|
50
|
-
|
|
51
50
|
with httpx.Client(base_url=base_uri) as client:
|
|
52
51
|
res = client.get("/health")
|
|
53
52
|
assert res.status_code == status.HTTP_200_OK
|
|
54
53
|
|
|
55
54
|
|
|
56
55
|
def test_handshake(base_uri: str):
|
|
57
|
-
|
|
58
56
|
with MeshClient(url=base_uri, mailbox=_CANNED_MAILBOX1, password=_PASSWORD, shared_key=_SHARED_KEY) as client:
|
|
59
57
|
res = client.handshake()
|
|
60
58
|
assert res == b"hello"
|
|
61
59
|
|
|
62
60
|
|
|
63
61
|
def test_handshake_bad_password(base_uri: str):
|
|
64
|
-
|
|
65
62
|
with MeshClient(url=base_uri, mailbox=_CANNED_MAILBOX1, password="BAD", shared_key=_SHARED_KEY) as client:
|
|
66
|
-
|
|
67
63
|
with pytest.raises(HTTPError) as err:
|
|
68
64
|
client.handshake()
|
|
65
|
+
assert err.value.response is not None
|
|
69
66
|
assert err.value.response.status_code == status.HTTP_403_FORBIDDEN
|
|
70
67
|
|
|
71
68
|
|
|
72
69
|
def test_send_receive_chunked_message(base_uri: str):
|
|
73
|
-
|
|
74
70
|
workflow_id = uuid4().hex
|
|
75
71
|
sent_payload = b"a" * 1000
|
|
76
72
|
message_id = _mesh_client_send_message(base_uri, _CANNED_MAILBOX1, _CANNED_MAILBOX2, workflow_id, sent_payload)
|
|
@@ -90,16 +86,15 @@ def test_send_receive_chunked_message(base_uri: str):
|
|
|
90
86
|
|
|
91
87
|
|
|
92
88
|
def test_track_message_by_message_id(base_uri: str):
|
|
93
|
-
|
|
94
89
|
workflow_id = uuid4().hex
|
|
95
90
|
sent_payload = b"a" * 1000
|
|
96
91
|
message_id = _mesh_client_send_message(base_uri, _CANNED_MAILBOX1, _CANNED_MAILBOX2, workflow_id, sent_payload)
|
|
97
92
|
assert _mesh_client_get_inbox_count(base_uri, _CANNED_MAILBOX2) == 1
|
|
98
93
|
|
|
99
94
|
tracking = _mesh_client_track_message_by_message_id(base_uri, _CANNED_MAILBOX1, message_id)
|
|
100
|
-
assert tracking["
|
|
101
|
-
assert tracking["status"] == "
|
|
102
|
-
assert tracking["sender"] == _CANNED_MAILBOX1
|
|
95
|
+
assert tracking["message_id"] == message_id
|
|
96
|
+
assert tracking["status"] == "accepted"
|
|
97
|
+
# assert tracking["sender"] == _CANNED_MAILBOX1
|
|
103
98
|
assert tracking["recipient"] == _CANNED_MAILBOX2
|
|
104
99
|
|
|
105
100
|
with MeshClient(url=base_uri, mailbox=_CANNED_MAILBOX2, password=_PASSWORD, shared_key=_SHARED_KEY) as recipient:
|
|
@@ -107,5 +102,5 @@ def test_track_message_by_message_id(base_uri: str):
|
|
|
107
102
|
message.acknowledge()
|
|
108
103
|
|
|
109
104
|
tracking = _mesh_client_track_message_by_message_id(base_uri, _CANNED_MAILBOX1, message_id)
|
|
110
|
-
assert tracking["
|
|
111
|
-
assert tracking["status"] == "
|
|
105
|
+
assert tracking["message_id"] == message_id
|
|
106
|
+
assert tracking["status"] == "acknowledged"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Optional
|
|
1
|
+
from typing import Any, ClassVar, Optional
|
|
2
2
|
from uuid import uuid4
|
|
3
3
|
|
|
4
4
|
import pytest
|
|
@@ -17,8 +17,8 @@ from ..models.message import (
|
|
|
17
17
|
from .helpers import temp_env_vars
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
@pytest.fixture(name="message"
|
|
21
|
-
def
|
|
20
|
+
@pytest.fixture(name="message")
|
|
21
|
+
def create_message() -> Message:
|
|
22
22
|
message = Message(
|
|
23
23
|
message_id=uuid4().hex.upper(),
|
|
24
24
|
workflow_id=uuid4().hex.upper(),
|
|
@@ -30,26 +30,24 @@ def _create_message() -> Message:
|
|
|
30
30
|
return message
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
@pytest.fixture(name="background_tasks"
|
|
34
|
-
def
|
|
33
|
+
@pytest.fixture(name="background_tasks")
|
|
34
|
+
def background_tasks() -> BackgroundTasks:
|
|
35
35
|
return BackgroundTasks()
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
async def test_canned_store_raises_for_save_message(message: Message):
|
|
39
|
+
with temp_env_vars(STORE_MODE="canned"):
|
|
40
|
+
store = get_store()
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
with temp_env_vars(STORE_MODE="canned"):
|
|
42
|
-
store = get_store()
|
|
42
|
+
with pytest.raises(NotImplementedError):
|
|
43
43
|
await store.save_message(message)
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
async def test_canned_messaging_does_not_raise_no_bgt(message: Message):
|
|
47
|
-
|
|
48
47
|
calls = []
|
|
49
48
|
|
|
50
49
|
class TestPlugin:
|
|
51
|
-
|
|
52
|
-
triggers = ["before_save_message", "after_save_message", "save_message_error"]
|
|
50
|
+
triggers: ClassVar[list[str]] = ["before_save_message", "after_save_message", "save_message_error"]
|
|
53
51
|
|
|
54
52
|
async def on_event(self, event: str, args: dict[str, Any], exception: Optional[Exception] = None):
|
|
55
53
|
calls.append((event, args, exception))
|
|
@@ -64,12 +62,10 @@ async def test_canned_messaging_does_not_raise_no_bgt(message: Message):
|
|
|
64
62
|
|
|
65
63
|
|
|
66
64
|
async def test_canned_messaging_does_not_raise_with_bgt(message: Message, background_tasks: BackgroundTasks):
|
|
67
|
-
|
|
68
65
|
calls = []
|
|
69
66
|
|
|
70
67
|
class Test2Plugin:
|
|
71
|
-
|
|
72
|
-
triggers = ["before_save_message", "after_save_message", "save_message_error"]
|
|
68
|
+
triggers: ClassVar[list[str]] = ["before_save_message", "after_save_message", "save_message_error"]
|
|
73
69
|
|
|
74
70
|
async def on_event(self, event: str, args: dict[str, Any], exception: Optional[Exception] = None):
|
|
75
71
|
calls.append((event, args, exception))
|
|
@@ -87,12 +83,10 @@ async def test_canned_messaging_does_not_raise_with_bgt(message: Message, backgr
|
|
|
87
83
|
|
|
88
84
|
|
|
89
85
|
async def test_messaging_does_not_raise_with_bgt(message: Message, background_tasks: BackgroundTasks):
|
|
90
|
-
|
|
91
86
|
calls = []
|
|
92
87
|
|
|
93
88
|
class Test2Plugin:
|
|
94
|
-
|
|
95
|
-
triggers = ["before_save_message", "after_save_message", "save_message_error"]
|
|
89
|
+
triggers: ClassVar[list[str]] = ["before_save_message", "after_save_message", "save_message_error"]
|
|
96
90
|
|
|
97
91
|
async def on_event(self, event: str, args: dict[str, Any], exception: Optional[Exception] = None):
|
|
98
92
|
calls.append((event, args, exception))
|
|
@@ -110,12 +104,10 @@ async def test_messaging_does_not_raise_with_bgt(message: Message, background_ta
|
|
|
110
104
|
|
|
111
105
|
|
|
112
106
|
async def test_error_events_raised_with_bgt(message: Message, background_tasks: BackgroundTasks):
|
|
113
|
-
|
|
114
107
|
calls = []
|
|
115
108
|
|
|
116
109
|
class Test2Plugin:
|
|
117
|
-
|
|
118
|
-
triggers = ["before_save_message", "after_save_message", "save_message_error"]
|
|
110
|
+
triggers: ClassVar[list[str]] = ["before_save_message", "after_save_message", "save_message_error"]
|
|
119
111
|
|
|
120
112
|
async def on_event(self, event: str, args: dict[str, Any], exception: Optional[Exception] = None):
|
|
121
113
|
calls.append((event, args, exception))
|
|
@@ -133,15 +125,13 @@ async def test_error_events_raised_with_bgt(message: Message, background_tasks:
|
|
|
133
125
|
|
|
134
126
|
|
|
135
127
|
class TestDiscoveredPlugin:
|
|
136
|
-
|
|
137
|
-
triggers = ["before_accept_message"]
|
|
128
|
+
triggers: ClassVar[list[str]] = ["before_accept_message"]
|
|
138
129
|
|
|
139
130
|
async def on_event(self, event: str, args: dict[str, Any], err: Optional[Exception] = None):
|
|
140
131
|
pass
|
|
141
132
|
|
|
142
133
|
|
|
143
134
|
async def test_plugin_discovery(monkeypatch):
|
|
144
|
-
|
|
145
135
|
messaging = Messaging(store=get_store(), plugins_module=tests_module)
|
|
146
136
|
|
|
147
137
|
calls = []
|
mesh_sandbox/tests/outbox.py
CHANGED
|
@@ -18,7 +18,6 @@ _CANNED_MAILBOX2 = "X26ABC2"
|
|
|
18
18
|
|
|
19
19
|
@pytest.mark.parametrize("accept", [APP_V1_JSON, APP_V2_JSON])
|
|
20
20
|
def test_memory_send_message_with_local_id(app: TestClient, accept: str):
|
|
21
|
-
|
|
22
21
|
sender = _CANNED_MAILBOX1
|
|
23
22
|
recipient = _CANNED_MAILBOX2
|
|
24
23
|
|
|
@@ -130,12 +129,10 @@ def test_memory_send_message_with_local_id(app: TestClient, accept: str):
|
|
|
130
129
|
|
|
131
130
|
@pytest.mark.parametrize("accept", [APP_V1_JSON, APP_V2_JSON])
|
|
132
131
|
def test_file_send_message_with_local_id(app: TestClient, accept: str, tmp_path: str):
|
|
133
|
-
|
|
134
132
|
sender = _CANNED_MAILBOX1
|
|
135
133
|
recipient = _CANNED_MAILBOX2
|
|
136
134
|
|
|
137
135
|
with temp_env_vars(STORE_MODE="file", MAILBOXES_DATA_DIR=tmp_path):
|
|
138
|
-
|
|
139
136
|
res = app.get(
|
|
140
137
|
f"/messageexchange/{recipient}/inbox",
|
|
141
138
|
headers={Headers.Authorization: generate_auth_token(recipient), Headers.Accept: accept},
|
|
@@ -193,7 +190,6 @@ def test_file_send_message_with_local_id(app: TestClient, accept: str, tmp_path:
|
|
|
193
190
|
|
|
194
191
|
@pytest.mark.parametrize("accept", [APP_V1_JSON, APP_V2_JSON])
|
|
195
192
|
def test_memory_send_chunked_message(app: TestClient, accept: str):
|
|
196
|
-
|
|
197
193
|
sender = _CANNED_MAILBOX1
|
|
198
194
|
recipient = _CANNED_MAILBOX2
|
|
199
195
|
|
|
@@ -313,12 +309,10 @@ def test_memory_send_chunked_message(app: TestClient, accept: str):
|
|
|
313
309
|
|
|
314
310
|
@pytest.mark.parametrize("accept", [APP_V1_JSON, APP_V2_JSON])
|
|
315
311
|
def test_file_send_chunked_message(app: TestClient, accept: str, tmp_path: str): # pylint: disable=too-many-statements
|
|
316
|
-
|
|
317
312
|
sender = _CANNED_MAILBOX1
|
|
318
313
|
recipient = _CANNED_MAILBOX2
|
|
319
314
|
|
|
320
315
|
with temp_env_vars(STORE_MODE="file", MAILBOXES_DATA_DIR=tmp_path):
|
|
321
|
-
|
|
322
316
|
res = app.get(
|
|
323
317
|
f"/messageexchange/{recipient}/inbox",
|
|
324
318
|
headers={Headers.Authorization: generate_auth_token(recipient), Headers.Accept: accept},
|
|
@@ -462,7 +456,6 @@ def test_file_send_chunked_message(app: TestClient, accept: str, tmp_path: str):
|
|
|
462
456
|
],
|
|
463
457
|
)
|
|
464
458
|
def test_mex_content_compress_validation(app: TestClient, value: str):
|
|
465
|
-
|
|
466
459
|
sender = _CANNED_MAILBOX1
|
|
467
460
|
recipient = _CANNED_MAILBOX2
|
|
468
461
|
|
|
@@ -500,7 +493,6 @@ def test_mex_content_compress_validation(app: TestClient, value: str):
|
|
|
500
493
|
],
|
|
501
494
|
)
|
|
502
495
|
def test_mex_content_encrypted_validation(app: TestClient, value: str):
|
|
503
|
-
|
|
504
496
|
sender = _CANNED_MAILBOX1
|
|
505
497
|
recipient = _CANNED_MAILBOX2
|
|
506
498
|
|
|
@@ -538,7 +530,6 @@ def test_mex_content_encrypted_validation(app: TestClient, value: str):
|
|
|
538
530
|
],
|
|
539
531
|
)
|
|
540
532
|
def test_mex_content_compressed_validation(app: TestClient, value: str):
|
|
541
|
-
|
|
542
533
|
sender = _CANNED_MAILBOX1
|
|
543
534
|
recipient = _CANNED_MAILBOX2
|
|
544
535
|
|
|
@@ -547,7 +538,7 @@ def test_mex_content_compressed_validation(app: TestClient, value: str):
|
|
|
547
538
|
|
|
548
539
|
|
|
549
540
|
@pytest.mark.parametrize(
|
|
550
|
-
"mex_content_checksum, expected_response_status",
|
|
541
|
+
("mex_content_checksum", "expected_response_status"),
|
|
551
542
|
[
|
|
552
543
|
("", status.HTTP_202_ACCEPTED),
|
|
553
544
|
("b10a8db164e0754105b7a99be72e3fe5", status.HTTP_202_ACCEPTED),
|
|
@@ -559,7 +550,6 @@ def test_mex_content_compressed_validation(app: TestClient, value: str):
|
|
|
559
550
|
],
|
|
560
551
|
)
|
|
561
552
|
def test_mex_content_checksum_validation(app: TestClient, mex_content_checksum: str, expected_response_status: int):
|
|
562
|
-
|
|
563
553
|
sender = _CANNED_MAILBOX1
|
|
564
554
|
recipient = _CANNED_MAILBOX2
|
|
565
555
|
|
|
@@ -571,7 +561,6 @@ def test_mex_content_checksum_validation(app: TestClient, mex_content_checksum:
|
|
|
571
561
|
|
|
572
562
|
|
|
573
563
|
def test_mex_local_id_validation(app: TestClient):
|
|
574
|
-
|
|
575
564
|
sender = _CANNED_MAILBOX1
|
|
576
565
|
recipient = _CANNED_MAILBOX2
|
|
577
566
|
|
|
@@ -584,7 +573,6 @@ def test_mex_local_id_validation(app: TestClient):
|
|
|
584
573
|
|
|
585
574
|
@pytest.mark.parametrize("accept", [APP_V1_JSON, APP_V2_JSON])
|
|
586
575
|
def test_checksum_is_returned(app: TestClient, accept: str):
|
|
587
|
-
|
|
588
576
|
sender = _CANNED_MAILBOX1
|
|
589
577
|
recipient = _CANNED_MAILBOX2
|
|
590
578
|
checksum = uuid4().hex
|
|
@@ -614,7 +602,6 @@ def test_checksum_is_returned(app: TestClient, accept: str):
|
|
|
614
602
|
|
|
615
603
|
|
|
616
604
|
def test_rich_outbox_returns_most_recent_messages(app: TestClient):
|
|
617
|
-
|
|
618
605
|
sender = _CANNED_MAILBOX1
|
|
619
606
|
recipient = _CANNED_MAILBOX2
|
|
620
607
|
total_messages = 105
|
|
@@ -627,7 +614,6 @@ def test_rich_outbox_returns_most_recent_messages(app: TestClient):
|
|
|
627
614
|
|
|
628
615
|
message_ids = {}
|
|
629
616
|
for index in range(total_messages):
|
|
630
|
-
|
|
631
617
|
resp = mesh_api_send_message(
|
|
632
618
|
app,
|
|
633
619
|
sender_mailbox_id=sender,
|
mesh_sandbox/views/error.py
CHANGED
|
@@ -12,7 +12,6 @@ from ..dependencies import parse_accept_header
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class MeshErrorV1(BaseModel):
|
|
15
|
-
|
|
16
15
|
messageID: Optional[str] = Field(description="message_id associated with the error", default=None)
|
|
17
16
|
errorEvent: Optional[str] = Field(description="message error phase", default="")
|
|
18
17
|
errorCode: Optional[str] = Field(description="message error code", default="")
|
|
@@ -20,7 +19,7 @@ class MeshErrorV1(BaseModel):
|
|
|
20
19
|
|
|
21
20
|
class Config:
|
|
22
21
|
title = "mesh_error_v1"
|
|
23
|
-
|
|
22
|
+
json_schema_extra = {
|
|
24
23
|
"example": {
|
|
25
24
|
"messageID": "20220228174323222_ABCDEF",
|
|
26
25
|
"errorEvent": "SEND",
|
|
@@ -31,14 +30,13 @@ class MeshErrorV1(BaseModel):
|
|
|
31
30
|
|
|
32
31
|
|
|
33
32
|
class MeshErrorV2(BaseModel):
|
|
34
|
-
|
|
35
33
|
message_id: Optional[str] = Field(description="message id associated with the error", default=None)
|
|
36
34
|
internal_id: Optional[str] = Field(description="internal id associated with the error", default=None)
|
|
37
35
|
detail: list[dict] = Field(description="error detail", default_factory=list)
|
|
38
36
|
|
|
39
37
|
class Config:
|
|
40
38
|
title = "mesh_error_v2"
|
|
41
|
-
|
|
39
|
+
json_schema_extra = {
|
|
42
40
|
"example": {
|
|
43
41
|
"message_id": "20220228174323222_ABCDEF",
|
|
44
42
|
"internal_id": "20220228174323222_ABCDEF",
|
|
@@ -48,7 +46,6 @@ class MeshErrorV2(BaseModel):
|
|
|
48
46
|
|
|
49
47
|
|
|
50
48
|
def get_validation_error_response(_request: Request, exc: RequestValidationError) -> Response:
|
|
51
|
-
|
|
52
49
|
for err in exc.errors():
|
|
53
50
|
if err["loc"][0] == "header" and cast(str, err["loc"][1]).lower() == Headers.Authorization.lower():
|
|
54
51
|
return Response(status_code=status.HTTP_403_FORBIDDEN)
|
|
@@ -67,9 +64,8 @@ def get_error_response(
|
|
|
67
64
|
status_code: int,
|
|
68
65
|
detail: Union[str, dict, None] = None,
|
|
69
66
|
message_id: Optional[str] = None,
|
|
70
|
-
headers: dict = None, # type: ignore[assignment]
|
|
67
|
+
headers: Optional[dict] = None, # type: ignore[assignment]
|
|
71
68
|
) -> JSONResponse:
|
|
72
|
-
|
|
73
69
|
internal_id = None
|
|
74
70
|
if hasattr(request.state, "internal_id"):
|
|
75
71
|
internal_id = request.state.internal_id
|
mesh_sandbox/views/inbox.py
CHANGED
|
@@ -10,16 +10,14 @@ from . import RichMessageV1
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class InboxV1(BaseModel):
|
|
13
|
-
|
|
14
13
|
messages: list[str] = Field(description="list of message ids in the inbox, up to 'max_results'")
|
|
15
14
|
|
|
16
15
|
class Config:
|
|
17
16
|
title = "inbox_v1"
|
|
18
|
-
|
|
17
|
+
json_schema_extra = {"example": {"messages": ["20220228174323222_ABCDEF", "20220228174323333_ABCDEF"]}}
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
class InboxV2(BaseModel):
|
|
22
|
-
|
|
23
21
|
messages: list[str] = Field(description="list of message ids in the inbox, up to 'max_results'")
|
|
24
22
|
links: Optional[dict[str, str]] = Field(description="map of links, e.g. links.next if more results exist")
|
|
25
23
|
approx_inbox_count: Optional[int] = Field(
|
|
@@ -31,7 +29,7 @@ class InboxV2(BaseModel):
|
|
|
31
29
|
|
|
32
30
|
class Config:
|
|
33
31
|
title = "inbox_v2"
|
|
34
|
-
|
|
32
|
+
json_schema_extra = {
|
|
35
33
|
"example": {
|
|
36
34
|
"messages": ["20220228174323222_ABCDEF", "20220228174323333_ABCDEF"],
|
|
37
35
|
"links": {
|
|
@@ -45,12 +43,11 @@ class InboxV2(BaseModel):
|
|
|
45
43
|
|
|
46
44
|
|
|
47
45
|
class AcknowledgeV1(BaseModel):
|
|
48
|
-
|
|
49
46
|
messageId: str = Field(description="message_id as passed in on the url")
|
|
50
47
|
|
|
51
48
|
class Config:
|
|
52
49
|
title = "acknowledge_message"
|
|
53
|
-
|
|
50
|
+
json_schema_extra = {"example": {"messageId": ["20220228174323222_ABCDEF"]}}
|
|
54
51
|
|
|
55
52
|
|
|
56
53
|
class InboxMessageV1(RichMessageV1):
|
|
@@ -94,7 +91,7 @@ class RichInboxView(BaseModel):
|
|
|
94
91
|
|
|
95
92
|
class Config:
|
|
96
93
|
title = "rich_inbox"
|
|
97
|
-
|
|
94
|
+
json_schema_extra = {
|
|
98
95
|
"example": {
|
|
99
96
|
"valid_at": "2021-11-22T14:35:52.29Z",
|
|
100
97
|
"messages": [
|
mesh_sandbox/views/lookup.py
CHANGED
|
@@ -13,7 +13,7 @@ class EndpointLookupItemV1(BaseModel):
|
|
|
13
13
|
|
|
14
14
|
class Config:
|
|
15
15
|
title = "endpoint_lookup_item_v1"
|
|
16
|
-
|
|
16
|
+
json_schema_extra = {
|
|
17
17
|
"example": {"address": "X2612345", "description": "this is a test mailbox", "endpoint_type": "MESH"}
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -24,7 +24,7 @@ class EndpointLookupV1(BaseModel):
|
|
|
24
24
|
|
|
25
25
|
class Config:
|
|
26
26
|
title = "endpoint_lookup_v1"
|
|
27
|
-
|
|
27
|
+
json_schema_extra = {
|
|
28
28
|
"example": {
|
|
29
29
|
"query_id": "20220228174323222_ABCDEF",
|
|
30
30
|
"results": {"address": "X2612345", "description": "this is a test mailbox", "endpoint_type": "MESH"},
|
|
@@ -38,7 +38,7 @@ class MailboxLookupItem(BaseModel):
|
|
|
38
38
|
|
|
39
39
|
class Config:
|
|
40
40
|
title = "lookup_item_v2"
|
|
41
|
-
|
|
41
|
+
json_schema_extra = {"example": {"mailbox_id": "X2612345", "mailbox_name": "this is a test mailbox"}}
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
class MailboxLookupV2(BaseModel):
|
|
@@ -46,13 +46,14 @@ class MailboxLookupV2(BaseModel):
|
|
|
46
46
|
|
|
47
47
|
class Config:
|
|
48
48
|
title = "lookup_v2"
|
|
49
|
-
|
|
49
|
+
json_schema_extra = {
|
|
50
|
+
"example": {"results": {"mailbox_id": "X2612345", "mailbox_name": "this is a test mailbox"}}
|
|
51
|
+
}
|
|
50
52
|
|
|
51
53
|
|
|
52
54
|
def endpoint_lookup_response(
|
|
53
55
|
mailboxes: list[Mailbox], model_version: int = 1
|
|
54
56
|
) -> Union[EndpointLookupV1, MailboxLookupV2]:
|
|
55
|
-
|
|
56
57
|
if model_version < 2:
|
|
57
58
|
return EndpointLookupV1(
|
|
58
59
|
query_id=uuid4().hex,
|
|
@@ -75,7 +76,6 @@ def endpoint_lookup_response(
|
|
|
75
76
|
|
|
76
77
|
# pylint: disable=unused-argument
|
|
77
78
|
def workflow_search_response(mailboxes: list[Mailbox], model_version: int = 1) -> MailboxLookupV2:
|
|
78
|
-
|
|
79
79
|
return MailboxLookupV2(
|
|
80
80
|
results=[
|
|
81
81
|
MailboxLookupItem(
|
mesh_sandbox/views/outbox.py
CHANGED
|
@@ -14,7 +14,7 @@ class SendMessageV1(BaseModel):
|
|
|
14
14
|
|
|
15
15
|
class Config:
|
|
16
16
|
title = "send_message"
|
|
17
|
-
|
|
17
|
+
json_schema_extra = {"example": {"messageID": "20220228174323222_ABCDEF"}}
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class SendMessageV2(BaseModel):
|
|
@@ -22,7 +22,7 @@ class SendMessageV2(BaseModel):
|
|
|
22
22
|
|
|
23
23
|
class Config:
|
|
24
24
|
title = "send_message"
|
|
25
|
-
|
|
25
|
+
json_schema_extra = {"example": {"message_id": "20220228174323222_ABCDEF"}}
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class UploadChunkV1(BaseModel):
|
|
@@ -31,11 +31,10 @@ class UploadChunkV1(BaseModel):
|
|
|
31
31
|
|
|
32
32
|
class Config:
|
|
33
33
|
title = "upload_chunk"
|
|
34
|
-
|
|
34
|
+
json_schema_extra = {"example": {"messageID": "20220228174323222_ABCDEF", "blockID": 3}}
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class OutboxMessageV1(RichMessageV1):
|
|
38
|
-
|
|
39
38
|
# pylint: disable=E0213
|
|
40
39
|
class Config:
|
|
41
40
|
validate_assignment = True
|
|
@@ -53,7 +52,7 @@ class RichOutboxView(BaseModel):
|
|
|
53
52
|
class Config:
|
|
54
53
|
validate_assignment = True
|
|
55
54
|
title = "rich_outbox"
|
|
56
|
-
|
|
55
|
+
json_schema_extra = {
|
|
57
56
|
"example": {
|
|
58
57
|
"valid_at": "2021-11-22T14:35:52.29Z",
|
|
59
58
|
"messages": [
|
|
@@ -85,26 +84,24 @@ class RichOutboxView(BaseModel):
|
|
|
85
84
|
|
|
86
85
|
|
|
87
86
|
def map_to_outbox_message(messages: list[Message]) -> list[OutboxMessageV1]:
|
|
88
|
-
return
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
total_chunks=msg.total_chunks or 0,
|
|
104
|
-
),
|
|
105
|
-
messages,
|
|
87
|
+
return [
|
|
88
|
+
OutboxMessageV1(
|
|
89
|
+
message_id=msg.message_id,
|
|
90
|
+
expiry_timestamp=msg.inbox_expiry_timestamp,
|
|
91
|
+
local_id=msg.metadata.local_id,
|
|
92
|
+
message_type=msg.message_type,
|
|
93
|
+
recipient=msg.recipient.mailbox_id,
|
|
94
|
+
recipient_name=msg.recipient.mailbox_name,
|
|
95
|
+
sender=msg.sender.mailbox_id,
|
|
96
|
+
sender_name=msg.sender.mailbox_name,
|
|
97
|
+
sent_date=msg.created_timestamp,
|
|
98
|
+
status=msg.status,
|
|
99
|
+
status_code=msg.last_event.code,
|
|
100
|
+
workflow_id=msg.workflow_id,
|
|
101
|
+
total_chunks=msg.total_chunks or 0,
|
|
106
102
|
)
|
|
107
|
-
|
|
103
|
+
for msg in messages
|
|
104
|
+
]
|
|
108
105
|
|
|
109
106
|
|
|
110
107
|
def get_rich_outbox_view(messages: list[Message], links: dict[str, str]) -> JSONResponse:
|
|
@@ -121,7 +118,6 @@ def get_rich_outbox_view(messages: list[Message], links: dict[str, str]) -> JSON
|
|
|
121
118
|
|
|
122
119
|
|
|
123
120
|
def send_message_response(message: Message, model_version: int = 1) -> Union[SendMessageV1, SendMessageV2]:
|
|
124
|
-
|
|
125
121
|
if model_version < 2:
|
|
126
122
|
return SendMessageV1(messageID=message.message_id)
|
|
127
123
|
|
|
@@ -129,7 +125,6 @@ def send_message_response(message: Message, model_version: int = 1) -> Union[Sen
|
|
|
129
125
|
|
|
130
126
|
|
|
131
127
|
def upload_chunk_response(message: Message, chunk_number: int, model_version: int = 1) -> Optional[UploadChunkV1]:
|
|
132
|
-
|
|
133
128
|
if model_version < 2:
|
|
134
129
|
return UploadChunkV1(messageID=message.message_id, blockID=chunk_number)
|
|
135
130
|
|
mesh_sandbox/views/tracking.py
CHANGED
|
@@ -9,7 +9,6 @@ _EMPTY: Final[str] = ""
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class TrackingV1(BaseModel):
|
|
12
|
-
|
|
13
12
|
addressType: Optional[str] = "ALL"
|
|
14
13
|
checksum: Optional[str] = Field(description="message status e.g. 'accepted' 'acknowledged'")
|
|
15
14
|
chunkCount: Optional[int] = Field(description="number of message chunks")
|
|
@@ -62,7 +61,6 @@ class TrackingV1(BaseModel):
|
|
|
62
61
|
|
|
63
62
|
|
|
64
63
|
class TrackingV2(BaseModel):
|
|
65
|
-
|
|
66
64
|
message_id: str = Field(description="message identifier")
|
|
67
65
|
local_id: Optional[str] = Field(description="local identifier supplied by sender")
|
|
68
66
|
workflow_id: Optional[str] = Field(description="message workflow identifier")
|
|
@@ -98,7 +96,6 @@ def _format_timestamp(timestamp: Optional[datetime]) -> str:
|
|
|
98
96
|
|
|
99
97
|
|
|
100
98
|
def create_tracking_response(message: Message, model_version: int = 1) -> Union[TrackingV1, TrackingV2]:
|
|
101
|
-
|
|
102
99
|
error_event = message.error_event
|
|
103
100
|
|
|
104
101
|
successful = bool(message.message_type == MessageType.DATA and not error_event)
|
|
@@ -123,7 +120,6 @@ def create_tracking_response(message: Message, model_version: int = 1) -> Union[
|
|
|
123
120
|
status_timestamp_string = _format_timestamp(status_timestamp)
|
|
124
121
|
|
|
125
122
|
if model_version < 2:
|
|
126
|
-
|
|
127
123
|
return TrackingV1(
|
|
128
124
|
checksum=message.metadata.checksum or _EMPTY,
|
|
129
125
|
chunkCount=message.total_chunks,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mesh-sandbox
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.12
|
|
4
4
|
Summary: NHSDigital mesh sandbox, a locally testable version of the MESH api
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: spinecore
|
|
@@ -10,12 +10,12 @@ Classifier: Programming Language :: Python :: 3
|
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.9
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
-
Requires-Dist: cryptography (>=
|
|
14
|
-
Requires-Dist: fastapi (>=0.
|
|
15
|
-
Requires-Dist: gunicorn (>=
|
|
13
|
+
Requires-Dist: cryptography (>=41.0.4,<42.0.0)
|
|
14
|
+
Requires-Dist: fastapi (>=0.103.1,<0.104.0)
|
|
15
|
+
Requires-Dist: gunicorn (>=21.2.0,<22.0.0)
|
|
16
16
|
Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
|
|
17
17
|
Requires-Dist: types-python-dateutil (>=2.8.9,<3.0.0)
|
|
18
|
-
Requires-Dist: uvicorn (>=0.
|
|
18
|
+
Requires-Dist: uvicorn (>=0.23.2,<0.24.0)
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
|
|
21
21
|
MESH Sandbox
|
|
@@ -51,57 +51,32 @@ services:
|
|
|
51
51
|
build:
|
|
52
52
|
context: https://github.com/NHSDigital/mesh-sandbox.git#refs/tags/v1.0.4
|
|
53
53
|
ports:
|
|
54
|
-
- "8700:
|
|
54
|
+
- "8700:443"
|
|
55
55
|
deploy:
|
|
56
56
|
restart_policy:
|
|
57
57
|
condition: on-failure
|
|
58
58
|
max_attempts: 3
|
|
59
59
|
healthcheck:
|
|
60
|
-
test: curl -
|
|
60
|
+
test: curl -ksf https://localhost/health || exit 1
|
|
61
61
|
interval: 3s
|
|
62
62
|
timeout: 10s
|
|
63
63
|
environment:
|
|
64
64
|
- SHARED_KEY=TestKey
|
|
65
|
+
- SSL=yes
|
|
66
|
+
# - STORE_MODE=file # store mode file will persist data to disk
|
|
65
67
|
volumes:
|
|
66
68
|
# mount a different mailboxes.jsonl to pre created mailboxes
|
|
67
69
|
- ./src/mesh_sandbox/store/data/mailboxes.jsonl:/app/mesh_sandbox/store/data/mailboxes.jsonl:ro
|
|
68
|
-
|
|
69
|
-
#
|
|
70
|
+
- ./src/mesh_sandbox/test_plugin:/app/mesh_sandbox/plugins:ro
|
|
71
|
+
# you can mount a directory if you want access the stored messages
|
|
72
|
+
# - ./messages:/tmp/mesh_store
|
|
73
|
+
# you can also mount different server cert and key if using ssl and you need a trusted certificate
|
|
74
|
+
# - ./mycert.pem:/tmp/server-cert.pem:ro
|
|
75
|
+
# - ./mycert.key:/tmp/server-cert.key:ro
|
|
70
76
|
|
|
71
77
|
```
|
|
72
78
|
|
|
73
79
|
Guidance for contributors
|
|
74
80
|
-------------------------
|
|
75
|
-
|
|
76
|
-
this project uses
|
|
77
|
-
|
|
78
|
-
- python 3.9
|
|
79
|
-
- java coretto11
|
|
80
|
-
- poetry > 1.2
|
|
81
|
-
|
|
82
|
-
Setup
|
|
83
|
-
-----
|
|
84
|
-
|
|
85
|
-
using asdf
|
|
86
|
-
[install asdf](https://asdf-vm.com/guide/getting-started.html#_3-install-asdf)
|
|
87
|
-
|
|
88
|
-
get the required plugins
|
|
89
|
-
|
|
90
|
-
```bash
|
|
91
|
-
asdf plugin add python
|
|
92
|
-
asdf plugin add java
|
|
93
|
-
asdf plugin add poetry
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
install the tools
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
asdf install
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
install the dependencies
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
make install
|
|
106
|
-
```
|
|
81
|
+
[contributing](CONTRIBUTING.md)
|
|
107
82
|
|