diff --git a/CHANGELOG.md b/CHANGELOG.md index 773b86b9..d84084bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [v2.1.0] (2023-06-08) + +[Full Changelog](https://github.com/singer-io/tap-square/compare/v2.0.0...v2.1.0) + +* Replaces the deprecated `settlements` stream with the `payouts` stream [#109](https://github.com/singer-io/tap-square/pull/109) + ## [v2.0.0](https://github.com/singer-io/tap-square/tree/v1.w.0) (2023-06-08) [Full Changelog](https://github.com/singer-io/tap-square/compare/v1.3.1...v2.0.0) diff --git a/README.md b/README.md index e440b0b8..40f9cbae 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,13 @@ This tap: * BankAccounts * Refunds * Payments + * Payouts * ModifierLists * Inventories * Orders * Roles * Shifts * CashDrawerShifts - * Settlements * Customers * Includes a schema for each resource reflecting most recent tested data retrieved using the api. See [the schema folder](https://github.com/singer-io/tap-square/tree/master/tap_square/schemas) for details. diff --git a/setup.py b/setup.py index 77932adf..421febfb 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from setuptools import setup setup(name='tap-square', - version='2.0.0', + version='2.1.0', description='Singer.io tap for extracting data from the Square API', author='Stitch', url='http://singer.io', diff --git a/tap_square/client.py b/tap_square/client.py index f43a52cd..bb7f43ba 100644 --- a/tap_square/client.py +++ b/tap_square/client.py @@ -348,33 +348,31 @@ def get_roles(self, bookmarked_cursor): bookmarked_cursor, ) - def get_settlements(self, location_id, start_time, bookmarked_cursor): - url = 'https://connect.squareup.com/v1/{}/settlements'.format(location_id) - - now = utils.now() - start_time_dt = utils.strptime_to_utc(start_time) - end_time_dt = now - - # Parameter `begin_time` cannot be before 1 Jan 2013 00:00:00Z - # Doc: https://developer.squareup.com/reference/square/settlements-api/v1-list-settlements - if start_time_dt < utils.strptime_to_utc("2013-01-01T00:00:00Z"): - raise Exception("Start Date for Settlements stream cannot come before `2013-01-01T00:00:00Z`, current start_date: {}".format(start_time)) - - while start_time_dt < now: - params = { - 'limit': 200, - 'begin_time': utils.strftime(start_time_dt), - } - # If query range is over a year, shorten to a year - if now - start_time_dt > timedelta(weeks=52): - end_time_dt = start_time_dt + timedelta(weeks=52) - params['end_time'] = utils.strftime(end_time_dt) - yield from self._get_v1_objects( - url, - params, - 'settlements', - bookmarked_cursor, - ) - # Attempt again to sync til "now" - start_time_dt = end_time_dt - end_time_dt = now + def get_payouts(self, location_id, start_time, bookmarked_cursor): + if bookmarked_cursor: + cursor = bookmarked_cursor + else: + cursor = '__initial__' # initial value so while loop is always entered one time + + end_time = utils.strftime(utils.now(), utils.DATETIME_PARSE) + while cursor: + if cursor == '__initial__': + # initial text was needed to go into the while loop, but api needs + # it to be a valid bookmarked cursor or None + cursor = bookmarked_cursor + + with singer.http_request_timer('GET payouts details'): + result = self._retryable_v2_method( + lambda bdy: self._client.payouts.list_payouts( + location_id=location_id, + begin_time=start_time, + end_time=end_time, + cursor=cursor, + limit=100, + ), + None, + ) + + yield (result.body.get('items', []), result.body.get('cursor')) + + cursor = result.body.get('cursor') diff --git a/tap_square/discover.py b/tap_square/discover.py index 180107c7..a474e25c 100644 --- a/tap_square/discover.py +++ b/tap_square/discover.py @@ -8,7 +8,7 @@ def get_abs_path(path): return os.path.join(os.path.dirname(os.path.realpath(__file__)), path) # NB: These streams cannot be queried using Sandbox OAuth credentials -PRODUCTION_ONLY_STREAMS = {'roles', 'bank_accounts', 'settlements'} +PRODUCTION_ONLY_STREAMS = {'roles', 'bank_accounts', 'payouts'} def get_schemas(sandbox): diff --git a/tap_square/schemas/payouts.json b/tap_square/schemas/payouts.json new file mode 100644 index 00000000..c122b967 --- /dev/null +++ b/tap_square/schemas/payouts.json @@ -0,0 +1,152 @@ +{ + "type": [ + "null", + "object" + ], + "properties": { + "id": { + "type": [ + "null", + "string" + ] + }, + "status": { + "type": [ + "null", + "string" + ] + }, + "location_id": { + "type": [ + "null", + "string" + ] + }, + "created_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "amount_money": { + "type": [ + "null", + "object" + ], + "properties": { + "amount": { + "type": [ + "null", + "integer" + ] + }, + "currency": { + "type": [ + "null", + "string" + ] + } + } + }, + "destination": { + "type": [ + "null", + "object" + ], + "properties": { + "type": { + "type": [ + "null", + "string" + ] + }, + "id": { + "type": [ + "null", + "string" + ] + } + } + }, + "version": { + "type": [ + "null", + "integer" + ] + }, + "type": { + "type": [ + "null", + "string" + ] + }, + "payout_fee": { + "type": [ + "null", + "array" + ], + "items": { + "type": [ + "null", + "object" + ], + "properties": { + "effective_at": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "type": { + "type": [ + "null", + "string" + ] + }, + "amount_money": { + "type": [ + "null", + "object" + ], + "properties": { + "amount": { + "type": [ + "null", + "integer" + ] + }, + "currency": { + "type": [ + "null", + "string" + ] + } + } + } + } + } + }, + "arrival_date": { + "type": [ + "null", + "string" + ], + "format": "date-time" + }, + "end_to_end_id": { + "type": [ + "null", + "string" + ] + } + } +} diff --git a/tap_square/schemas/settlements.json b/tap_square/schemas/settlements.json deleted file mode 100644 index f00a99de..00000000 --- a/tap_square/schemas/settlements.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "type": "object", - "properties": { - "id": { - "type": ["null", "string"] - }, - "bank_account_id": { - "type": ["null", "string"] - }, - "entries": { - "type": ["null", "array"], - "items": { - "amount_money": { - "type": ["null", "object"], - "properties": { - "amount": { - "type": ["null", "integer"] - }, - "currency_code": { - "type": ["null", "string"] - } - } - }, - "fee_money": { - "type": ["null", "object"], - "properties": { - "amount": { - "type": ["null", "integer"] - }, - "currency_code": { - "type": ["null", "string"] - } - } - }, - "payment_id": { - "type": ["null", "string"] - }, - "`type`": { - "type": ["null", "string"] - } - } - }, - "initiated_at": { - "type": ["null", "string"], - "format": "date-time" - }, - "status": { - "type": ["null", "string"] - }, - "total_money": { - "type": ["null", "object"], - "properties": { - "amount": { - "type": ["null", "integer"] - }, - "currency_code": { - "type": ["null", "string"] - } - } - } - } -} diff --git a/tap_square/streams.py b/tap_square/streams.py index cbd4e53f..260b2e62 100644 --- a/tap_square/streams.py +++ b/tap_square/streams.py @@ -331,8 +331,8 @@ def get_pages(self, bookmarked_cursor, start_time): yield from self.client.get_cash_drawer_shifts(location_id, start_time, bookmarked_cursor) -class Settlements(FullTableStream): - tap_stream_id = 'settlements' +class Payouts(FullTableStream): + tap_stream_id = 'payouts' key_properties = ['id'] replication_method = 'FULL_TABLE' valid_replication_keys = [] @@ -340,8 +340,8 @@ class Settlements(FullTableStream): def get_pages(self, bookmarked_cursor, start_time): for location_id in Locations.get_all_location_ids(self.client): - # Settlements requests can only take up to 1 location_id at a time - yield from self.client.get_settlements(location_id, start_time, bookmarked_cursor) + # payouts requests can only take up to 1 location_id at a time + yield from self.client.get_payouts(location_id, start_time, bookmarked_cursor) class TeamMembers(Stream): tap_stream_id = 'team_members' @@ -399,13 +399,13 @@ def sync(self, state, stream_schema, stream_metadata, config, transformer): 'bank_accounts': BankAccounts, 'refunds': Refunds, 'payments': Payments, + 'payouts': Payouts, 'modifier_lists': ModifierLists, 'inventories': Inventories, 'orders': Orders, 'roles': Roles, 'shifts': Shifts, 'cash_drawer_shifts': CashDrawerShifts, - 'settlements': Settlements, 'team_members': TeamMembers, 'customers': Customers } diff --git a/tests/base.py b/tests/base.py index 58758582..2f0708a2 100644 --- a/tests/base.py +++ b/tests/base.py @@ -41,7 +41,7 @@ class TestSquareBase(ABC, TestCase): START_DATE_FORMAT = "%Y-%m-%dT00:00:00Z" STATIC_START_DATE = "2020-07-13T00:00:00Z" START_DATE = "" - PRODUCTION_ONLY_STREAMS = {'roles', 'bank_accounts', 'settlements'} + PRODUCTION_ONLY_STREAMS = {'roles', 'bank_accounts', 'payouts'} DEFAULT_BATCH_LIMIT = 1000 API_LIMIT = { @@ -55,11 +55,11 @@ class TestSquareBase(ABC, TestCase): 'roles': 100, 'refunds': 100, 'payments': 100, + 'payouts': 100, 'customers': 100, 'modifier_lists': DEFAULT_BATCH_LIMIT, 'orders': 500, 'shifts': 200, - 'settlements': 200, } def setUp(self): @@ -180,18 +180,18 @@ def expected_metadata(self): self.PRIMARY_KEYS: {'id'}, self.REPLICATION_METHOD: self.FULL, }, - "refunds": { + "payouts": { self.PRIMARY_KEYS: {'id'}, self.REPLICATION_METHOD: self.FULL, }, - "roles": { + "refunds": { self.PRIMARY_KEYS: {'id'}, self.REPLICATION_METHOD: self.FULL, - self.START_DATE_KEY: 'updated_at' }, - "settlements": { + "roles": { self.PRIMARY_KEYS: {'id'}, self.REPLICATION_METHOD: self.FULL, + self.START_DATE_KEY: 'updated_at' }, "shifts": { self.PRIMARY_KEYS: {'id'}, @@ -227,7 +227,6 @@ def production_streams(): return { 'roles', 'bank_accounts', - 'settlements', } def sandbox_streams(self): @@ -250,7 +249,7 @@ def untestable_streams(): return { 'bank_accounts', # No endpoints for CREATE or UPDATE 'cash_drawer_shifts', # Require cash transactions (not supported by API) - 'settlements', # Depenedent on bank_account related transactions, no endpoints for CREATE or UPDATE + 'payouts', # Depenedent on bank_account related transactions, no endpoints for CREATE or UPDATE } def dynamic_data_streams(self): diff --git a/tests/test_client.py b/tests/test_client.py index 07639fe0..4515a00b 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -415,19 +415,6 @@ def get_roles(self, bookmarked_cursor): bookmarked_cursor, ) - def get_settlements(self, location_id, start_time, bookmarked_cursor): - url = 'https://connect.squareup.com/v1/{}/settlements'.format(location_id) - params = { - 'limit': 200, - 'begin_time': start_time, - } - yield from self._get_v1_objects( - url, - params, - 'roles', - bookmarked_cursor, - ) - def get_all_location_ids(self): all_location_ids = list() for page, _ in self.get_locations(): @@ -454,13 +441,6 @@ def get_cds_pages(self, start_time, bookmarked_cursor): for page, cursor in self.get_cash_drawer_shifts(location_id, start_time, bookmarked_cursor): yield page, cursor - def get_settlements_pages(self, start_time, bookmarked_cursor): #pylint: disable=unused-argument - # refactored from settlements.sync - for location_id in self.get_all_location_ids(): - # Settlements requests can only take up to 1 location_id at a time - for page, batch_token in self.get_settlements(location_id, start_time, bookmarked_cursor): - yield page, batch_token - def get_all(self, stream, start_date): # pylint: disable=too-many-return-statements if stream == 'items': return [obj for page, _ in self.get_catalog('ITEM', start_date, None) for obj in page] @@ -490,8 +470,6 @@ def get_all(self, stream, start_date): # pylint: disable=too-many-return-stateme elif stream == 'shifts': return [obj for page, _ in self.get_shifts(None) for obj in page if obj['updated_at'] >= start_date] - elif stream == 'settlements': - return [obj for page, _ in self.get_settlements_pages(start_date, None) for obj in page] elif stream == 'cash_drawer_shifts': return [obj for page, _ in self.get_cds_pages(start_date, None) for obj in page] elif stream == 'customers':