diff --git a/CHANGELOG.md b/CHANGELOG.md index f980a9fd..f9498567 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 1.11.0 + * Deprecate the streams for the older version. [#196](https://github.com/singer-io/tap-shopify/pull/196) + * Deprecated streams - products, inventory_items, metafields (product) + ## 1.10.0 * Updates the Shopify SDK to 12.3.0 * Updates API version used to 2024-01 diff --git a/setup.py b/setup.py index 30f2aec2..b97ffd3a 100755 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name="tap-shopify", - version="1.10.0", + version="1.11.0", description="Singer.io tap for extracting Shopify data", author="Stitch", url="http://github.com/singer-io/tap-shopify", diff --git a/tap_shopify/__init__.py b/tap_shopify/__init__.py index c6323c90..06c6025f 100644 --- a/tap_shopify/__init__.py +++ b/tap_shopify/__init__.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 import os -import datetime +from datetime import datetime, timezone import json import time import math @@ -13,13 +13,30 @@ from singer import metadata from singer import Transformer from tap_shopify.context import Context -from tap_shopify.exceptions import ShopifyError +from tap_shopify.exceptions import ShopifyError, ShopifyDeprecationError from tap_shopify.streams.base import shopify_error_handling, get_request_timeout import tap_shopify.streams # Load stream objects into Context REQUIRED_CONFIG_KEYS = ["shop", "api_key"] + LOGGER = singer.get_logger() SDC_KEYS = {'id': 'integer', 'name': 'string', 'myshopify_domain': 'string'} +DEPRECATED_STREAMS = ["products", "inventory_items"] +IS_METAFIELDS_SELECTED = False +SELECTED_DEPRECATED_STREAMS = [] +CUTOFF_DATE = datetime(2025, 3, 31, tzinfo=timezone.utc) +TODAY_UTC = utils.now() + +def raise_warning(): + + if "products" in SELECTED_DEPRECATED_STREAMS and IS_METAFIELDS_SELECTED: + SELECTED_DEPRECATED_STREAMS.append("metafields (product related)") + + if SELECTED_DEPRECATED_STREAMS and TODAY_UTC > CUTOFF_DATE: + raise ShopifyDeprecationError( + f"The {SELECTED_DEPRECATED_STREAMS} stream(s) are no longer supported after 31st March 2025. " + "Please upgrade to the latest version of tap-shopify, which supports GraphQL endpoints for these streams." + ) @shopify_error_handling def initialize_shopify_client(): @@ -161,6 +178,17 @@ def sync(): LOGGER.info('Skipping stream: %s', stream_id) continue + if stream_id in DEPRECATED_STREAMS: + SELECTED_DEPRECATED_STREAMS.append(stream_id) + if TODAY_UTC > CUTOFF_DATE: + LOGGER.critical( + f"The {stream_id} stream is no longer supported. " + "Please upgrade to the latest version of tap-shopify, which supports GraphQL endpoints for this stream." + ) + continue + if stream_id == 'metafields': + global IS_METAFIELDS_SELECTED + IS_METAFIELDS_SELECTED = True LOGGER.info('Syncing stream: %s', stream_id) if not Context.state.get('bookmarks'): @@ -211,6 +239,7 @@ def main(): Context.catalog = discover() sync() + raise_warning() except pyactiveresource.connection.ResourceNotFound as exc: raise ShopifyError(exc, 'Ensure shop is entered correctly') from exc except pyactiveresource.connection.UnauthorizedAccess as exc: @@ -224,6 +253,8 @@ def main(): msg = body.get('errors') finally: raise ShopifyError(exc, msg) from exc + except ShopifyDeprecationError as exc: + raise ShopifyDeprecationError(exc) from None except Exception as exc: raise ShopifyError(exc) from exc diff --git a/tap_shopify/exceptions.py b/tap_shopify/exceptions.py index 94fa62dd..1284f2a5 100644 --- a/tap_shopify/exceptions.py +++ b/tap_shopify/exceptions.py @@ -1,3 +1,6 @@ class ShopifyError(Exception): def __init__(self, error, msg=''): super().__init__('{}\n{}'.format(error.__class__.__name__, msg)) + +class ShopifyDeprecationError(Exception): + pass diff --git a/tap_shopify/streams/metafields.py b/tap_shopify/streams/metafields.py index 2c62d1d0..6c62875b 100644 --- a/tap_shopify/streams/metafields.py +++ b/tap_shopify/streams/metafields.py @@ -1,6 +1,8 @@ import json import shopify import singer +from singer import utils +from datetime import datetime, timezone from tap_shopify.context import Context from tap_shopify.streams.base import (Stream, @@ -9,9 +11,15 @@ OutOfOrderIdsError) LOGGER = singer.get_logger() +CUTOFF_DATE = datetime(2025, 3, 31, tzinfo=timezone.utc) +TODAY_UTC = utils.now() +PARENT_STREAMS = ['orders', 'customers', 'products', 'custom_collections'] def get_selected_parents(): - for parent_stream in ['orders', 'customers', 'products', 'custom_collections']: + # Shopify has sunset the products REST API endpoint on 31st January 2025 + if TODAY_UTC > CUTOFF_DATE: + PARENT_STREAMS.remove('products') + for parent_stream in PARENT_STREAMS: if Context.is_selected(parent_stream): yield Context.stream_objects[parent_stream]()