From 9a30e3073535cfb486710989a55be9bd473e1e50 Mon Sep 17 00:00:00 2001 From: fderuiter <127706008+fderuiter@users.noreply.github.com> Date: Sun, 5 Apr 2026 16:42:43 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20Refactor:=20Endpoints?= =?UTF-8?q?=20-=20Explicit=20Study=20Key=20Strategy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Design Change: Replaced implicit boolean flags (`_pop_study_filter`) and exception attributes (`_missing_study_exception`) with explicit Strategy instances (`STUDY_KEY_STRATEGY = PopStudyKeyStrategy()`) on endpoint classes. Removed backward-compatible flag evaluation in `ParamMixin`. DRY Gains: Removed redundant attribute definitions in endpoints and simplified logic in `ParamMixin`'s property resolution. Solidity: Enforces Explicit > Implicit design (SOLID) by composing endpoints with concrete Strategy objects rather than relying on abstract mixins to evaluate hidden state flags. Breaking Changes: Removed `_pop_study_filter` and `_missing_study_exception` from `EndpointProtocol` and `ParamMixin`. Subclasses must now explicitly define `STUDY_KEY_STRATEGY`. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- src/imednet/core/endpoint/mixins/params.py | 8 +------- src/imednet/core/endpoint/protocols.py | 2 -- src/imednet/endpoints/codings.py | 4 ++-- src/imednet/endpoints/forms.py | 4 ++-- src/imednet/endpoints/intervals.py | 4 ++-- src/imednet/endpoints/records.py | 1 - src/imednet/endpoints/sites.py | 4 ++-- src/imednet/endpoints/users.py | 4 ++-- src/imednet/endpoints/variables.py | 4 ++-- 9 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/imednet/core/endpoint/mixins/params.py b/src/imednet/core/endpoint/mixins/params.py index cacad4f5..b5565010 100644 --- a/src/imednet/core/endpoint/mixins/params.py +++ b/src/imednet/core/endpoint/mixins/params.py @@ -6,7 +6,6 @@ DefaultParamProcessor, KeepStudyKeyStrategy, OptionalStudyKeyStrategy, - PopStudyKeyStrategy, StudyKeyStrategy, ) from imednet.core.endpoint.structs import ParamState @@ -20,8 +19,6 @@ class ParamMixin: """Mixin for handling endpoint parameters and filters.""" requires_study_key: bool = True - _pop_study_filter: bool = False - _missing_study_exception: type[Exception] = ValueError PARAM_PROCESSOR: Optional[ParamProcessor] = None PARAM_PROCESSOR_CLS: type[ParamProcessor] = DefaultParamProcessor @@ -38,11 +35,8 @@ def study_key_strategy(self) -> StudyKeyStrategy: if self.STUDY_KEY_STRATEGY: return self.STUDY_KEY_STRATEGY - # Backward compatibility logic if self.requires_study_key: - if self._pop_study_filter: - return PopStudyKeyStrategy(exception_cls=self._missing_study_exception) - return KeepStudyKeyStrategy(exception_cls=self._missing_study_exception) + return KeepStudyKeyStrategy() return OptionalStudyKeyStrategy() @property diff --git a/src/imednet/core/endpoint/protocols.py b/src/imednet/core/endpoint/protocols.py index fa92aebe..69f41790 100644 --- a/src/imednet/core/endpoint/protocols.py +++ b/src/imednet/core/endpoint/protocols.py @@ -17,8 +17,6 @@ class EndpointProtocol(Protocol): _enable_cache: bool requires_study_key: bool PAGE_SIZE: int - _pop_study_filter: bool - _missing_study_exception: type[Exception] def _auto_filter(self, filters: Dict[str, Any]) -> Dict[str, Any]: """Apply automatic filters (e.g., default study key).""" diff --git a/src/imednet/endpoints/codings.py b/src/imednet/endpoints/codings.py index e76711e7..bf71ae29 100644 --- a/src/imednet/endpoints/codings.py +++ b/src/imednet/endpoints/codings.py @@ -3,6 +3,7 @@ from imednet.core.endpoint.base import GenericEndpoint from imednet.core.endpoint.edc_mixin import EdcEndpointMixin from imednet.core.endpoint.mixins import FilterGetEndpointMixin, ListEndpointMixin +from imednet.core.endpoint.strategies import PopStudyKeyStrategy from imednet.models.codings import Coding @@ -21,5 +22,4 @@ class CodingsEndpoint( PATH = "codings" MODEL = Coding _id_param = "codingId" - _pop_study_filter = True - _missing_study_exception = KeyError + STUDY_KEY_STRATEGY = PopStudyKeyStrategy(exception_cls=KeyError) diff --git a/src/imednet/endpoints/forms.py b/src/imednet/endpoints/forms.py index ebcbca41..320dc41e 100644 --- a/src/imednet/endpoints/forms.py +++ b/src/imednet/endpoints/forms.py @@ -3,6 +3,7 @@ from imednet.core.endpoint.base import GenericEndpoint from imednet.core.endpoint.edc_mixin import EdcEndpointMixin from imednet.core.endpoint.mixins import FilterGetEndpointMixin, ListEndpointMixin +from imednet.core.endpoint.strategies import PopStudyKeyStrategy from imednet.models.forms import Form @@ -22,6 +23,5 @@ class FormsEndpoint( MODEL = Form _id_param = "formId" _enable_cache = True - _pop_study_filter = True - _missing_study_exception = KeyError + STUDY_KEY_STRATEGY = PopStudyKeyStrategy(exception_cls=KeyError) PAGE_SIZE = 500 diff --git a/src/imednet/endpoints/intervals.py b/src/imednet/endpoints/intervals.py index 0e6525e7..fb173491 100644 --- a/src/imednet/endpoints/intervals.py +++ b/src/imednet/endpoints/intervals.py @@ -3,6 +3,7 @@ from imednet.core.endpoint.base import GenericEndpoint from imednet.core.endpoint.edc_mixin import EdcEndpointMixin from imednet.core.endpoint.mixins import FilterGetEndpointMixin, ListEndpointMixin +from imednet.core.endpoint.strategies import PopStudyKeyStrategy from imednet.models.intervals import Interval @@ -22,6 +23,5 @@ class IntervalsEndpoint( MODEL = Interval _id_param = "intervalId" _enable_cache = True - _pop_study_filter = True - _missing_study_exception = KeyError + STUDY_KEY_STRATEGY = PopStudyKeyStrategy(exception_cls=KeyError) PAGE_SIZE = 500 diff --git a/src/imednet/endpoints/records.py b/src/imednet/endpoints/records.py index 2820c857..95bc73ab 100644 --- a/src/imednet/endpoints/records.py +++ b/src/imednet/endpoints/records.py @@ -27,7 +27,6 @@ class RecordsEndpoint( PATH = "records" MODEL = Record _id_param = "recordId" - _pop_study_filter = False PARAM_PROCESSOR = MappingParamProcessor({"record_data_filter": "recordDataFilter"}) def create( diff --git a/src/imednet/endpoints/sites.py b/src/imednet/endpoints/sites.py index d128aad4..cd021eba 100644 --- a/src/imednet/endpoints/sites.py +++ b/src/imednet/endpoints/sites.py @@ -3,6 +3,7 @@ from imednet.core.endpoint.base import GenericEndpoint from imednet.core.endpoint.edc_mixin import EdcEndpointMixin from imednet.core.endpoint.mixins import FilterGetEndpointMixin, ListEndpointMixin +from imednet.core.endpoint.strategies import PopStudyKeyStrategy from imednet.models.sites import Site @@ -21,5 +22,4 @@ class SitesEndpoint( PATH = "sites" MODEL = Site _id_param = "siteId" - _pop_study_filter = True - _missing_study_exception = KeyError + STUDY_KEY_STRATEGY = PopStudyKeyStrategy(exception_cls=KeyError) diff --git a/src/imednet/endpoints/users.py b/src/imednet/endpoints/users.py index a073ed2d..5f841b12 100644 --- a/src/imednet/endpoints/users.py +++ b/src/imednet/endpoints/users.py @@ -3,7 +3,7 @@ from imednet.core.endpoint.base import GenericEndpoint from imednet.core.endpoint.edc_mixin import EdcEndpointMixin from imednet.core.endpoint.mixins import FilterGetEndpointMixin, ListEndpointMixin -from imednet.core.endpoint.strategies import MappingParamProcessor +from imednet.core.endpoint.strategies import MappingParamProcessor, PopStudyKeyStrategy from imednet.models.users import User @@ -22,7 +22,7 @@ class UsersEndpoint( PATH = "users" MODEL = User _id_param = "userId" - _pop_study_filter = True + STUDY_KEY_STRATEGY = PopStudyKeyStrategy() PARAM_PROCESSOR = MappingParamProcessor( mapping={"include_inactive": "includeInactive"}, defaults={"include_inactive": False}, diff --git a/src/imednet/endpoints/variables.py b/src/imednet/endpoints/variables.py index adb6a03e..ca95ec3c 100644 --- a/src/imednet/endpoints/variables.py +++ b/src/imednet/endpoints/variables.py @@ -3,6 +3,7 @@ from imednet.core.endpoint.base import GenericEndpoint from imednet.core.endpoint.edc_mixin import EdcEndpointMixin from imednet.core.endpoint.mixins import FilterGetEndpointMixin, ListEndpointMixin +from imednet.core.endpoint.strategies import PopStudyKeyStrategy from imednet.models.variables import Variable @@ -22,6 +23,5 @@ class VariablesEndpoint( MODEL = Variable _id_param = "variableId" _enable_cache = True - _pop_study_filter = True - _missing_study_exception = KeyError + STUDY_KEY_STRATEGY = PopStudyKeyStrategy(exception_cls=KeyError) PAGE_SIZE = 500