-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[TDL-15656] Added Support for dev mode #41
base: master
Are you sure you want to change the base?
Conversation
Co-authored-by: Rushikesh Todkar <98420315+RushiT0122@users.noreply.github.com>
with open(self.tmp_config_path, "w") as ff: | ||
json.dump(self.base_config, ff) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will strongly suggest deleting the temp config once the unit test is executed. If we don't delete it, I think it will keep on accumulating the temp configs (as the everyday job runs) within circleci server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed, but as the cci runner is temporary provisioned and the files created during testing are not preserved.
it will be autodeleted
tap_eloqua/client.py
Outdated
Server5xxError, | ||
max_tries=5, | ||
factor=2) | ||
@backoff.on_exception(backoff.expo,Server5xxError,5,factor=2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@backoff.on_exception(backoff.expo,Server5xxError,5,factor=2) | |
@backoff.on_exception(backoff.expo, | |
Server5xxError, | |
max_tries=5, | |
factor=2) |
tap_eloqua/client.py
Outdated
except KeyError as err: | ||
raise Exception("Unable to locate key in config") from err | ||
if not self.__access_token or self.__expires < now(): | ||
raise Exception("Access Token in config is expired, unable to authenticate in dev mode") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use either ex
or e
as a convention for exception.
except KeyError as err: | |
raise Exception("Unable to locate key in config") from err | |
if not self.__access_token or self.__expires < now(): | |
raise Exception("Access Token in config is expired, unable to authenticate in dev mode") | |
except KeyError as ex: | |
raise Exception("Unable to locate key in config") from ex | |
if not self.__access_token or self.__expires < now(): | |
raise Exception("Access Token in config is expired, unable to authenticate in dev mode") |
tap_eloqua/client.py
Outdated
@@ -135,3 +128,4 @@ def get(self, path, **kwargs): | |||
|
|||
def post(self, path, **kwargs): | |||
return self.request('POST', path=path, **kwargs) | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this extra line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
tap_eloqua/client.py
Outdated
expires_seconds = data['expires_in'] - 10 # pad by 10 seconds | ||
self.__expires = datetime.utcnow() + timedelta(seconds=expires_seconds) | ||
if not self.dev_mode: | ||
update_config_keys = {"refresh_token":self.__refresh_token,"access_token":self.__access_token,"expires_in": strftime(self.__expires)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reduce the statement length.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
update_config_keys = {"refresh_token":self.__refresh_token,"access_token":self.__access_token,"expires_in": strftime(self.__expires)} | |
update_config_keys = {"refresh_token": self.__refresh_token, | |
"access_token": self.__access_token, | |
"expires_in": strftime(self.__expires)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
@@ -377,12 +377,12 @@ def sync_activity_stream(client, | |||
activity_type): | |||
finished = False | |||
sync_start = pendulum.now('UTC') | |||
end_date = sync_start | |||
end_date, last_sync_date = sync_start, None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these changes in sync.py
dev-mode related? If not can we keep it separate from dev-mode changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And if these are required changes then update the description on this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RushiT0122
These are not dev mode related changes, but if you see the code from Line:380 - Line:403
last_sync_date
was declared on Line:385 in the try statement, but accessed outside its declared scope in the except block, hence it was introduced here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure this is a problem in other languages, but not python
>>> for i in range(3):
... try:
... print("About to initialize `my_variable`")
... my_variable = i
... print(f"my_variable: {my_variable}")
... raise RuntimeError(f"Problem occured with {my_variable}")
... except Exception as err:
... print(f"Caught {type(err)} with message {str(err)}")
... print(f"Current value of my_variable {my_variable}")
...
About to initialize `my_variable`
my_variable: 0
Caught <class 'RuntimeError'> with message Problem occured with 0
Current value of my_variable 0
About to initialize `my_variable`
my_variable: 1
Caught <class 'RuntimeError'> with message Problem occured with 1
Current value of my_variable 1
About to initialize `my_variable`
my_variable: 2
Caught <class 'RuntimeError'> with message Problem occured with 2
Current value of my_variable 2
tap_eloqua/utils.py
Outdated
LOGGER = get_logger() | ||
|
||
|
||
def write_config(config_path,data :Dict) -> Dict: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please check this comment on tap-deputy: singer-io/tap-deputy#3 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
made required changes
tests/unittests/test_devmode.py
Outdated
try: | ||
client = EloquaClient(config_path=self.tmp_config_path,dev_mode=True,config=self.base_config) | ||
|
||
client.get_access_token() | ||
except Exception as error: | ||
self.assertEqual(str(error), "Unable to locate key in config") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use with self.assertRaises()
unittest construct here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't use assertRaises here, because we can only check the "type" of the exception with it.
But in this case, all exceptions are raised with "Exception" class and I want to validate the message-string that is raised with it.
self.assertEqual(str(error), "Unable to locate key in config") | ||
|
||
@patch("requests.Session.post", side_effect=mocked_auth_post) | ||
def test_client_valid_token(self, mocked_auth_post): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def test_client_valid_token(self, mocked_auth_post): | |
def test_client_valid_access_token(self, mocked_auth_post): |
client.get_access_token() | ||
|
||
@patch("requests.Session.post", side_effect=mocked_auth_post) | ||
def test_client_invalid_token(self, mocked_auth_post): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def test_client_invalid_token(self, mocked_auth_post): | |
def test_client_invalid_access_token(self, mocked_auth_post): |
tests/unittests/test_devmode.py
Outdated
try: | ||
client = EloquaClient(config_path=self.tmp_config_path,dev_mode=True,config=config_sample) | ||
client.get_access_token() | ||
except Exception as error: | ||
self.assertEqual(str(error), "Access Token in config is expired, unable to authenticate in dev mode") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use with self.assertRaises() unittest construct here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't use assertRaises here, because we can only check the "type" of the exception with it.
But in this case, all exceptions are raised with "Exception" class and I want to validate the message-string that is raised with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docs for assertRaises
. These docs show how you can use a context manager with assertRaises
. And they show that the exception object is a property on the manager, which means we can do exactly what you want to do
Here's how you use it
$ python3
Python 3.10.9 (main, Dec 15 2022, 18:25:35) [Clang 14.0.0 (clang-1400.0.29.202)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
# start setup
>>> import unittest
>>> class some_test(unittest.TestCase):
... pass
...
>>> my_test = some_test()
# Prove assertRaises works
>>> with my_test.assertRaises(Exception):
... 1+1
...
2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/Cellar/python@3.10/3.10.9/Frameworks/Python.framework/Versions/3.10/lib/python3.10/unittest/case.py", line 226, in __exit__
self._raiseFailure("{} not raised".format(exc_name))
File "/usr/local/Cellar/python@3.10/3.10.9/Frameworks/Python.framework/Versions/3.10/lib/python3.10/unittest/case.py", line 163, in _raiseFailure
raise self.test_case.failureException(msg)
AssertionError: Exception not raised
# Prove the test can pass
>>> with my_test.assertRaises(Exception) as context_manager:
... raise Exception("this is a test")
...
# Show we have the exception
>>> actual_exception = context_manager.exception
>>> actual_exception
Exception('this is a test')
>>> str(actual_exception)
'this is a test'
self.assertEqual(True, "access_token" in config) | ||
self.assertEqual(True, "refresh_token" in config) | ||
|
||
def test_devmode_accesstoken_absent(self, *args, **kwargs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like *args
and **kwargs
is unused
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, there are a lot of little changes to make. Functionally, the code seems fine and a lot like other dev-mode implementations.
But because code is written once and read over and over, I think it's very important that we think about cleaning up the style once the logic is complete. It's nice to reference any of the style guides out on the internet (they all basically say the same thing).
name: 'pylint' | ||
command: | | ||
source /usr/local/share/virtualenvs/tap-eloqua/bin/activate | ||
pylint tap_eloqua -d C,R,W |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use the standard list of disables?
@@ -377,12 +377,12 @@ def sync_activity_stream(client, | |||
activity_type): | |||
finished = False | |||
sync_start = pendulum.now('UTC') | |||
end_date = sync_start | |||
end_date, last_sync_date = sync_start, None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure this is a problem in other languages, but not python
>>> for i in range(3):
... try:
... print("About to initialize `my_variable`")
... my_variable = i
... print(f"my_variable: {my_variable}")
... raise RuntimeError(f"Problem occured with {my_variable}")
... except Exception as err:
... print(f"Caught {type(err)} with message {str(err)}")
... print(f"Current value of my_variable {my_variable}")
...
About to initialize `my_variable`
my_variable: 0
Caught <class 'RuntimeError'> with message Problem occured with 0
Current value of my_variable 0
About to initialize `my_variable`
my_variable: 1
Caught <class 'RuntimeError'> with message Problem occured with 1
Current value of my_variable 1
About to initialize `my_variable`
my_variable: 2
Caught <class 'RuntimeError'> with message Problem occured with 2
Current value of my_variable 2
@@ -0,0 +1,18 @@ | |||
from typing import Dict |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unused
@patch("requests.Session.post", side_effect=mocked_auth_post) | ||
def test_client_without_dev_mode(self, mocked_auth_post): | ||
"""checks if the client can write refresh token and expiry to | ||
config.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this line wrapped if line 23 isn't?
self.assertEqual(True, "expires_in" in config) | ||
self.assertEqual(True, "access_token" in config) | ||
self.assertEqual(True, "refresh_token" in config) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there are 2 better options here:
"expires_in" in config
is a boolean. So these are equivalent
self.assertEqual(True, "expires_in" in config)
self.assert("expires_in" in config)
if self.dev_mode: | ||
try: | ||
self.__access_token = self.config['access_token'] | ||
self.__expires=strptime_to_utc(self.config['expires_in']) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.__expires=strptime_to_utc(self.config['expires_in']) | |
self.__expires = strptime_to_utc(self.config['expires_in']) |
"refresh_token":self.__refresh_token, | ||
"access_token":self.__access_token, | ||
"expires_in": strftime(self.__expires) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"refresh_token":self.__refresh_token, | |
"access_token":self.__access_token, | |
"expires_in": strftime(self.__expires) | |
"refresh_token": self.__refresh_token, | |
"access_token": self.__access_token, | |
"expires_in": strftime(self.__expires) |
"access_token":self.__access_token, | ||
"expires_in": strftime(self.__expires) | ||
} | ||
self.config = write_config(self.__config_path,update_config_keys) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.config = write_config(self.__config_path,update_config_keys) | |
self.config = write_config(self.__config_path, update_config_keys) |
data = self.request('GET', | ||
url='https://login.eloqua.com/id', | ||
endpoint='base_url') | ||
data = self.request('GET',url='https://login.eloqua.com/id',endpoint='base_url') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
data = self.request('GET',url='https://login.eloqua.com/id',endpoint='base_url') | |
data = self.request('GET', url='https://login.eloqua.com/id', endpoint='base_url') |
json.dump(self.base_config, ff) | ||
|
||
|
||
client = EloquaClient(config_path=self.tmp_config_path,dev_mode=False,config=self.base_config) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
client = EloquaClient(config_path=self.tmp_config_path,dev_mode=False,config=self.base_config) | |
client = EloquaClient(config_path=self.tmp_config_path, dev_mode=False, config=self.base_config) |
Description of change
Add support for
-dev
/--dev
cli param,refresh_token
expiry while running in dev-modeaccess_token
andexpiry
in configaccess_token
is not availableManual QA steps
access_token
&expires_in
--dev
or '-dev' paramRisks (Low)
P.R Dependant on #158
Rollback steps