airbyte-source-shopify 4.3.0__tar.gz → 4.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/PKG-INFO +1 -1
  2. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/pyproject.toml +1 -1
  3. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/http_request.py +22 -1
  4. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/source.py +16 -4
  5. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/streams/streams.py +1 -1
  6. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/utils.py +1 -1
  7. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/.airbyte-pypi-readme.md +0 -0
  8. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/__init__.py +0 -0
  9. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/auth.py +0 -0
  10. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/config_migrations.py +0 -0
  11. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/run.py +0 -0
  12. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/abandoned_checkouts.json +0 -0
  13. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/articles.json +0 -0
  14. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/balance_transactions.json +0 -0
  15. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/blogs.json +0 -0
  16. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/collection_products.json +0 -0
  17. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/collections.json +0 -0
  18. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/collects.json +0 -0
  19. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/countries.json +0 -0
  20. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/custom_collections.json +0 -0
  21. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/customer_address.json +0 -0
  22. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/customer_journey_summary.json +0 -0
  23. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/customers.json +0 -0
  24. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/deleted_products.json +0 -0
  25. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/discount_codes.json +0 -0
  26. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/discount_codes_sync.json +0 -0
  27. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/disputes.json +0 -0
  28. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/draft_orders.json +0 -0
  29. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/fulfillment_orders.json +0 -0
  30. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/fulfillments.json +0 -0
  31. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/inventory_items.json +0 -0
  32. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/inventory_levels.json +0 -0
  33. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/locations.json +0 -0
  34. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/market_countries.json +0 -0
  35. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/metafield_articles.json +0 -0
  36. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/metafield_blogs.json +0 -0
  37. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/metafield_collections.json +0 -0
  38. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/metafield_customers.json +0 -0
  39. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/metafield_draft_orders.json +0 -0
  40. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/metafield_locations.json +0 -0
  41. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/metafield_orders.json +0 -0
  42. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/metafield_pages.json +0 -0
  43. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/metafield_product_images.json +0 -0
  44. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/metafield_product_variants.json +0 -0
  45. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/metafield_products.json +0 -0
  46. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/metafield_shops.json +0 -0
  47. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/metafield_smart_collections.json +0 -0
  48. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/order_agreements.json +0 -0
  49. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/order_refunds.json +0 -0
  50. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/order_risks.json +0 -0
  51. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/orders.json +0 -0
  52. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/pages.json +0 -0
  53. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/price_rules.json +0 -0
  54. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/product_images.json +0 -0
  55. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/product_variants.json +0 -0
  56. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/products.json +0 -0
  57. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/profile_location_groups.json +0 -0
  58. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/shop.json +0 -0
  59. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/smart_collections.json +0 -0
  60. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/tender_transactions.json +0 -0
  61. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/schemas/transactions.json +0 -0
  62. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/scopes.py +0 -0
  63. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/shopify_graphql/bulk/__init__.py +0 -0
  64. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/shopify_graphql/bulk/exceptions.py +0 -0
  65. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/shopify_graphql/bulk/external_sort.py +0 -0
  66. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/shopify_graphql/bulk/job.py +0 -0
  67. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/shopify_graphql/bulk/query.py +0 -0
  68. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/shopify_graphql/bulk/record.py +0 -0
  69. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/shopify_graphql/bulk/retry.py +0 -0
  70. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/shopify_graphql/bulk/status.py +0 -0
  71. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/shopify_graphql/bulk/tools.py +0 -0
  72. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/spec.json +0 -0
  73. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/streams/base_streams.py +0 -0
  74. {airbyte_source_shopify-4.3.0 → airbyte_source_shopify-4.3.2}/source_shopify/transform.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: airbyte-source-shopify
3
- Version: 4.3.0
3
+ Version: 4.3.2
4
4
  Summary: Source CDK implementation for Shopify.
5
5
  Home-page: https://airbyte.com
6
6
  License: ELv2
@@ -3,7 +3,7 @@ requires = [ "poetry-core>=1.0.0",]
3
3
  build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
- version = "4.3.0"
6
+ version = "4.3.2"
7
7
  name = "airbyte-source-shopify"
8
8
  description = "Source CDK implementation for Shopify."
9
9
  authors = [ "Airbyte <contact@airbyte.io>",]
@@ -1,12 +1,13 @@
1
1
  # Copyright (c) 2024 Airbyte, Inc., all rights reserved.
2
2
 
3
+ import re
3
4
  from typing import Optional, Union
4
5
 
5
6
  import requests
6
7
  from requests import exceptions
7
8
 
8
9
  from airbyte_cdk.models import FailureType
9
- from airbyte_cdk.sources.streams.http.error_handlers import ErrorHandler, ErrorResolution, ResponseAction
10
+ from airbyte_cdk.sources.streams.http.error_handlers import ErrorHandler, ErrorResolution, HttpStatusErrorHandler, ResponseAction
10
11
 
11
12
 
12
13
  RESPONSE_CONSUMPTION_EXCEPTIONS = (
@@ -26,6 +27,26 @@ TRANSIENT_EXCEPTIONS = (
26
27
  _NO_ERROR_RESOLUTION = ErrorResolution(ResponseAction.SUCCESS, None, None)
27
28
 
28
29
 
30
+ class ShopifyConnectionCheckErrorHandler(HttpStatusErrorHandler):
31
+ def interpret_response(self, response: Optional[Union[requests.Response, Exception]]) -> ErrorResolution:
32
+ if isinstance(response, requests.Response):
33
+ if response.status_code == 401:
34
+ return ErrorResolution(
35
+ ResponseAction.FAIL,
36
+ FailureType.config_error,
37
+ "Shopify authentication failed: the access token is invalid or revoked. Re-authenticate the source or regenerate the Admin API access token.",
38
+ )
39
+ if response.status_code == 403:
40
+ scopes = sorted(set(re.findall(r"\b(?:read|write)_[a-z_]+\b", response.text)))
41
+ required = f" ({', '.join(scopes)})" if scopes else ""
42
+ return ErrorResolution(
43
+ ResponseAction.FAIL,
44
+ FailureType.config_error,
45
+ f"Shopify access is denied. Grant the app the required access scopes{required}, then re-authorize the source or regenerate the Admin API access token.",
46
+ )
47
+ return super().interpret_response(response)
48
+
49
+
29
50
  class ShopifyErrorHandler(ErrorHandler):
30
51
  def __init__(self, stream_name: str = "<no specified stream>") -> None:
31
52
  self._stream_name = stream_name
@@ -13,10 +13,12 @@ from requests.exceptions import ConnectionError, RequestException, SSLError
13
13
  from airbyte_cdk.models import FailureType, SyncMode
14
14
  from airbyte_cdk.sources import AbstractSource
15
15
  from airbyte_cdk.sources.streams import Stream
16
+ from airbyte_cdk.sources.streams.http.error_handlers.default_error_mapping import DEFAULT_ERROR_MAPPING
16
17
  from airbyte_cdk.sources.streams.http.exceptions import BaseBackoffException
17
18
  from airbyte_cdk.utils import AirbyteTracedException
18
19
 
19
20
  from .auth import MissingAccessTokenError, ShopifyAuthenticator
21
+ from .http_request import ShopifyConnectionCheckErrorHandler
20
22
  from .scopes import ShopifyScopes
21
23
  from .streams.streams import (
22
24
  AbandonedCheckouts,
@@ -71,7 +73,7 @@ from .streams.streams import (
71
73
  Transactions,
72
74
  TransactionsGraphql,
73
75
  )
74
- from .utils import ShopifyWrongShopNameError
76
+ from .utils import ShopifyNonRetryableErrors, ShopifyWrongShopNameError
75
77
 
76
78
 
77
79
  class ConnectionCheckTest:
@@ -79,6 +81,9 @@ class ConnectionCheckTest:
79
81
  self.config = config
80
82
  # use `Shop` as a test stream for connection check
81
83
  self.test_stream = Shop(self.config)
84
+ self.test_stream._http_client._error_handler = ShopifyConnectionCheckErrorHandler(
85
+ self.test_stream.logger, error_mapping=DEFAULT_ERROR_MAPPING | ShopifyNonRetryableErrors(self.test_stream.name)
86
+ )
82
87
  # setting `max_retries` to 0 for the stage of `check connection`,
83
88
  # because it keeps retrying for wrong shop names,
84
89
  # but it should stop immediately
@@ -115,7 +120,11 @@ class ConnectionCheckTest:
115
120
  return False, self.describe_error("index_error", shop_name, response)
116
121
  except MissingAccessTokenError:
117
122
  return False, self.describe_error("missing_token_error")
118
- except (BaseBackoffException, AirbyteTracedException) as error:
123
+ except AirbyteTracedException as error:
124
+ if error.failure_type == FailureType.config_error:
125
+ raise
126
+ return False, self.describe_error("connection_error", shop_name)
127
+ except BaseBackoffException as error:
119
128
  return False, self.describe_error("connection_error", shop_name) or str(error)
120
129
 
121
130
  def get_shop_id(self) -> str:
@@ -161,9 +170,12 @@ class SourceShopify(AbstractSource):
161
170
  # If a scheme is present, parse out the host; urlparse only fills netloc
162
171
  # when "://" is present.
163
172
  if "://" in value:
164
- parsed = urlparse(value)
173
+ try:
174
+ parsed = urlparse(value)
175
+ except ValueError:
176
+ raise ShopifyWrongShopNameError(raw)
165
177
  host = parsed.netloc
166
- if parsed.path not in ("", "/") or parsed.params or parsed.query or parsed.fragment:
178
+ if parsed.scheme not in ("http", "https") or parsed.path not in ("", "/") or parsed.params or parsed.query or parsed.fragment:
167
179
  raise ShopifyWrongShopNameError(raw)
168
180
  else:
169
181
  host, _, path = value.partition("/")
@@ -238,7 +238,7 @@ class DeletedProducts(IncrementalShopifyStream):
238
238
  query_filter = "action:destroy AND subject_type:Product"
239
239
  if stream_state and stream_state.get(self.cursor_field):
240
240
  state_value = stream_state[self.cursor_field]
241
- query_filter += f" AND created_at:>'{state_value}'"
241
+ query_filter += f" AND created_at:>='{state_value}'"
242
242
 
243
243
  variables = {
244
244
  "first": 250,
@@ -81,7 +81,7 @@ class ShopifyWrongShopNameError(AirbyteTracedException):
81
81
  """Raises the error when `Shopify Store` name is incorrect or couldn't be verified by the Shopify"""
82
82
 
83
83
  def __init__(self, url, **kwargs) -> None:
84
- self.message = f"The `Shopify Store` name is invalid or missing for `input configuration`, make sure it's valid. Details: {url}"
84
+ self.message = "The Shopify Store name is invalid or missing. Enter the store handle or its myshopify.com domain, without paths, credentials, query strings, or fragments."
85
85
  super().__init__(message=self.message, internal_message=self.message, failure_type=FailureType.config_error, **kwargs)
86
86
 
87
87