From 255617a2a1a9f762948d0464b36e39c09c5bf3c1 Mon Sep 17 00:00:00 2001 From: petruki <31597636+petruki@users.noreply.github.com> Date: Sat, 21 Feb 2026 12:26:13 -0800 Subject: [PATCH] feat: added remote required request flag --- README.md | 4 ++-- switcher_client/switcher.py | 6 ++++-- switcher_client/switcher_data.py | 9 ++++++++ tests/test_switcher_remote.py | 35 ++++++++++++++++++++++++++++++-- 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0617123..3eb5cab 100644 --- a/README.md +++ b/README.md @@ -251,9 +251,9 @@ The following features are currently in development: switcher.throttle(1000).is_on('FEATURE01') ``` -#### Hybrid Mode (Coming Soon) +#### Hybrid Mode ```python -# 🚧 TODO: Force remote resolution for specific features +# Force remote resolution for specific features switcher.remote().is_on('FEATURE01') ``` diff --git a/switcher_client/switcher.py b/switcher_client/switcher.py index 237c96a..7aa0c63 100644 --- a/switcher_client/switcher.py +++ b/switcher_client/switcher.py @@ -28,7 +28,9 @@ def _init_worker(self, context: Context): def prepare(self, key: Optional[str] = None): """ Checks API credentials and connectivity """ self._validate_args(key) - RemoteAuth.auth() + + if not self._context.options.local or self._force_remote: + RemoteAuth.auth() def is_on(self, key: Optional[str] = None) -> bool: """ Execute criteria """ @@ -64,7 +66,7 @@ def schedule_background_refresh(self): def _submit(self) -> ResultDetail: """ Submit criteria for execution (local or remote) """ # verify if query from snapshot - if (self._context.options.local): + if (self._context.options.local and not self._force_remote): return self._execute_local_criteria() try: diff --git a/switcher_client/switcher_data.py b/switcher_client/switcher_data.py index 7163c39..0fe9fe2 100644 --- a/switcher_client/switcher_data.py +++ b/switcher_client/switcher_data.py @@ -14,6 +14,7 @@ def __init__(self, context: Context,key: Optional[str] = None): self._throttle_period = 0 self._next_refresh_time = 0 # timestamp self._default_result = None + self._force_remote = False def check(self, strategy_type: str, input: str)-> Self: """ Adds a strategy for validation """ @@ -55,6 +56,14 @@ def throttle(self, period: int) -> Self: return self + def remote(self, force_remote: bool = True) -> Self: + """ Force the use of the remote API when local is enabled """ + if not self._context.options.local: + raise ValueError("Local mode is not enabled") + + self._force_remote = force_remote + return self + def default_result(self, result: bool) -> Self: """ Sets the default result for the switcher """ self._default_result = result diff --git a/tests/test_switcher_remote.py b/tests/test_switcher_remote.py index 0822c08..eaed494 100644 --- a/tests/test_switcher_remote.py +++ b/tests/test_switcher_remote.py @@ -7,6 +7,7 @@ from switcher_client.errors import RemoteAuthError from switcher_client import Client from switcher_client.lib.globals.global_auth import GlobalAuth +from switcher_client.lib.globals.global_context import ContextOptions async_error = None @@ -104,6 +105,35 @@ def test_remote_renew_token(httpx_mock): switcher.is_on('MY_SWITCHER') assert GlobalAuth.get_token() == '[new_token]' +def test_remote_with_remote_required_request(httpx_mock): + """ Should call the remote API with success for required remote request""" + + # given + key = 'FF2FOR2022' + given_auth(httpx_mock) + given_check_criteria(httpx_mock, key=key, response={'result': True}) + given_context(options=ContextOptions(local=True, snapshot_location='tests/snapshots')) + Client.load_snapshot() + + switcher = Client.get_switcher() + + # test + assert switcher.remote().is_on(key) + +def test_remote_err_with_remote_reqquired_request_no_local(): + """ Should raise an exception when local mode is not enabled and remote is forced """ + + # given + given_context(options=ContextOptions(local=False)) + + switcher = Client.get_switcher() + + # test + with pytest.raises(ValueError) as excinfo: + switcher.remote().is_on('MY_SWITCHER') + + assert 'Local mode is not enabled' in str(excinfo.value) + def test_remote_err_no_key(httpx_mock): """ Should raise an exception when no key is provided """ @@ -186,12 +216,13 @@ def test_remote_err_check_criteria_default_result(httpx_mock): # Helpers -def given_context(url='https://api.switcherapi.com', api_key='[API_KEY]'): +def given_context(url='https://api.switcherapi.com', api_key='[API_KEY]', options = ContextOptions()): Client.build_context( url=url, api_key=api_key, domain='Playground', - component='switcher-playground' + component='switcher-playground', + options=options ) def given_auth(httpx_mock: HTTPXMock, status=200, token: Optional[str]='[token]', exp=int(round(time.time() * 1000))):