airbyte-source-shopify 3.0.13.dev202512261812__tar.gz → 3.0.14.dev202512222034__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 (70) hide show
  1. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/PKG-INFO +1 -1
  2. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/pyproject.toml +1 -1
  3. airbyte_source_shopify-3.0.14.dev202512222034/source_shopify/schemas/deleted_products.json +27 -0
  4. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/scopes.py +1 -0
  5. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/source.py +2 -0
  6. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/spec.json +0 -9
  7. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/streams/base_streams.py +1 -26
  8. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/streams/streams.py +94 -0
  9. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/README.md +0 -0
  10. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/__init__.py +0 -0
  11. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/auth.py +0 -0
  12. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/config_migrations.py +0 -0
  13. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/http_request.py +0 -0
  14. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/run.py +0 -0
  15. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/abandoned_checkouts.json +0 -0
  16. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/articles.json +0 -0
  17. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/balance_transactions.json +0 -0
  18. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/blogs.json +0 -0
  19. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/collections.json +0 -0
  20. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/collects.json +0 -0
  21. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/countries.json +0 -0
  22. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/custom_collections.json +0 -0
  23. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/customer_address.json +0 -0
  24. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/customer_journey_summary.json +0 -0
  25. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/customers.json +0 -0
  26. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/discount_codes.json +0 -0
  27. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/disputes.json +0 -0
  28. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/draft_orders.json +0 -0
  29. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/fulfillment_orders.json +0 -0
  30. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/fulfillments.json +0 -0
  31. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/inventory_items.json +0 -0
  32. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/inventory_levels.json +0 -0
  33. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/locations.json +0 -0
  34. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/metafield_articles.json +0 -0
  35. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/metafield_blogs.json +0 -0
  36. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/metafield_collections.json +0 -0
  37. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/metafield_customers.json +0 -0
  38. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/metafield_draft_orders.json +0 -0
  39. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/metafield_locations.json +0 -0
  40. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/metafield_orders.json +0 -0
  41. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/metafield_pages.json +0 -0
  42. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/metafield_product_images.json +0 -0
  43. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/metafield_product_variants.json +0 -0
  44. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/metafield_products.json +0 -0
  45. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/metafield_shops.json +0 -0
  46. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/metafield_smart_collections.json +0 -0
  47. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/order_agreements.json +0 -0
  48. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/order_refunds.json +0 -0
  49. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/order_risks.json +0 -0
  50. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/orders.json +0 -0
  51. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/pages.json +0 -0
  52. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/price_rules.json +0 -0
  53. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/product_images.json +0 -0
  54. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/product_variants.json +0 -0
  55. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/products.json +0 -0
  56. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/profile_location_groups.json +0 -0
  57. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/shop.json +0 -0
  58. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/smart_collections.json +0 -0
  59. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/tender_transactions.json +0 -0
  60. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/schemas/transactions.json +0 -0
  61. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/shopify_graphql/bulk/__init__.py +0 -0
  62. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/shopify_graphql/bulk/exceptions.py +0 -0
  63. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/shopify_graphql/bulk/job.py +0 -0
  64. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/shopify_graphql/bulk/query.py +0 -0
  65. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/shopify_graphql/bulk/record.py +0 -0
  66. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/shopify_graphql/bulk/retry.py +0 -0
  67. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/shopify_graphql/bulk/status.py +0 -0
  68. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/shopify_graphql/bulk/tools.py +0 -0
  69. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/transform.py +0 -0
  70. {airbyte_source_shopify-3.0.13.dev202512261812 → airbyte_source_shopify-3.0.14.dev202512222034}/source_shopify/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: airbyte-source-shopify
3
- Version: 3.0.13.dev202512261812
3
+ Version: 3.0.14.dev202512222034
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 = "3.0.13.dev.202512261812"
6
+ version = "3.0.14.dev.202512222034"
7
7
  name = "airbyte-source-shopify"
8
8
  description = "Source CDK implementation for Shopify."
9
9
  authors = [ "Airbyte <contact@airbyte.io>",]
@@ -0,0 +1,27 @@
1
+ {
2
+ "type": ["object", "null"],
3
+ "additionalProperties": true,
4
+ "properties": {
5
+ "id": {
6
+ "description": "The unique identifier of the deleted product.",
7
+ "type": ["null", "integer"]
8
+ },
9
+ "deleted_at": {
10
+ "description": "The date and time when the product was deleted.",
11
+ "type": ["null", "string"],
12
+ "format": "date-time"
13
+ },
14
+ "deleted_message": {
15
+ "description": "Message related to the deletion of the product.",
16
+ "type": ["null", "string"]
17
+ },
18
+ "deleted_description": {
19
+ "description": "Description of the reason for deletion.",
20
+ "type": ["null", "string"]
21
+ },
22
+ "shop_url": {
23
+ "description": "The URL of the shop where the product was listed.",
24
+ "type": ["null", "string"]
25
+ }
26
+ }
27
+ }
@@ -37,6 +37,7 @@ SCOPES_MAPPING: Mapping[str, set[str]] = {
37
37
  "MetafieldDraftOrders": ("read_draft_orders",),
38
38
  # SCOPE: read_products
39
39
  "Products": ("read_products",),
40
+ "DeletedProducts": ("read_products",),
40
41
  "MetafieldProducts": ("read_products",),
41
42
  "ProductImages": ("read_products",),
42
43
  "MetafieldProductImages": ("read_products",),
@@ -27,6 +27,7 @@ from .streams.streams import (
27
27
  CustomerAddress,
28
28
  CustomerJourneySummary,
29
29
  Customers,
30
+ DeletedProducts,
30
31
  DiscountCodes,
31
32
  Disputes,
32
33
  DraftOrders,
@@ -211,6 +212,7 @@ class SourceShopify(AbstractSource):
211
212
  PriceRules(config),
212
213
  ProductImages(config),
213
214
  Products(config),
215
+ DeletedProducts(config),
214
216
  ProductVariants(config),
215
217
  Shop(config),
216
218
  SmartCollections(config),
@@ -119,15 +119,6 @@
119
119
  "default": 100000,
120
120
  "minimum": 15000,
121
121
  "maximum": 1000000
122
- },
123
- "lookback_window_in_days": {
124
- "type": "integer",
125
- "title": "Lookback Window (in days)",
126
- "description": "The number of days to look back for records that may have been missed due to race conditions or late-arriving data. This is useful for ensuring data completeness in incremental syncs. Setting this to 1 or more will re-fetch records from the specified number of days before the last sync state.",
127
- "default": 0,
128
- "minimum": 0,
129
- "maximum": 30,
130
- "order": 7
131
122
  }
132
123
  }
133
124
  },
@@ -209,24 +209,6 @@ class IncrementalShopifyStream(ShopifyStream, ABC):
209
209
  current_state_value = current_stream_state.get(self.cursor_field) or self.default_state_comparison_value
210
210
  return {self.cursor_field: max(last_record_value, current_state_value)}
211
211
 
212
- def _apply_lookback_window(self, state_value: str) -> str:
213
- """
214
- Apply the lookback window to the state value by subtracting the configured number of days.
215
- This helps capture records that may have been missed due to race conditions or late-arriving data.
216
- """
217
- lookback_days = self.config.get("lookback_window_in_days", 0)
218
- if lookback_days > 0:
219
- state_datetime = pdm.parse(state_value)
220
- adjusted_datetime = state_datetime.subtract(days=lookback_days)
221
- # Ensure we don't go before the configured start_date
222
- start_date = self.config.get("start_date")
223
- if start_date:
224
- start_datetime = pdm.parse(start_date)
225
- if adjusted_datetime < start_datetime:
226
- adjusted_datetime = start_datetime
227
- return adjusted_datetime.to_rfc3339_string()
228
- return state_value
229
-
230
212
  @stream_state_cache.cache_stream_state
231
213
  def request_params(
232
214
  self, stream_state: Optional[Mapping[str, Any]] = None, next_page_token: Optional[Mapping[str, Any]] = None, **kwargs
@@ -236,11 +218,7 @@ class IncrementalShopifyStream(ShopifyStream, ABC):
236
218
  if not next_page_token:
237
219
  params["order"] = f"{self.order_field} asc"
238
220
  if stream_state:
239
- state_value = stream_state.get(self.cursor_field)
240
- # Apply lookback window to go back N days from the state
241
- if state_value and self.filter_field != "since_id":
242
- state_value = self._apply_lookback_window(state_value)
243
- params[self.filter_field] = state_value
221
+ params[self.filter_field] = stream_state.get(self.cursor_field)
244
222
  return params
245
223
 
246
224
  def track_checkpoint_cursor(self, record_value: Union[str, int], filter_record_value: Optional[str] = None) -> None:
@@ -844,9 +822,6 @@ class IncrementalShopifyGraphQlBulkStream(IncrementalShopifyStream):
844
822
  def stream_slices(self, stream_state: Optional[Mapping[str, Any]] = None, **kwargs) -> Iterable[Optional[Mapping[str, Any]]]:
845
823
  if self.filter_field:
846
824
  state = self._get_state_value(stream_state)
847
- # Apply lookback window only when we have a stream state (not on first sync)
848
- if stream_state:
849
- state = self._apply_lookback_window(state)
850
825
  start = pdm.parse(state)
851
826
  end = pdm.now()
852
827
  while start < end:
@@ -135,6 +135,100 @@ class Products(IncrementalShopifyGraphQlBulkStream):
135
135
  bulk_query: Product = Product
136
136
 
137
137
 
138
+ class DeletedProducts(IncrementalShopifyStream):
139
+ """
140
+ Stream for fetching deleted products using the Shopify GraphQL Events API.
141
+ This stream queries events with action:destroy and subject_type:Product to get deleted product records.
142
+ https://shopify.dev/docs/api/admin-graphql/latest/queries/events
143
+ """
144
+
145
+ data_field = "graphql"
146
+ cursor_field = "deleted_at"
147
+ http_method = "POST"
148
+ filter_field = None
149
+
150
+ _page_cursor: Optional[str] = None
151
+
152
+ EVENTS_QUERY = """
153
+ query GetDeletedProductEvents($first: Int!, $after: String, $query: String) {
154
+ events(first: $first, after: $after, query: $query, sortKey: CREATED_AT) {
155
+ pageInfo {
156
+ hasNextPage
157
+ endCursor
158
+ }
159
+ nodes {
160
+ ... on BasicEvent {
161
+ id
162
+ createdAt
163
+ message
164
+ subjectId
165
+ subjectType
166
+ }
167
+ }
168
+ }
169
+ }
170
+ """
171
+
172
+ @property
173
+ def url_base(self) -> str:
174
+ return f"https://{self.config['shop']}.myshopify.com/admin/api/{self.api_version}/graphql.json"
175
+
176
+ def path(self, **kwargs) -> str:
177
+ return ""
178
+
179
+ def request_params(
180
+ self, stream_state: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None, **kwargs
181
+ ) -> MutableMapping[str, Any]:
182
+ return {}
183
+
184
+ def request_body_json(
185
+ self,
186
+ stream_state: Optional[Mapping[str, Any]] = None,
187
+ stream_slice: Optional[Mapping[str, Any]] = None,
188
+ next_page_token: Optional[Mapping[str, Any]] = None,
189
+ ) -> Optional[Mapping[str, Any]]:
190
+ query_filter = "action:destroy AND subject_type:Product"
191
+ if stream_state and stream_state.get(self.cursor_field):
192
+ state_value = stream_state[self.cursor_field]
193
+ query_filter += f" AND created_at:>'{state_value}'"
194
+
195
+ variables = {
196
+ "first": 250,
197
+ "query": query_filter,
198
+ }
199
+
200
+ if next_page_token and next_page_token.get("cursor"):
201
+ variables["after"] = next_page_token["cursor"]
202
+
203
+ return {
204
+ "query": self.EVENTS_QUERY,
205
+ "variables": variables,
206
+ }
207
+
208
+ def next_page_token(self, response: requests.Response) -> Optional[Mapping[str, Any]]:
209
+ json_response = response.json()
210
+ page_info = json_response.get("data", {}).get("events", {}).get("pageInfo", {})
211
+ if page_info.get("hasNextPage"):
212
+ return {"cursor": page_info.get("endCursor")}
213
+ return None
214
+
215
+ def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapping]:
216
+ json_response = response.json()
217
+ events = json_response.get("data", {}).get("events", {}).get("nodes", [])
218
+ for event in events:
219
+ if event.get("subjectType") == "Product" and event.get("subjectId"):
220
+ subject_id = event.get("subjectId", "")
221
+ product_id = int(subject_id.split("/")[-1]) if "/" in subject_id else None
222
+ if product_id:
223
+ yield {
224
+ "id": product_id,
225
+ "deleted_at": event.get("createdAt"),
226
+ "deleted_message": event.get("message"),
227
+ "deleted_description": None,
228
+ "shop_url": self.config.get("shop"),
229
+ }
230
+
231
+
138
232
  class MetafieldProducts(IncrementalShopifyGraphQlBulkStream):
139
233
  parent_stream_class = Products
140
234
  bulk_query: MetafieldProduct = MetafieldProduct