diff --git a/openfeature/api.py b/openfeature/api.py index 6687bb89..92a7443e 100644 --- a/openfeature/api.py +++ b/openfeature/api.py @@ -4,11 +4,11 @@ from openfeature.evaluation_context import EvaluationContext from openfeature.exception import GeneralError from openfeature.hook import Hook +from openfeature.provider import FeatureProvider from openfeature.provider.metadata import Metadata from openfeature.provider.no_op_provider import NoOpProvider -from openfeature.provider.provider import AbstractProvider -_provider: AbstractProvider = NoOpProvider() +_provider: FeatureProvider = NoOpProvider() _evaluation_context = EvaluationContext() @@ -21,7 +21,7 @@ def get_client( return OpenFeatureClient(name=name, version=version, provider=_provider) -def set_provider(provider: AbstractProvider) -> None: +def set_provider(provider: FeatureProvider) -> None: global _provider if provider is None: raise GeneralError(error_message="No provider") @@ -31,7 +31,7 @@ def set_provider(provider: AbstractProvider) -> None: provider.initialize(_evaluation_context) -def get_provider() -> typing.Optional[AbstractProvider]: +def get_provider() -> typing.Optional[FeatureProvider]: global _provider return _provider diff --git a/openfeature/client.py b/openfeature/client.py index 4c8d8abe..c76402fb 100644 --- a/openfeature/client.py +++ b/openfeature/client.py @@ -24,8 +24,8 @@ before_hooks, error_hooks, ) +from openfeature.provider import FeatureProvider from openfeature.provider.no_op_provider import NoOpProvider -from openfeature.provider.provider import AbstractProvider logger = logging.getLogger("openfeature") @@ -69,7 +69,7 @@ def __init__( self, name: typing.Optional[str], version: typing.Optional[str], - provider: AbstractProvider, + provider: FeatureProvider, context: typing.Optional[EvaluationContext] = None, hooks: typing.Optional[typing.List[Hook]] = None, ) -> None: diff --git a/openfeature/provider/__init__.py b/openfeature/provider/__init__.py index e69de29b..d5ddacff 100644 --- a/openfeature/provider/__init__.py +++ b/openfeature/provider/__init__.py @@ -0,0 +1,61 @@ +import typing + +from openfeature.evaluation_context import EvaluationContext +from openfeature.flag_evaluation import FlagResolutionDetails +from openfeature.hook import Hook + +from .metadata import Metadata + + +class FeatureProvider(typing.Protocol): # pragma: no cover + def initialize(self, evaluation_context: EvaluationContext) -> None: + ... + + def shutdown(self) -> None: + ... + + def get_metadata(self) -> Metadata: + ... + + def get_provider_hooks(self) -> typing.List[Hook]: + ... + + def resolve_boolean_details( + self, + flag_key: str, + default_value: bool, + evaluation_context: typing.Optional[EvaluationContext] = None, + ) -> FlagResolutionDetails[bool]: + ... + + def resolve_string_details( + self, + flag_key: str, + default_value: str, + evaluation_context: typing.Optional[EvaluationContext] = None, + ) -> FlagResolutionDetails[str]: + ... + + def resolve_integer_details( + self, + flag_key: str, + default_value: int, + evaluation_context: typing.Optional[EvaluationContext] = None, + ) -> FlagResolutionDetails[int]: + ... + + def resolve_float_details( + self, + flag_key: str, + default_value: float, + evaluation_context: typing.Optional[EvaluationContext] = None, + ) -> FlagResolutionDetails[float]: + ... + + def resolve_object_details( + self, + flag_key: str, + default_value: typing.Union[dict, list], + evaluation_context: typing.Optional[EvaluationContext] = None, + ) -> FlagResolutionDetails[typing.Union[dict, list]]: + ... diff --git a/openfeature/provider/provider.py b/openfeature/provider/provider.py index 625b7f6b..7b400316 100644 --- a/openfeature/provider/provider.py +++ b/openfeature/provider/provider.py @@ -4,10 +4,11 @@ from openfeature.evaluation_context import EvaluationContext from openfeature.flag_evaluation import FlagResolutionDetails from openfeature.hook import Hook +from openfeature.provider import FeatureProvider from openfeature.provider.metadata import Metadata -class AbstractProvider: +class AbstractProvider(FeatureProvider): def initialize(self, evaluation_context: EvaluationContext) -> None: pass diff --git a/tests/test_api.py b/tests/test_api.py index 040db910..cb3cfecf 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -19,7 +19,7 @@ from openfeature.hook import Hook from openfeature.provider.metadata import Metadata from openfeature.provider.no_op_provider import NoOpProvider -from openfeature.provider.provider import AbstractProvider +from openfeature.provider.provider import FeatureProvider def test_should_not_raise_exception_with_noop_client(): @@ -61,7 +61,7 @@ def test_should_try_set_provider_and_fail_if_none_provided(): def test_should_invoke_provider_initialize_function_on_newly_registered_provider(): # Given evaluation_context = EvaluationContext("targeting_key", {"attr1": "val1"}) - provider = MagicMock(spec=AbstractProvider) + provider = MagicMock(spec=FeatureProvider) # When set_evaluation_context(evaluation_context) @@ -73,8 +73,8 @@ def test_should_invoke_provider_initialize_function_on_newly_registered_provider def test_should_invoke_provider_shutdown_function_once_provider_is_no_longer_in_use(): # Given - provider_1 = MagicMock(spec=AbstractProvider) - provider_2 = MagicMock(spec=AbstractProvider) + provider_1 = MagicMock(spec=FeatureProvider) + provider_2 = MagicMock(spec=FeatureProvider) # When set_provider(provider_1) @@ -148,7 +148,7 @@ def test_should_add_hooks_to_api_hooks(): def test_should_call_provider_shutdown_on_api_shutdown(): # Given - provider = MagicMock(spec=AbstractProvider) + provider = MagicMock(spec=FeatureProvider) set_provider(provider) # When