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.
Files changed (49) hide show
  1. mesh_sandbox/__init__.py +1 -1
  2. mesh_sandbox/api.py +0 -1
  3. mesh_sandbox/common/__init__.py +1 -3
  4. mesh_sandbox/common/messaging.py +20 -32
  5. mesh_sandbox/common/mex_headers.py +0 -1
  6. mesh_sandbox/conftest.py +2 -6
  7. mesh_sandbox/dependencies.py +5 -6
  8. mesh_sandbox/handlers/admin.py +0 -2
  9. mesh_sandbox/handlers/handshake.py +0 -2
  10. mesh_sandbox/handlers/inbox.py +1 -10
  11. mesh_sandbox/handlers/lookup.py +0 -2
  12. mesh_sandbox/handlers/outbox.py +4 -8
  13. mesh_sandbox/handlers/tracking.py +0 -2
  14. mesh_sandbox/models/mailbox.py +0 -2
  15. mesh_sandbox/models/message.py +2 -5
  16. mesh_sandbox/models/workflow.py +0 -2
  17. mesh_sandbox/routers/inbox.py +5 -5
  18. mesh_sandbox/routers/inbox_count.py +2 -2
  19. mesh_sandbox/routers/lookup.py +3 -4
  20. mesh_sandbox/routers/outbox.py +3 -3
  21. mesh_sandbox/routers/tracking.py +2 -2
  22. mesh_sandbox/store/base.py +0 -1
  23. mesh_sandbox/store/canned_store.py +9 -15
  24. mesh_sandbox/store/file_store.py +0 -1
  25. mesh_sandbox/store/serialisation.py +2 -3
  26. mesh_sandbox/test_plugin/example_plugin.py +3 -4
  27. mesh_sandbox/tests/admin.py +0 -26
  28. mesh_sandbox/tests/docker_tests.py +0 -2
  29. mesh_sandbox/tests/exceptions.py +0 -1
  30. mesh_sandbox/tests/handshake.py +3 -11
  31. mesh_sandbox/tests/helpers.py +0 -3
  32. mesh_sandbox/tests/inbox.py +0 -13
  33. mesh_sandbox/tests/java_client_tests.py +1 -6
  34. mesh_sandbox/tests/lookup.py +2 -4
  35. mesh_sandbox/tests/mesh_api_helpers.py +1 -8
  36. mesh_sandbox/tests/mesh_client_tests.py +7 -12
  37. mesh_sandbox/tests/messaging_tests.py +13 -23
  38. mesh_sandbox/tests/outbox.py +1 -15
  39. mesh_sandbox/tests/serialisation.py +0 -1
  40. mesh_sandbox/views/error.py +3 -7
  41. mesh_sandbox/views/inbox.py +4 -7
  42. mesh_sandbox/views/lookup.py +6 -6
  43. mesh_sandbox/views/outbox.py +21 -26
  44. mesh_sandbox/views/tracking.py +0 -4
  45. {mesh_sandbox-1.0.10.dist-info → mesh_sandbox-1.0.12.dist-info}/METADATA +16 -41
  46. mesh_sandbox-1.0.12.dist-info/RECORD +76 -0
  47. mesh_sandbox-1.0.10.dist-info/RECORD +0 -76
  48. {mesh_sandbox-1.0.10.dist-info → mesh_sandbox-1.0.12.dist-info}/LICENSE +0 -0
  49. {mesh_sandbox-1.0.10.dist-info → mesh_sandbox-1.0.12.dist-info}/WHEEL +0 -0
mesh_sandbox/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "1.0.10"
1
+ __version__ = "1.0.12"
mesh_sandbox/api.py CHANGED
@@ -38,7 +38,6 @@ app = FastAPI(
38
38
 
39
39
  @app.on_event("startup")
40
40
  async def startup():
41
-
42
41
  config = get_env_config()
43
42
  # pylint: disable=logging-fstring-interpolation
44
43
  logger.info(f"startup auth_mode: {config.auth_mode} store_mode: {config.store_mode}")
@@ -4,7 +4,7 @@ import os
4
4
  from dataclasses import dataclass, field
5
5
  from functools import partial
6
6
  from hashlib import sha256
7
- from typing import Any, Callable, Final, Optional, TypeVar, Union
7
+ from typing import Any, Callable, Final, Optional, TypeVar
8
8
 
9
9
  from fastapi.encoders import jsonable_encoder
10
10
  from pydantic import BaseModel # pylint: disable=no-name-in-module
@@ -47,7 +47,6 @@ def strtobool(val: Any) -> Optional[bool]:
47
47
 
48
48
  @dataclass
49
49
  class EnvConfig:
50
-
51
50
  env: str = field(default="local")
52
51
  build_label: str = field(default="latest")
53
52
  auth_mode: str = field(default="no_auth")
@@ -72,7 +71,6 @@ T = TypeVar("T")
72
71
 
73
72
 
74
73
  def index_of(items: list[T], find: Callable[[T], bool]) -> int:
75
-
76
74
  for index, elem in enumerate(items):
77
75
  if find(elem):
78
76
  return index
@@ -6,7 +6,7 @@ from abc import ABC, abstractmethod
6
6
  from collections import defaultdict
7
7
  from functools import wraps
8
8
  from types import ModuleType
9
- from typing import Any, Callable, Literal, NamedTuple, Optional, TypeVar, cast
9
+ from typing import Any, Callable, ClassVar, Literal, NamedTuple, Optional, TypeVar, cast
10
10
 
11
11
  from fastapi import HTTPException, status
12
12
  from starlette.background import BackgroundTasks
@@ -19,24 +19,25 @@ from . import constants, generate_cipher_text
19
19
 
20
20
 
21
21
  class _SandboxPlugin(ABC):
22
-
23
- triggers: list[
24
- Literal[
25
- "before_accept_message",
26
- "after_accept_message",
27
- "accept_message_error",
28
- "before_save_message",
29
- "after_save_message",
30
- "save_message_error",
31
- "before_send_message",
32
- "after_send_message",
33
- "send_message_error",
34
- "before_acknowledge_message",
35
- "after_acknowledge_message",
36
- "acknowledge_message_error",
37
- "before_save_chunk",
38
- "after_save_chunk",
39
- "save_chunk_error",
22
+ triggers: ClassVar[
23
+ list[
24
+ Literal[
25
+ "before_accept_message",
26
+ "after_accept_message",
27
+ "accept_message_error",
28
+ "before_save_message",
29
+ "after_save_message",
30
+ "save_message_error",
31
+ "before_send_message",
32
+ "after_send_message",
33
+ "send_message_error",
34
+ "before_acknowledge_message",
35
+ "after_acknowledge_message",
36
+ "acknowledge_message_error",
37
+ "before_save_chunk",
38
+ "after_save_chunk",
39
+ "save_chunk_error",
40
+ ]
40
41
  ]
41
42
  ] = []
42
43
 
@@ -83,7 +84,6 @@ MESH_AUTH_SCHEME = "NHSMESH"
83
84
 
84
85
 
85
86
  def try_parse_authorisation_token(auth_token: str) -> Optional[AuthoriseHeaderParts]:
86
-
87
87
  if not auth_token:
88
88
  return None
89
89
 
@@ -126,7 +126,6 @@ class Messaging:
126
126
  self.event_name = event_name
127
127
 
128
128
  def __call__(self, func):
129
-
130
129
  if not inspect.iscoroutinefunction(func):
131
130
  raise ValueError(f"wrapped function is not awaitable: {func}")
132
131
 
@@ -155,7 +154,6 @@ class Messaging:
155
154
 
156
155
  class _IfNotReadonly:
157
156
  def __call__(self, func):
158
-
159
157
  if not inspect.iscoroutinefunction(func):
160
158
  raise ValueError(f"wrapped function is not awaitable: {func}")
161
159
 
@@ -170,18 +168,15 @@ class Messaging:
170
168
  return _async_inner
171
169
 
172
170
  def _find_plugins(self, package: ModuleType):
173
-
174
171
  for _, name, _ in pkgutil.iter_modules(package.__path__, package.__name__ + "."):
175
172
  module = importlib.import_module(name)
176
173
  for _, plugin_type in inspect.getmembers(module):
177
-
178
174
  if not inspect.isclass(plugin_type) or not plugin_type.__name__.endswith("Plugin"):
179
175
  continue
180
176
 
181
177
  self.register_plugin(plugin_type)
182
178
 
183
179
  def register_plugin(self, plugin_type: type):
184
-
185
180
  self.logger.info(f"potential plugin: {plugin_type.__name__} found")
186
181
  if not hasattr(plugin_type, "triggers"):
187
182
  self.logger.warning(f"plugin: {plugin_type.__name__} has no class attr triggers .. not loading")
@@ -224,7 +219,6 @@ class Messaging:
224
219
  return created
225
220
 
226
221
  async def on_event(self, event: str, event_args: dict[str, Any], exception: Optional[Exception] = None):
227
-
228
222
  instances = self._plugin_instances.get(event, [])
229
223
  if not instances:
230
224
  registered = self._plugin_registry.get(event, [])
@@ -245,7 +239,6 @@ class Messaging:
245
239
 
246
240
  @_TriggersEvent(event_name="send_message")
247
241
  async def send_message(self, message: Message, body: bytes, background_tasks: BackgroundTasks) -> Message:
248
-
249
242
  if message.total_chunks > 0:
250
243
  await self.save_chunk(message=message, chunk_number=1, chunk=body, background_tasks=background_tasks)
251
244
 
@@ -261,7 +254,6 @@ class Messaging:
261
254
  @_TriggersEvent(event_name="accept_message")
262
255
  @_IfNotReadonly()
263
256
  async def accept_message(self, message: Message, file_size: int, background_tasks: BackgroundTasks):
264
-
265
257
  if message.status != MessageStatus.ACCEPTED:
266
258
  message.events.insert(0, MessageEvent(status=MessageStatus.ACCEPTED))
267
259
 
@@ -284,7 +276,6 @@ class Messaging:
284
276
  async def add_message_event(
285
277
  self, message: Message, event: MessageEvent, background_tasks: BackgroundTasks
286
278
  ) -> Message:
287
-
288
279
  message.events.insert(0, event)
289
280
  await self.save_message(message=message, background_tasks=background_tasks)
290
281
 
@@ -342,7 +333,6 @@ class Messaging:
342
333
  return await self.get_inbox_messages(mailbox_id, _accepted_messages)
343
334
 
344
335
  async def _validate_auth_token(self, mailbox_id: str, authorization: str) -> Optional[Mailbox]:
345
-
346
336
  if self.config.auth_mode == "none":
347
337
  return await self.get_mailbox(mailbox_id, accessed=True)
348
338
 
@@ -358,7 +348,6 @@ class Messaging:
358
348
  raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=constants.ERROR_MAILBOX_TOKEN_MISMATCH)
359
349
 
360
350
  if self.config.auth_mode == "canned":
361
-
362
351
  if header_parts.nonce.upper() != "VALID":
363
352
  raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=constants.ERROR_INVALID_AUTH_TOKEN)
364
353
  return await self.get_mailbox(mailbox_id, accessed=True)
@@ -386,7 +375,6 @@ class Messaging:
386
375
  return mailbox
387
376
 
388
377
  async def authorise_mailbox(self, mailbox_id: str, authorization: str) -> Optional[Mailbox]:
389
-
390
378
  mailbox = await self._validate_auth_token(mailbox_id, authorization)
391
379
 
392
380
  if not mailbox:
@@ -10,7 +10,6 @@ from .constants import Headers
10
10
 
11
11
 
12
12
  def ensure_text(text: str, encoding="utf-8", errors="strict"):
13
-
14
13
  if isinstance(text, str):
15
14
  return text
16
15
 
mesh_sandbox/conftest.py CHANGED
@@ -15,9 +15,8 @@ from .dependencies import get_env_config, get_messaging, get_store
15
15
  from .tests.helpers import temp_env_vars
16
16
 
17
17
 
18
- @pytest.fixture(scope="function", autouse=True)
18
+ @pytest.fixture(autouse=True)
19
19
  def setup():
20
-
21
20
  get_store.cache_clear()
22
21
  get_env_config.cache_clear()
23
22
  get_messaging.cache_clear()
@@ -32,9 +31,8 @@ def setup():
32
31
  yield
33
32
 
34
33
 
35
- @pytest.fixture(scope="function", name="app")
34
+ @pytest.fixture(name="app")
36
35
  def test_app() -> TestClient:
37
-
38
36
  return TestClient(app)
39
37
 
40
38
 
@@ -58,7 +56,6 @@ class StoppableServer:
58
56
 
59
57
  @pytest.fixture(scope="session", name="base_uri")
60
58
  def create_server(unused_tcp_port_factory: Callable[[], int]):
61
-
62
59
  port = unused_tcp_port_factory()
63
60
 
64
61
  server = StoppableServer(port)
@@ -66,7 +63,6 @@ def create_server(unused_tcp_port_factory: Callable[[], int]):
66
63
  server_thread = Thread(target=server.run)
67
64
  server_thread.start()
68
65
  try:
69
-
70
66
  base_uri = f"http://localhost:{port}"
71
67
  timeout = time() + 1
72
68
  with httpx.Client(base_url=base_uri) as client:
@@ -18,7 +18,6 @@ _ACCEPTABLE_ACCEPTS = re.compile(r"^application/vnd\.mesh\.v(\d+)\+json$")
18
18
 
19
19
 
20
20
  def parse_accept_header(accept: Optional[str]) -> Optional[int]:
21
-
22
21
  if not accept:
23
22
  return 1
24
23
 
@@ -107,17 +106,17 @@ async def normalise_content_type(
107
106
  return (content_type or "").strip().lower()
108
107
 
109
108
 
110
- @lru_cache()
109
+ @lru_cache
111
110
  def get_env_config() -> EnvConfig:
112
111
  return EnvConfig()
113
112
 
114
113
 
115
- @lru_cache()
114
+ @lru_cache
116
115
  def get_logger() -> logging.Logger:
117
116
  return logging.getLogger("mesh-sandbox")
118
117
 
119
118
 
120
- @lru_cache()
119
+ @lru_cache
121
120
  def get_store() -> Store:
122
121
  logger = get_logger()
123
122
  config = get_env_config()
@@ -133,12 +132,12 @@ def get_store() -> Store:
133
132
  raise ValueError(f"unrecognised store mode {config.store_mode}")
134
133
 
135
134
 
136
- @lru_cache()
135
+ @lru_cache
137
136
  def get_messaging() -> Messaging:
138
137
  return Messaging(store=get_store())
139
138
 
140
139
 
141
- @lru_cache()
140
+ @lru_cache
142
141
  def get_fernet() -> FernetHelper:
143
142
  return FernetHelper()
144
143
 
@@ -22,7 +22,6 @@ class AdminHandler:
22
22
  self.messaging = messaging
23
23
 
24
24
  async def reset(self, mailbox_id: Optional[str] = None):
25
-
26
25
  if self.messaging.readonly:
27
26
  raise HTTPException(
28
27
  status_code=status.HTTP_405_METHOD_NOT_ALLOWED,
@@ -91,7 +90,6 @@ class AdminHandler:
91
90
  async def add_message_event(
92
91
  self, message_id: str, new_event: AddMessageEventRequest, background_tasks: BackgroundTasks
93
92
  ):
94
-
95
93
  message = await self.messaging.get_message(message_id)
96
94
  if not message:
97
95
  raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
@@ -6,7 +6,6 @@ from ..models.mailbox import Mailbox
6
6
 
7
7
 
8
8
  class HandshakeHandler:
9
-
10
9
  # pylint: disable=too-many-arguments
11
10
  async def handshake(
12
11
  self,
@@ -20,7 +19,6 @@ class HandshakeHandler:
20
19
  mex_osarchitecture: str,
21
20
  accepts_api_version: int = 1,
22
21
  ):
23
-
24
22
  if accepts_api_version < 2:
25
23
  return {"mailboxId": mailbox.mailbox_id}
26
24
 
@@ -42,7 +42,6 @@ class InboxHandler:
42
42
 
43
43
  @staticmethod
44
44
  def _get_status_headers(message: Message) -> dict[str, Optional[str]]:
45
-
46
45
  status_timestamp = (
47
46
  message.status_timestamp(MessageStatus.ACCEPTED, MessageStatus.ERROR) or datetime.utcnow()
48
47
  ).strftime("%Y%m%d%H%M%S")
@@ -50,7 +49,6 @@ class InboxHandler:
50
49
  error_event = message.error_event
51
50
 
52
51
  if message.message_type == MessageType.DATA and not error_event:
53
-
54
52
  return {
55
53
  Headers.Mex_StatusCode: "00",
56
54
  Headers.Mex_StatusEvent: "TRANSFER",
@@ -76,7 +74,6 @@ class InboxHandler:
76
74
 
77
75
  @staticmethod
78
76
  def _get_response_headers(message: Message, chunk_number: int):
79
-
80
77
  headers = {
81
78
  Headers.Mex_From: message.sender.mailbox_id,
82
79
  Headers.Mex_To: message.recipient.mailbox_id,
@@ -108,7 +105,6 @@ class InboxHandler:
108
105
  return {h: v for h, v in headers.items() if v}
109
106
 
110
107
  async def head_message(self, mailbox: Mailbox, message_id: str):
111
-
112
108
  message = await self.messaging.get_message(message_id)
113
109
 
114
110
  if not message:
@@ -211,7 +207,6 @@ class InboxHandler:
211
207
  async def acknowledge_message(
212
208
  self, background_tasks: BackgroundTasks, mailbox: Mailbox, message_id: str, accepts_api_version: int = 1
213
209
  ):
214
-
215
210
  message = await self.messaging.get_message(message_id)
216
211
  if not message:
217
212
  raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=constants.ERROR_MESSAGE_DOES_NOT_EXIST)
@@ -240,7 +235,6 @@ class InboxHandler:
240
235
  message_filter: Optional[Callable[[Message], bool]] = None,
241
236
  rich: bool = False,
242
237
  ) -> tuple[list[Message], Optional[dict]]:
243
-
244
238
  messages = (
245
239
  sorted(
246
240
  await self.messaging.get_inbox_messages(mailbox.mailbox_id),
@@ -270,8 +264,7 @@ class InboxHandler:
270
264
  return messages, last_key
271
265
 
272
266
  @staticmethod
273
- def _get_workflow_filter(workflow_filter: Optional[str]) -> Optional[Callable[[Message], bool]]:
274
-
267
+ def _get_workflow_filter(workflow_filter: Optional[str]) -> Optional[Callable[[Message], bool]]: # noqa: C901
275
268
  workflow_id_filter = (workflow_filter or "").strip()
276
269
  if not workflow_id_filter:
277
270
  return None
@@ -332,7 +325,6 @@ class InboxHandler:
332
325
  continue_from: Optional[str] = None,
333
326
  workflow_filter: Optional[str] = None,
334
327
  ) -> Response:
335
-
336
328
  last_key: Optional[dict] = None
337
329
 
338
330
  if continue_from:
@@ -379,7 +371,6 @@ class InboxHandler:
379
371
  continue_from: Optional[str],
380
372
  max_results: int = 100,
381
373
  ) -> JSONResponse:
382
-
383
374
  last_key: Optional[dict] = None
384
375
  if continue_from:
385
376
  last_key = self.fernet.decode_dict(continue_from)
@@ -10,7 +10,6 @@ class LookupHandler:
10
10
  self.messaging = messaging
11
11
 
12
12
  async def lookup_by_ods_code_and_workflow(self, ods_code: str, workflow_id: str, accepts_api_version: int = 1):
13
-
14
13
  if not ods_code or (ods_code and not ods_code.strip()):
15
14
  raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="ods code missing")
16
15
 
@@ -22,7 +21,6 @@ class LookupHandler:
22
21
  return endpoint_lookup_response(mailboxes, accepts_api_version)
23
22
 
24
23
  async def lookup_by_workflow_id(self, workflow_id: str, accepts_api_version: int = 1):
25
-
26
24
  if not workflow_id or (workflow_id and not workflow_id.strip()):
27
25
  raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="workflow id missing")
28
26
 
@@ -43,7 +43,7 @@ def get_chunk_range(chunk_range: str, request_chunk_no: int) -> tuple[Optional[s
43
43
  return "bad headers", 0, 0
44
44
 
45
45
  try:
46
- chunk_no, total_chunks = [int(val.strip()) for val in parts]
46
+ chunk_no, total_chunks = (int(val.strip()) for val in parts)
47
47
  except ValueError:
48
48
  return "bad header value - chunk values should be numeric", 0, 0
49
49
 
@@ -78,7 +78,6 @@ class OutboxHandler:
78
78
  content_type: str,
79
79
  accepts_api_version: int = 1,
80
80
  ): # pylint: disable=too-many-locals
81
-
82
81
  if not mex_headers.mex_to:
83
82
  raise HTTPException(status_code=http_status.HTTP_400_BAD_REQUEST, detail=constants.ERROR_MISSING_TO_ADDRESS)
84
83
 
@@ -146,11 +145,9 @@ class OutboxHandler:
146
145
  await self.messaging.send_message(message=message, body=body, background_tasks=background_tasks)
147
146
 
148
147
  self.logger.info(
149
- (
150
- f"created message: message_id={message.message_id} "
151
- f"from={message.sender.mailbox_id} to={message.recipient.mailbox_id} "
152
- f"workflow={message.workflow_id or ''}"
153
- )
148
+ f"created message: message_id={message.message_id} "
149
+ f"from={message.sender.mailbox_id} to={message.recipient.mailbox_id} "
150
+ f"workflow={message.workflow_id or ''}"
154
151
  )
155
152
 
156
153
  return send_message_response(message, accepts_api_version)
@@ -166,7 +163,6 @@ class OutboxHandler:
166
163
  content_encoding: str,
167
164
  accepts_api_version: int = 1,
168
165
  ):
169
-
170
166
  chunk_range = (mex_chunk_range or "").strip()
171
167
  if not chunk_range:
172
168
  raise MessagingException(
@@ -17,7 +17,6 @@ class TrackingHandler:
17
17
  self.messaging = messaging
18
18
 
19
19
  async def tracking_by_message_id(self, sender_mailbox: Mailbox, message_id: str, accepts_api_version: int = 1):
20
-
21
20
  message: Optional[Message] = await self.messaging.get_message(message_id)
22
21
 
23
22
  if not message:
@@ -35,7 +34,6 @@ class TrackingHandler:
35
34
  return JSONResponse(content=exclude_none_json_encoder(model), media_type=MESH_MEDIA_TYPES[accepts_api_version])
36
35
 
37
36
  async def tracking_by_local_id(self, sender_mailbox: Mailbox, local_id: str):
38
-
39
37
  messages: list[Message] = await self.messaging.get_by_local_id(sender_mailbox.mailbox_id, local_id)
40
38
 
41
39
  if len(messages) == 0:
@@ -5,7 +5,6 @@ from typing import Optional
5
5
 
6
6
  @dataclass
7
7
  class Mailbox:
8
-
9
8
  mailbox_id: str
10
9
  mailbox_name: str
11
10
  billing_entity: Optional[str] = field(default=None)
@@ -35,5 +34,4 @@ class Mailbox:
35
34
  self._inbox_count = inbox_count
36
35
 
37
36
  def __post_init__(self):
38
-
39
37
  self.mailbox_id = self.mailbox_id.upper()
@@ -1,12 +1,12 @@
1
+ from collections.abc import Generator
1
2
  from dataclasses import dataclass, field
2
3
  from datetime import datetime
3
- from typing import Callable, Final, Generator, Optional
4
+ from typing import Callable, Final, Optional
4
5
 
5
6
  from dateutil.relativedelta import relativedelta
6
7
 
7
8
 
8
9
  class MessageStatus:
9
-
10
10
  UPLOADING: Final[str] = "uploading" # still uploading chunks
11
11
  ACCEPTED: Final[str] = "accepted" # finished uploading
12
12
  ACKNOWLEDGED: Final[str] = "acknowledged" # client has acknowledged the message
@@ -17,7 +17,6 @@ class MessageStatus:
17
17
 
18
18
 
19
19
  class MessageType:
20
-
21
20
  REPORT: Final[str] = "REPORT"
22
21
  DATA: Final[str] = "DATA"
23
22
 
@@ -31,7 +30,6 @@ class MessageDeliveryStatus:
31
30
 
32
31
  @dataclass
33
32
  class MessageMetadata:
34
-
35
33
  subject: Optional[str] = field(default=None)
36
34
  content_type: Optional[str] = field(default=None)
37
35
  content_encoding: Optional[str] = field(default=None)
@@ -133,6 +131,5 @@ class Message: # pylint: disable=too-many-public-methods,too-many-instance-attr
133
131
  yield event
134
132
 
135
133
  def __post_init__(self):
136
-
137
134
  self.message_id = self.message_id.upper()
138
135
  self.events = self.events or [MessageEvent(status=MessageStatus.ACCEPTED, timestamp=self.created_timestamp)]
@@ -3,13 +3,11 @@ from dataclasses import dataclass, field
3
3
 
4
4
  @dataclass
5
5
  class Workflow:
6
-
7
6
  workflow_id: str
8
7
  senders: list[str] = field(default_factory=list)
9
8
  receivers: list[str] = field(default_factory=list)
10
9
 
11
10
  def __post_init__(self):
12
-
13
11
  self.workflow_id = (self.workflow_id or "").strip()
14
12
 
15
13
  self.senders = [mailbox for mailbox in ((mb or "").strip().upper() for mb in self.senders) if mailbox]
@@ -35,8 +35,8 @@ router = APIRouter(
35
35
  responses={
36
36
  status.HTTP_200_OK: {
37
37
  "content": {
38
- MESH_MEDIA_TYPES[2]: {"schema": InboxV2.schema()},
39
- MESH_MEDIA_TYPES[1]: {"schema": InboxV1.schema()},
38
+ MESH_MEDIA_TYPES[2]: {"schema": InboxV2.model_json_schema()},
39
+ MESH_MEDIA_TYPES[1]: {"schema": InboxV1.model_json_schema()},
40
40
  }
41
41
  },
42
42
  status.HTTP_403_FORBIDDEN: {"description": "Authentication failed", "content": None},
@@ -149,7 +149,7 @@ async def head_message(
149
149
  status.HTTP_200_OK: {
150
150
  "content": {
151
151
  MESH_MEDIA_TYPES[2]: {
152
- "schema": RichInboxView.schema(),
152
+ "schema": RichInboxView.model_json_schema(),
153
153
  }
154
154
  },
155
155
  }
@@ -205,7 +205,7 @@ async def rich_inbox(
205
205
  },
206
206
  status.HTTP_206_PARTIAL_CONTENT: {
207
207
  "description": (
208
- "Partial Content Indicates that chunk has been downloaded "
208
+ "Partial Content - Indicates that chunk has been downloaded "
209
209
  "successfully and that there are further chunks."
210
210
  ),
211
211
  "content": {"application/octet-stream": None},
@@ -244,7 +244,7 @@ async def retrieve_message(
244
244
  },
245
245
  status.HTTP_206_PARTIAL_CONTENT: {
246
246
  "description": (
247
- "Partial Content Indicates that chunk has been downloaded "
247
+ "Partial Content - Indicates that chunk has been downloaded "
248
248
  "successfully and that there are further chunks."
249
249
  ),
250
250
  "content": {"application/octet-stream": None},
@@ -24,10 +24,10 @@ router = APIRouter(
24
24
  200: {
25
25
  "content": {
26
26
  MESH_MEDIA_TYPES[2]: {
27
- "schema": InboxCountV2.schema(),
27
+ "schema": InboxCountV2.model_json_schema(),
28
28
  },
29
29
  MESH_MEDIA_TYPES[1]: {
30
- "schema": InboxCountV1.schema(),
30
+ "schema": InboxCountV1.model_json_schema(),
31
31
  },
32
32
  }
33
33
  }
@@ -19,10 +19,10 @@ router = APIRouter(
19
19
  status.HTTP_200_OK: {
20
20
  "content": {
21
21
  MESH_MEDIA_TYPES[2]: {
22
- "schema": MailboxLookupV2.schema(),
22
+ "schema": MailboxLookupV2.model_json_schema(),
23
23
  },
24
24
  MESH_MEDIA_TYPES[1]: {
25
- "schema": EndpointLookupV1.schema(),
25
+ "schema": EndpointLookupV1.model_json_schema(),
26
26
  },
27
27
  }
28
28
  }
@@ -35,7 +35,6 @@ async def lookup_by_ods_code_and_workflow_id(
35
35
  accepts_api_version: int = Depends(get_accepts_api_version),
36
36
  handler: LookupHandler = Depends(LookupHandler),
37
37
  ):
38
-
39
38
  return await handler.lookup_by_ods_code_and_workflow(ods_code, workflow_id, accepts_api_version)
40
39
 
41
40
 
@@ -46,7 +45,7 @@ async def lookup_by_ods_code_and_workflow_id(
46
45
  status.HTTP_200_OK: {
47
46
  "content": {
48
47
  MESH_MEDIA_TYPES[2]: {
49
- "schema": MailboxLookupV2.schema(),
48
+ "schema": MailboxLookupV2.model_json_schema(),
50
49
  }
51
50
  }
52
51
  }
@@ -40,10 +40,10 @@ router = APIRouter(
40
40
  status.HTTP_202_ACCEPTED: {
41
41
  "content": {
42
42
  MESH_MEDIA_TYPES[2]: {
43
- "schema": SendMessageV2.schema(),
43
+ "schema": SendMessageV2.model_json_schema(),
44
44
  },
45
45
  MESH_MEDIA_TYPES[1]: {
46
- "schema": SendMessageV1.schema(),
46
+ "schema": SendMessageV1.model_json_schema(),
47
47
  },
48
48
  }
49
49
  },
@@ -111,7 +111,7 @@ async def send_chunk(
111
111
  200: {
112
112
  "content": {
113
113
  MESH_MEDIA_TYPES[2]: {
114
- "schema": RichOutboxView.schema(),
114
+ "schema": RichOutboxView.model_json_schema(),
115
115
  }
116
116
  }
117
117
  }
@@ -27,10 +27,10 @@ router = APIRouter(
27
27
  200: {
28
28
  "content": {
29
29
  MESH_MEDIA_TYPES[2]: {
30
- "schema": TrackingV2.schema(),
30
+ "schema": TrackingV2.model_json_schema(),
31
31
  },
32
32
  MESH_MEDIA_TYPES[1]: {
33
- "schema": TrackingV1.schema(),
33
+ "schema": TrackingV1.model_json_schema(),
34
34
  },
35
35
  }
36
36
  }
@@ -8,7 +8,6 @@ from ..models.message import Message
8
8
 
9
9
 
10
10
  class Store(ABC):
11
-
12
11
  readonly = True
13
12
 
14
13
  def __init__(self, config: EnvConfig, logger: logging.Logger):