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
17 changes: 16 additions & 1 deletion switcher_client/lib/remote_auth.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from time import time

from switcher_client.lib.remote import Remote
from switcher_client.lib.globals.global_context import Context
from switcher_client.lib.globals import GlobalAuth
Expand All @@ -14,4 +16,17 @@ def init(context: Context):
def auth():
token, exp = Remote.auth(RemoteAuth.__context)
GlobalAuth.set_token(token)
GlobalAuth.set_exp(exp)
GlobalAuth.set_exp(exp)

@staticmethod
def check_health():
if GlobalAuth.get_token() != 'SILENT':
return

@staticmethod
def is_token_expired() -> bool:
exp = GlobalAuth.get_exp()
if exp is None:
return True

return float(exp) < time()
24 changes: 18 additions & 6 deletions switcher_client/switcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,25 @@ def __init__(self, context: Context, key: Optional[str] = None):
super().__init__(key)
self._context = context

def prepare(self, key: Optional[str] = None):
""" Checks API credentials and connectivity """
self.__validate_args(key)
RemoteAuth.auth()

def is_on(self, key: Optional[str] = None) -> bool:
""" Execute criteria """
self._show_details = False
self.__validate_args(key)
self.__validate_args(key, details=False)
return self.__submit().result

def is_on_with_details(self, key: Optional[str] = None) -> ResultDetail:
""" Execute criteria with details """
self._show_details = True
self.__validate_args(key)
self.__validate_args(key, details=True)
return self.__submit()

def __submit(self) -> ResultDetail:
""" Submit criteria for execution (local or remote) """
self.__validate()
self.__execute_api_checks()
response = self.__execute_remote_criteria()

return response
Expand All @@ -39,18 +42,27 @@ def __validate(self) -> 'Switcher':
if not self._key:
errors.append('Missing key field')

self.__execute_api_checks()
if not GlobalAuth.get_token():
errors.append('Missing token field')

if errors:
raise ValueError(f"Something went wrong: {', '.join(errors)}")

return self

def __validate_args(self, key: Optional[str] = None):
def __validate_args(self, key: Optional[str] = None, details: Optional[bool] = None):
if key is not None:
self._key = key

if details is not None:
self._show_details = details

def __execute_api_checks(self):
""" Assure API is available and token is valid """
RemoteAuth.auth()
RemoteAuth.check_health()
if RemoteAuth.is_token_expired():
self.prepare(self._key)

def __execute_remote_criteria(self):
""" Execute remote criteria """
Expand Down
69 changes: 66 additions & 3 deletions tests/test_switcher_remote.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from typing import Optional

import pytest
import responses
import time

from switcher_client.errors import RemoteAuthError
from switcher_client import Client, ContextOptions
from switcher_client import Client
from switcher_client.lib.globals.global_auth import GlobalAuth

@responses.activate
def test_remote():
Expand Down Expand Up @@ -41,6 +44,28 @@ def test_remote_with_input():
assert switcher \
.check_value('user_id') \
.is_on('MY_SWITCHER')

@responses.activate
def test_remote_with_prepare():
""" Should prepare call the remote API with success """

# given
given_auth()
given_check_criteria(response={'result': True}, match=[
responses.matchers.json_params_matcher({
'entry': [{
'strategy': 'VALUE_VALIDATION',
'input': 'user_id'
}]
})
])
given_context()

switcher = Client.get_switcher()

# test
switcher.check_value('user_id').prepare('MY_SWITCHER')
assert switcher.is_on()

@responses.activate
def test_remote_with_details():
Expand All @@ -66,18 +91,56 @@ def test_remote_with_details():
assert response.result is True
assert response.metadata == {'key': 'value'}

@responses.activate
def test_remote_renew_token():
""" Should renew the token when it is expired """

# given
given_auth(status=200, token='[expired_token]', exp=int(round(time.time())) - 3600)
given_auth(status=200, token='[new_token]', exp=int(round(time.time())) + 3600)
given_check_criteria(response={'result': True})
given_context()

switcher = Client.get_switcher()

# test
switcher.is_on('MY_SWITCHER')
assert GlobalAuth.get_token() == '[expired_token]'
switcher.is_on('MY_SWITCHER')
assert GlobalAuth.get_token() == '[new_token]'

@responses.activate
def test_remote_err_no_key():
""" Should raise an exception when no key is provided """

# given
given_auth()
given_context()

switcher = Client.get_switcher()

# test
with pytest.raises(ValueError):
with pytest.raises(ValueError) as excinfo:
switcher.is_on()

assert 'Missing key field' in str(excinfo.value)

@responses.activate
def test_remote_err_no_token():
""" Should raise an exception when no token is provided """

# given
given_auth(status=200, token=None)
given_context()

switcher = Client.get_switcher()

# test
with pytest.raises(ValueError) as excinfo:
switcher.is_on('MY_SWITCHER')

assert 'Missing token field' in str(excinfo.value)

@responses.activate
def test_remote_err_invalid_api_key():
""" Should raise an exception when the API key is invalid """
Expand Down Expand Up @@ -121,7 +184,7 @@ def given_context(url='https://api.switcherapi.com', api_key='[API_KEY]'):
component='switcher-playground'
)

def given_auth(status=200, token='[token]', exp=int(round(time.time() * 1000))):
def given_auth(status=200, token: Optional[str]='[token]', exp=int(round(time.time() * 1000))):
responses.add(
responses.POST,
'https://api.switcherapi.com/criteria/auth',
Expand Down