Skip to content

Commit

Permalink
[TDL-15661] Implement dev mode for 2.latest (#64)
Browse files Browse the repository at this point in the history
* TDL-20058: Add missing integration tests. (#61)

* Added missing assersions in tap-tester tests

* updated tap-tester tests

* added docstring for test case classes

* updated test cases

Co-authored-by: harshpatel4crest <[email protected]>

* TDL-13906: Update README.md (#60)

* updated readme file, init and added unittest

* updated config.yml file

* added logger for sync mode

* formatted unitttests

* TDL-20063: Add unit test cases (#59)

* added unittest of sync.py

* updated config.yml file

* added unittest for unknown stream sync and updated sync code

* updated unittests

* formatted unittest

* TDL-20061: Added missing fields (#58)

* Added missing fields & add shared schema

* Updated types

* Updated types

* Reverted datatype for some fields

* Updated datatype for QtyOnHand

Co-authored-by: harshpatel4crest <[email protected]>

* TDL-20062: Add minor version in API requests (#57)

* Added minor version in requests

* Added unit tests

* Removed workaround for some fields as minorversion is added

* Fixed all field test

* Updated code comment

* resolved unittest failure

Co-authored-by: harshpatel4crest <[email protected]>

* TDL-20057: Add custom exception handling (#56)

* added exception handling

* added parameterized import in config.yml file

* updated test case name

* handled other 4XX error and updated test cases

* added test case

* updated test case

* added pylint

* updated formatting

* formatted test case

* TDL-20059: Implement currently_syncing (#55)

* added currently syncing functionality

* added interrupted sync tap-tester test

* updated test case

* Updated the tests method to use common test name to use token chaining

* Updated the test method to use common test name to use token chaining

* updated interrupted sync test

* updated interrupted sync test

* added docstring for test case name

* updated comment indentation

* updated tap-tester tests

* updated interrupted sync test case

* updated interrupted sync test case

* formatted test case

* resolve unittest failure

Co-authored-by: RushT007 <[email protected]>

* added new stream CustomerType (#62)

* Implemented dev mode for tap-quickbooks

* Make pylint happy

* Reducing the instance attributes and updated the pylint disable with too-many-instance-attributes

* added new test cases for dev mode

* Fixing the review comments

* (dev-mode) Removing the expires_at key and modifiying the logic accordingly.

* Make pylint happy

* update singer-python version

* remove the extra test file

* Remove the unit test for currently syncing as it is already getting tested in interrupt sync

* pass the arguments  while creating the QuickbooksClient

* Reformat code

* Update setup.py and changelog.md for dev mode support
  • Loading branch information
sgandhi1311 authored Jan 12, 2023
1 parent ee37570 commit 27230bf
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 13 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
command: |
source /usr/local/share/virtualenvs/tap-quickbooks/bin/activate
# TODO: Adjust the pylint disables
pylint tap_quickbooks --disable 'broad-except,chained-comparison,empty-docstring,fixme,invalid-name,line-too-long,missing-class-docstring,missing-function-docstring,missing-module-docstring,no-else-raise,no-else-return,too-few-public-methods,too-many-arguments,too-many-branches,too-many-lines,too-many-locals,ungrouped-imports,wrong-spelling-in-comment,wrong-spelling-in-docstring,bad-whitespace,undefined-loop-variable'
pylint tap_quickbooks --disable 'broad-except,chained-comparison,empty-docstring,fixme,invalid-name,line-too-long,missing-class-docstring,missing-function-docstring,missing-module-docstring,no-else-raise,no-else-return,too-few-public-methods,too-many-arguments,too-many-branches,too-many-lines,too-many-locals,ungrouped-imports,wrong-spelling-in-comment,wrong-spelling-in-docstring,bad-whitespace,undefined-loop-variable,too-many-instance-attributes'
- run:
name: 'Unit Tests'
command: |
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## 2.1.0

* Add support for dev mode [#64](https://github.com/singer-io/tap-quickbooks/pull/64)

## 2.0.0

* Updated field types and added new fields as per the doc [#58](https://github.com/singer-io/tap-quickbooks/pull/58)
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
from setuptools import setup

setup(name='tap-quickbooks',
version='2.0.0',
version='2.1.0',
description='Singer.io tap for extracting data from the Quickbooks API',
author='Stitch',
url='http://singer.io',
classifiers=['Programming Language :: Python :: 3 :: Only'],
py_modules=['tap_quickbooks'],
install_requires=[
'singer-python==5.12.1',
'singer-python==5.13.0',
'requests==2.23.0',
'requests_oauthlib==1.3.0',
],
Expand Down
4 changes: 3 additions & 1 deletion tap_quickbooks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ def main():
args = singer.parse_args(required_config_keys)

config = args.config
client = QuickbooksClient(args.config_path, config)
if args.dev:
LOGGER.warning("Executing Tap in Dev mode")
client = QuickbooksClient(args.config_path, config, args.dev)
state = args.state

if args.properties and not args.catalog:
Expand Down
36 changes: 29 additions & 7 deletions tap_quickbooks/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def raise_for_error(response):
raise ex(message) from None

class QuickbooksClient():
def __init__(self, config_path, config):
def __init__(self, config_path, config, dev_mode = False):
token = {
'refresh_token': config['refresh_token'],
'token_type': 'Bearer',
Expand All @@ -134,11 +134,7 @@ def __init__(self, config_path, config):
self.realm_id = config['realm_id']
self.config_path = config_path
self.config = config
self.session = OAuth2Session(config['client_id'],
token=token,
auto_refresh_url=TOKEN_REFRESH_URL,
auto_refresh_kwargs=extra,
token_updater=self._write_config)
self.create_session(dev_mode, token, extra)
# Latest minorversion is '65' according to doc, https://developer.intuit.com/app/developer/qbo/docs/learn/explore-the-quickbooks-online-api/minor-versions
self.minor_version = 65
try:
Expand All @@ -148,6 +144,32 @@ def __init__(self, config_path, config):
LOGGER.info("Error initializing QuickbooksClient during token refresh, please reauthenticate.")
raise e

def create_session(self, dev_mode, token, extra):
"""
If dev mode is enabled then session is created with the existing tokens.
Else session is created with refreshed tokens.
"""
if dev_mode:
self.access_token = self.config.get('access_token')

if not self.access_token:
raise Exception("Access token config property is missing")

dev_mode_token = {
"refresh_token": self.config.get('refresh_token'),
# Using the existing access_token for dev mode
"access_token": self.access_token,
'token_type': 'Bearer'
}

self.session = OAuth2Session(self.config['client_id'],
token=dev_mode_token)
else:
self.session = OAuth2Session(self.config['client_id'],
token=token,
auto_refresh_url=TOKEN_REFRESH_URL,
auto_refresh_kwargs=extra,
token_updater=self._write_config)
def _write_config(self, token):
LOGGER.info("Credentials Refreshed")

Expand All @@ -156,7 +178,7 @@ def _write_config(self, token):
config = json.load(file)

config['refresh_token'] = token['refresh_token']

config['access_token'] = token['access_token']
with open(self.config_path, 'w') as file:
json.dump(config, file, indent=2)

Expand Down
2 changes: 1 addition & 1 deletion tests/test_quickbooks_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,5 +177,5 @@ def test_run(self):
actual_fields.append(md_entry['breadcrumb'][1])

# Verify there is no duplicate metadata entries
self.assertEqual(len(actual_fields), len(set(actual_fields)),
self.assertEqual(len(actual_fields), len(set(actual_fields)),
msg = "duplicates in the metadata entries retrieved")
63 changes: 63 additions & 0 deletions tests/unittests/test_dev_mode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import json
import os
import unittest
from datetime import timedelta
from unittest.mock import patch, MagicMock

import singer
from singer.utils import now, strftime
from tap_quickbooks.client import QuickbooksClient

LOGGER = singer.get_logger()


class Test_ClientDevMode(unittest.TestCase):
"""Test the dev mode functionality."""

def setUp(self):
"""Creates a sample config for test execution"""
# Data to be written
self.mock_config = {
"user_agent": "test_user_agent",
"access_token": "sample_access_token",
"refresh_token": "sample_refresh_token",
"client_id": "sample_client_id",
"client_secret": "sample_client_secret",
"realm_id": "1234567890",
"expires_at": strftime(now() + timedelta(hours=1))
}
self.tmp_config_filename = "sample_quickbooks_config.json"

# Serializing json
json_object = json.dumps(self.mock_config, indent=4)
# Writing to sample_quickbooks_config.json
with open(self.tmp_config_filename, "w") as outfile:
outfile.write(json_object)

def tearDown(self):
"""Deletes the sample config"""
if os.path.isfile(self.tmp_config_filename):
os.remove(self.tmp_config_filename)

@patch("tap_quickbooks.client.QuickbooksClient._write_config")
@patch("requests_oauthlib.OAuth2Session.request", return_value=MagicMock(status_code=200))
def test_client_with_dev_mode(self, mock_request, mock_write_config):
"""Checks the dev mode implementation and verifies write config functionality is
not called"""
QuickbooksClient(config_path=self.tmp_config_filename,
config=self.mock_config,
dev_mode=True)

# _write_config function should never be called as it will update the config
self.assertEquals(mock_write_config.call_count, 0)

@patch("requests_oauthlib.OAuth2Session.request", return_value=MagicMock(status_code=200))
def test_client_dev_mode_missing_access_token(self, mock_request):
"""Exception should be raised if missing access token"""

del self.mock_config["access_token"]

with self.assertRaises(Exception):
QuickbooksClient(config_path=self.tmp_config_filename,
config=self.mock_config,
dev_mode=True)
3 changes: 2 additions & 1 deletion tests/unittests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
class Parse_Args:
"""Mocked parse args"""

def __init__(self, discover=False, state=None, catalog=None, properties=None, config_path=None) -> None:
def __init__(self, discover=False, state=None, catalog=None, properties=None, config_path=None, dev=None) -> None:
self.discover = discover
self.state = state
self.config = {}
self.catalog = catalog
self.properties = properties
self.config_path = config_path
self.dev = dev

class TestQuickbooksInit(unittest.TestCase):

Expand Down

0 comments on commit 27230bf

Please sign in to comment.