airbyte-source-shopify 3.0.14.dev202512222245__py3-none-any.whl → 3.1.0.dev202512181916__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: airbyte-source-shopify
3
- Version: 3.0.14.dev202512222245
3
+ Version: 3.1.0.dev202512181916
4
4
  Summary: Source CDK implementation for Shopify.
5
5
  Home-page: https://airbyte.com
6
6
  License: ELv2
@@ -14,7 +14,6 @@ source_shopify/schemas/custom_collections.json,sha256=ElDY1y_G_VFPOGr9ipU022AZDW
14
14
  source_shopify/schemas/customer_address.json,sha256=kLKylul_xK7eO_aduvDddj078k8aQLM4JtUEpa5Tp_8,2628
15
15
  source_shopify/schemas/customer_journey_summary.json,sha256=GlZmIqFeR1x84DwgwMo28HQ8QseImkhvYxLSNRkUmwg,12733
16
16
  source_shopify/schemas/customers.json,sha256=pywejBEtvMnRHvvnJQhfA8NlCBjELv7XuMlKdpS4cZI,10233
17
- source_shopify/schemas/deleted_products.json,sha256=7TbqrABNDg9ZFAeLJCPe2Z5jtk9QGbJDuoSKofGd1lg,773
18
17
  source_shopify/schemas/discount_codes.json,sha256=YxGptdpjXwM46fmzs5zoi3m96xAWKXrfllC_oIZW0vo,3730
19
18
  source_shopify/schemas/disputes.json,sha256=Uvzy1Gi954cLxij6H0Vbz0OWS6GS10YaJI4DJfY2x8I,1775
20
19
  source_shopify/schemas/draft_orders.json,sha256=3NaZt9Qk4mY8VO9pB5UBzw3GZibL8MvI0z_tlnMsdBI,24211
@@ -50,7 +49,7 @@ source_shopify/schemas/shop.json,sha256=vEGiTvEYX7qnMq06MRVBycqih49h49xjTNC6gJux
50
49
  source_shopify/schemas/smart_collections.json,sha256=kv7dINsvgzJ0RyKfFNKjU0apdNDXwQaHfnNZfQsshcU,2009
51
50
  source_shopify/schemas/tender_transactions.json,sha256=U8fdT-eflycEPzYSpBDiB0lp9wxmJHgioHTrICflh78,2006
52
51
  source_shopify/schemas/transactions.json,sha256=vbwscH3UcAtbSsC70mBka4oNaFR4S3S6IFBmzR7t37U,10226
53
- source_shopify/scopes.py,sha256=78f9QL3PJZ9UDx1gIWzNwx5fYJE9OB3vPi9RahB_kFw,6533
52
+ source_shopify/scopes.py,sha256=N0njfMHn3Q1AQXuTj5VfjQOio10jaDarpC_oLYnWvqc,6490
54
53
  source_shopify/shopify_graphql/bulk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
54
  source_shopify/shopify_graphql/bulk/exceptions.py,sha256=4dj7Za4xIfwL-zf8joT9svF_RSoGlE3GviMiIl1e1rs,2532
56
55
  source_shopify/shopify_graphql/bulk/job.py,sha256=c3Cg70_Io9jTD-rU-5MvjHaPmJCtcpeqEYnRtFECGOo,28673
@@ -59,13 +58,13 @@ source_shopify/shopify_graphql/bulk/record.py,sha256=X6VGngugv7a_S8UEeDo121BkdCV
59
58
  source_shopify/shopify_graphql/bulk/retry.py,sha256=R5rSJJE8D5zcj6mN-OmmNO2aFZEIdjAlWclDDVW5KPI,2626
60
59
  source_shopify/shopify_graphql/bulk/status.py,sha256=RmuQ2XsYL3iRCpVGxea9F1wXGmbwasDCSXjaTyL4LMA,328
61
60
  source_shopify/shopify_graphql/bulk/tools.py,sha256=nUQ2ZmPTKJNJdfLToR6KJtLKcJFCChSifkAOvwg0Vss,4065
62
- source_shopify/source.py,sha256=oikoM-VPNk62zlmeAQR59PMxfuXq2s42N7zaqLM6_lo,8575
61
+ source_shopify/source.py,sha256=txb3wIm-3xXd8-5QLSeu2TeHBSnppwy5PEIOEl40mVw,8517
63
62
  source_shopify/spec.json,sha256=ITYWiQ-NrI5VISk5qmUQhp9ChUE2FV18d8xzVzPwvAg,6144
64
63
  source_shopify/streams/base_streams.py,sha256=k_4uLaLADLRTUcSmP8uA_830uuzRvnqUaCVGcb0Zpd8,42625
65
- source_shopify/streams/streams.py,sha256=ZP8hxPN-MdeGJx9Q0QNruwCd0GE6ZyPCOMcG931Z2C0,18095
64
+ source_shopify/streams/streams.py,sha256=le0urCpcjNcSv2hvoD7Cj4kFprmCZmfpP6418wY5LFI,16028
66
65
  source_shopify/transform.py,sha256=mn0htL812_90zc_YszGQa0hHcIZQpYYdmk8IqpZm5TI,4685
67
66
  source_shopify/utils.py,sha256=DSqEchu-MQJ7zust7CNfqOkGIv9OSR-5UUsuD-bsDa8,16224
68
- airbyte_source_shopify-3.0.14.dev202512222245.dist-info/METADATA,sha256=YBnDLoD-FjJxCLN22sJOKrtXA49mu7doWIJhiYcWr3g,5314
69
- airbyte_source_shopify-3.0.14.dev202512222245.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
70
- airbyte_source_shopify-3.0.14.dev202512222245.dist-info/entry_points.txt,sha256=SyTwKSsPk9MCdPf01saWpnp8hcmZOgBssVcSIvMbBeQ,57
71
- airbyte_source_shopify-3.0.14.dev202512222245.dist-info/RECORD,,
67
+ airbyte_source_shopify-3.1.0.dev202512181916.dist-info/METADATA,sha256=XVREhXQzCNgPLMzNWLMCGmb8oLH9O383_FPq6MwxQ9I,5313
68
+ airbyte_source_shopify-3.1.0.dev202512181916.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
69
+ airbyte_source_shopify-3.1.0.dev202512181916.dist-info/entry_points.txt,sha256=SyTwKSsPk9MCdPf01saWpnp8hcmZOgBssVcSIvMbBeQ,57
70
+ airbyte_source_shopify-3.1.0.dev202512181916.dist-info/RECORD,,
source_shopify/scopes.py CHANGED
@@ -37,7 +37,6 @@ 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",),
41
40
  "MetafieldProducts": ("read_products",),
42
41
  "ProductImages": ("read_products",),
43
42
  "MetafieldProductImages": ("read_products",),
source_shopify/source.py CHANGED
@@ -27,7 +27,6 @@ from .streams.streams import (
27
27
  CustomerAddress,
28
28
  CustomerJourneySummary,
29
29
  Customers,
30
- DeletedProducts,
31
30
  DiscountCodes,
32
31
  Disputes,
33
32
  DraftOrders,
@@ -212,7 +211,6 @@ class SourceShopify(AbstractSource):
212
211
  PriceRules(config),
213
212
  ProductImages(config),
214
213
  Products(config),
215
- DeletedProducts(config),
216
214
  ProductVariants(config),
217
215
  Shop(config),
218
216
  SmartCollections(config),
@@ -135,100 +135,6 @@ 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
-
232
138
  class MetafieldProducts(IncrementalShopifyGraphQlBulkStream):
233
139
  parent_stream_class = Products
234
140
  bulk_query: MetafieldProduct = MetafieldProduct
@@ -291,18 +197,47 @@ class MetafieldSmartCollections(MetafieldShopifySubstream):
291
197
 
292
198
  class Collects(IncrementalShopifyStream):
293
199
  """
294
- Collects stream does not support Incremental Refresh based on datetime fields, only `since_id` is supported:
295
- https://shopify.dev/docs/admin-api/rest/reference/products/collect
200
+ Collects stream uses client-side incremental sync because the Shopify REST API
201
+ does not support filtering by updated_at for this endpoint - only `since_id` is available.
202
+ https://shopify.dev/docs/api/admin-rest/latest/resources/collect
296
203
 
297
- The Collect stream is the link between Products and Collections, if the Collection is created for Products,
298
- the `collect` record is created, it's reasonable to Full Refresh all collects. As for Incremental refresh -
299
- we would use the since_id specificaly for this stream.
204
+ The Collect stream is the link between Products and Collections. To capture both new records
205
+ AND updates to existing records, we fetch all records and filter client-side based on updated_at.
206
+
207
+ Breaking change from previous versions: cursor_field changed from `id` to `updated_at`.
300
208
  """
301
209
 
302
210
  data_field = "collects"
303
- cursor_field = "id"
211
+ cursor_field = "updated_at"
304
212
  order_field = "id"
305
- filter_field = "since_id"
213
+
214
+ def request_params(
215
+ self, stream_state: Optional[Mapping[str, Any]] = None, next_page_token: Optional[Mapping[str, Any]] = None, **kwargs
216
+ ) -> MutableMapping[str, Any]:
217
+ """
218
+ Override to implement client-side incremental sync.
219
+ We fetch all records (no server-side filtering) and filter client-side using filter_records_newer_than_state.
220
+ The Shopify Collects API only supports since_id filtering, which cannot capture updates to existing records.
221
+ """
222
+ params = {"limit": self.limit}
223
+ if next_page_token:
224
+ params.update(**next_page_token)
225
+ else:
226
+ params["order"] = f"{self.order_field} asc"
227
+ return params
228
+
229
+ def read_records(
230
+ self,
231
+ stream_state: Optional[Mapping[str, Any]] = None,
232
+ stream_slice: Optional[Mapping[str, Any]] = None,
233
+ **kwargs,
234
+ ) -> Iterable[Mapping[str, Any]]:
235
+ """
236
+ Override to apply client-side filtering based on updated_at cursor.
237
+ This ensures we capture both new records and updates to existing records.
238
+ """
239
+ records = super().read_records(stream_state=stream_state, stream_slice=stream_slice, **kwargs)
240
+ yield from self.filter_records_newer_than_state(stream_state, records)
306
241
 
307
242
 
308
243
  class Collections(IncrementalShopifyGraphQlBulkStream):
@@ -1,27 +0,0 @@
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
- }