Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
28 changes: 28 additions & 0 deletions src/imednet/core/endpoint/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,31 @@ def _validate_study_key(self, study_key: Optional[str]) -> None:
from imednet.errors import ClientError

raise ClientError("Study key must be provided or set in the context")

def _get_endpoint_path(self, study_key: Optional[str], *extra_segments: Any) -> str:
"""
Build the endpoint path with optional study key and extra segments.
"""
self._validate_study_key(study_key)
segments = []
if self.requires_study_key:
segments.append(study_key)
if self.PATH:
segments.append(self.PATH)
segments.extend(extra_segments)
return self._build_path(*segments)

def _raise_not_found(self, study_key: Optional[str], item_id: Any = None) -> None:
"""
Raise a standardized NotFoundError.
"""
from imednet.errors import NotFoundError

msg_parts = [f"{self.MODEL.__name__}"]
if item_id is not None:
msg_parts.append(str(item_id))
msg_parts.append("not found")
if self.requires_study_key and study_key:
msg_parts.append(f"in study {study_key}")

raise NotFoundError(" ".join(msg_parts))
26 changes: 3 additions & 23 deletions src/imednet/core/endpoint/mixins/get.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from __future__ import annotations

from typing import Any, Iterable, List, Optional, cast
from typing import Any, List, Optional, cast

from imednet.core.endpoint.abc import EndpointABC
from imednet.core.endpoint.operations.filter_get import FilterGetOperation
from imednet.core.endpoint.operations.get import PathGetOperation
from imednet.core.paginator import AsyncPaginator, Paginator
from imednet.core.protocols import AsyncRequestorProtocol, RequestorProtocol
from imednet.errors import NotFoundError

from ..protocols import ListEndpointProtocol
from .parsing import ParsingMixin, T
Expand All @@ -22,12 +21,7 @@ class FilterGetEndpointMixin(EndpointABC[T]):

def _validate_get_result(self, items: List[T], study_key: Optional[str], item_id: Any) -> T:
if not items:
self._validate_study_key(study_key)
if self.requires_study_key:
raise NotFoundError(
f"{self.MODEL.__name__} {item_id} not found in study {study_key}"
)
raise NotFoundError(f"{self.MODEL.__name__} {item_id} not found")
self._raise_not_found(study_key, item_id)
return items[0]

def _create_filter_operation(
Expand Down Expand Up @@ -102,20 +96,6 @@ class PathGetEndpointMixin(ParsingMixin[T], EndpointABC[T]):

# PATH is inherited from EndpointABC as abstract

def _get_path_for_id(self, study_key: Optional[str], item_id: Any) -> str:
segments: Iterable[Any]
self._validate_study_key(study_key)
if self.requires_study_key:
segments = (study_key, self.PATH, item_id)
else:
segments = (self.PATH, item_id) if self.PATH else (item_id,)

# No cast needed as we inherit EndpointABC which defines _build_path
return self._build_path(*segments)

def _raise_not_found(self, study_key: Optional[str], item_id: Any) -> None:
raise NotFoundError(f"{self.MODEL.__name__} not found")

def _create_path_operation(self, study_key: Optional[str], item_id: Any) -> PathGetOperation[T]:
"""
Create a PathGetOperation instance.
Expand All @@ -127,7 +107,7 @@ def _create_path_operation(self, study_key: Optional[str], item_id: Any) -> Path
Returns:
An instantiated PathGetOperation.
"""
path = self._get_path_for_id(study_key, item_id)
path = self._get_endpoint_path(study_key, item_id)
return PathGetOperation[T](
path=path,
parse_func=self._parse_item,
Expand Down
13 changes: 2 additions & 11 deletions src/imednet/core/endpoint/mixins/list.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from typing import Any, Callable, Dict, Iterable, List, Optional, cast
from typing import Any, Callable, Dict, List, Optional, cast

from imednet.constants import DEFAULT_PAGE_SIZE
from imednet.core.endpoint.abc import EndpointABC
Expand All @@ -22,15 +22,6 @@ class ListEndpointMixin(ParamMixin, CacheMixin[T], ParsingMixin[T], EndpointABC[
PAGINATOR_CLS: type[Paginator] = Paginator
ASYNC_PAGINATOR_CLS: type[AsyncPaginator] = AsyncPaginator

def _get_path(self, study: Optional[str]) -> str:
segments: Iterable[Any]
self._validate_study_key(study)
if self.requires_study_key:
segments = (study, self.PATH)
else:
segments = (self.PATH,) if self.PATH else ()
return self._build_path(*segments)

def _resolve_parse_func(self) -> Callable[[Any], T]:
"""
Resolve the parsing function to use for this endpoint.
Expand Down Expand Up @@ -90,7 +81,7 @@ def _prepare_list_request(
cached_result=cast(List[T], cached_result),
)

path = self._get_path(study)
path = self._get_endpoint_path(study)
return ListRequestState(
path=path,
params=params,
Expand Down
8 changes: 8 additions & 0 deletions src/imednet/core/endpoint/protocols.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ def _validate_study_key(self, study_key: Optional[str]) -> None:
"""Validate that a study key is provided if required."""
...

def _get_endpoint_path(self, study_key: Optional[str], *extra_segments: Any) -> str:
"""Build the API path with optional study key and extra segments."""
...

def _raise_not_found(self, study_key: Optional[str], item_id: Any = None) -> None:
"""Raise a standardized NotFoundError."""
...


@runtime_checkable
class ListEndpointProtocol(Protocol[T]):
Expand Down
6 changes: 0 additions & 6 deletions src/imednet/endpoints/jobs.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
"""Endpoint for checking job status in a study."""

from typing import Any, Optional

from imednet.core.endpoint.base import GenericEndpoint
from imednet.core.endpoint.edc_mixin import EdcEndpointMixin
from imednet.core.endpoint.mixins import ListEndpointMixin, PathGetEndpointMixin
from imednet.core.paginator import AsyncJsonListPaginator, JsonListPaginator
from imednet.errors import NotFoundError
from imednet.models.jobs import JobStatus


Expand All @@ -27,6 +24,3 @@ class JobsEndpoint(
MODEL = JobStatus
PAGINATOR_CLS = JsonListPaginator
ASYNC_PAGINATOR_CLS = AsyncJsonListPaginator

def _raise_not_found(self, study_key: Optional[str], item_id: Any) -> None:
raise NotFoundError(f"Job {item_id} not found in study {study_key}")
4 changes: 2 additions & 2 deletions src/imednet/endpoints/records.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def create(
Raises:
ClientError: If email_notify contains invalid characters
"""
path = self._build_path(study_key, self.PATH)
path = self._get_endpoint_path(study_key)
operation = RecordCreateOperation[Job](
path=path,
records_data=records_data,
Expand Down Expand Up @@ -90,7 +90,7 @@ async def async_create(
Raises:
ClientError: If email_notify contains invalid characters
"""
path = self._build_path(study_key, self.PATH)
path = self._get_endpoint_path(study_key)
operation = RecordCreateOperation[Job](
path=path,
records_data=records_data,
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_core_endpoint_mixins_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class DummyPathGetEndpointNoStudy(DummyPathGetEndpoint):
def test_path_get_endpoint_no_study_key_no_path():
ep = DummyPathGetEndpointNoStudy()
# It should generate path with just item_id
assert ep._get_path_for_id(None, 123) == "/123"
assert ep._get_endpoint_path(None, 123) == "/123"


def test_path_get_endpoint_raise_not_found():
Expand Down
Loading