airbyte-source-shopify 3.1.0__tar.gz → 3.1.0.dev202512181928__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.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/PKG-INFO +1 -1
  2. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/pyproject.toml +1 -1
  3. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/scopes.py +0 -1
  4. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/source.py +0 -2
  5. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/streams/streams.py +36 -111
  6. airbyte_source_shopify-3.1.0/source_shopify/schemas/deleted_products.json +0 -27
  7. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/README.md +0 -0
  8. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/__init__.py +0 -0
  9. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/auth.py +0 -0
  10. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/config_migrations.py +0 -0
  11. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/http_request.py +0 -0
  12. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/run.py +0 -0
  13. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/abandoned_checkouts.json +0 -0
  14. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/articles.json +0 -0
  15. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/balance_transactions.json +0 -0
  16. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/blogs.json +0 -0
  17. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/collections.json +0 -0
  18. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/collects.json +0 -0
  19. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/countries.json +0 -0
  20. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/custom_collections.json +0 -0
  21. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/customer_address.json +0 -0
  22. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/customer_journey_summary.json +0 -0
  23. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/customers.json +0 -0
  24. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/discount_codes.json +0 -0
  25. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/disputes.json +0 -0
  26. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/draft_orders.json +0 -0
  27. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/fulfillment_orders.json +0 -0
  28. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/fulfillments.json +0 -0
  29. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/inventory_items.json +0 -0
  30. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/inventory_levels.json +0 -0
  31. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/locations.json +0 -0
  32. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/metafield_articles.json +0 -0
  33. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/metafield_blogs.json +0 -0
  34. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/metafield_collections.json +0 -0
  35. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/metafield_customers.json +0 -0
  36. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/metafield_draft_orders.json +0 -0
  37. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/metafield_locations.json +0 -0
  38. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/metafield_orders.json +0 -0
  39. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/metafield_pages.json +0 -0
  40. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/metafield_product_images.json +0 -0
  41. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/metafield_product_variants.json +0 -0
  42. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/metafield_products.json +0 -0
  43. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/metafield_shops.json +0 -0
  44. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/metafield_smart_collections.json +0 -0
  45. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/order_agreements.json +0 -0
  46. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/order_refunds.json +0 -0
  47. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/order_risks.json +0 -0
  48. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/orders.json +0 -0
  49. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/pages.json +0 -0
  50. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/price_rules.json +0 -0
  51. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/product_images.json +0 -0
  52. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/product_variants.json +0 -0
  53. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/products.json +0 -0
  54. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/profile_location_groups.json +0 -0
  55. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/shop.json +0 -0
  56. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/smart_collections.json +0 -0
  57. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/tender_transactions.json +0 -0
  58. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/schemas/transactions.json +0 -0
  59. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/shopify_graphql/bulk/__init__.py +0 -0
  60. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/shopify_graphql/bulk/exceptions.py +0 -0
  61. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/shopify_graphql/bulk/job.py +0 -0
  62. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/shopify_graphql/bulk/query.py +0 -0
  63. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/shopify_graphql/bulk/record.py +0 -0
  64. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/shopify_graphql/bulk/retry.py +0 -0
  65. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/shopify_graphql/bulk/status.py +0 -0
  66. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/shopify_graphql/bulk/tools.py +0 -0
  67. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/spec.json +0 -0
  68. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/streams/base_streams.py +0 -0
  69. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/source_shopify/transform.py +0 -0
  70. {airbyte_source_shopify-3.1.0 → airbyte_source_shopify-3.1.0.dev202512181928}/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.1.0
3
+ Version: 3.1.0.dev202512181928
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.1.0"
6
+ version = "3.1.0.dev.202512181928"
7
7
  name = "airbyte-source-shopify"
8
8
  description = "Source CDK implementation for Shopify."
9
9
  authors = [ "Airbyte <contact@airbyte.io>",]
@@ -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",),
@@ -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,110 +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
- Note: This stream extends IncrementalShopifyStream (REST base class) rather than IncrementalShopifyGraphQlBulkStream
145
- because it uses Shopify's standard GraphQL Events API, NOT the Bulk Operations API (bulkOperationRunQuery).
146
- The Events API has a fundamentally different architecture:
147
- - Uses immediate queries with cursor pagination (not async job creation + file download)
148
- - Returns results in response.data.events.nodes (not JSONL files)
149
- - Requires different URL endpoint (/graphql.json), request format (POST with query in body), and pagination logic
150
-
151
- Therefore, url_base, path, request_params, next_page_token, and parse_response are overridden to accommodate
152
- the GraphQL request/response structure while maintaining incremental sync capabilities from the base class.
153
- """
154
-
155
- data_field = "graphql"
156
- cursor_field = "deleted_at"
157
- http_method = "POST"
158
- filter_field = None
159
-
160
- _page_cursor: Optional[str] = None
161
-
162
- EVENTS_QUERY = """
163
- query GetDeletedProductEvents($first: Int!, $after: String, $query: String) {
164
- events(first: $first, after: $after, query: $query, sortKey: CREATED_AT) {
165
- pageInfo {
166
- hasNextPage
167
- endCursor
168
- }
169
- nodes {
170
- ... on BasicEvent {
171
- id
172
- createdAt
173
- message
174
- subjectId
175
- subjectType
176
- }
177
- }
178
- }
179
- }
180
- """
181
-
182
- @property
183
- def url_base(self) -> str:
184
- return f"https://{self.config['shop']}.myshopify.com/admin/api/{self.api_version}/graphql.json"
185
-
186
- def path(self, **kwargs) -> str:
187
- return ""
188
-
189
- def request_params(
190
- self, stream_state: Mapping[str, Any] = None, next_page_token: Mapping[str, Any] = None, **kwargs
191
- ) -> MutableMapping[str, Any]:
192
- return {}
193
-
194
- def request_body_json(
195
- self,
196
- stream_state: Optional[Mapping[str, Any]] = None,
197
- stream_slice: Optional[Mapping[str, Any]] = None,
198
- next_page_token: Optional[Mapping[str, Any]] = None,
199
- ) -> Optional[Mapping[str, Any]]:
200
- query_filter = "action:destroy AND subject_type:Product"
201
- if stream_state and stream_state.get(self.cursor_field):
202
- state_value = stream_state[self.cursor_field]
203
- query_filter += f" AND created_at:>'{state_value}'"
204
-
205
- variables = {
206
- "first": 250,
207
- "query": query_filter,
208
- }
209
-
210
- if next_page_token and next_page_token.get("cursor"):
211
- variables["after"] = next_page_token["cursor"]
212
-
213
- return {
214
- "query": self.EVENTS_QUERY,
215
- "variables": variables,
216
- }
217
-
218
- def next_page_token(self, response: requests.Response) -> Optional[Mapping[str, Any]]:
219
- json_response = response.json()
220
- page_info = json_response.get("data", {}).get("events", {}).get("pageInfo", {})
221
- if page_info.get("hasNextPage"):
222
- return {"cursor": page_info.get("endCursor")}
223
- return None
224
-
225
- def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapping]:
226
- json_response = response.json()
227
- events = json_response.get("data", {}).get("events", {}).get("nodes", [])
228
- for event in events:
229
- if event.get("subjectType") == "PRODUCT" and event.get("subjectId"):
230
- subject_id = event.get("subjectId", "")
231
- product_id = int(subject_id.split("/")[-1]) if "/" in subject_id else None
232
- if product_id:
233
- yield {
234
- "id": product_id,
235
- "deleted_at": event.get("createdAt"),
236
- "deleted_message": event.get("message"),
237
- "deleted_description": None,
238
- "shop_url": self.config.get("shop"),
239
- }
240
-
241
-
242
138
  class MetafieldProducts(IncrementalShopifyGraphQlBulkStream):
243
139
  parent_stream_class = Products
244
140
  bulk_query: MetafieldProduct = MetafieldProduct
@@ -301,18 +197,47 @@ class MetafieldSmartCollections(MetafieldShopifySubstream):
301
197
 
302
198
  class Collects(IncrementalShopifyStream):
303
199
  """
304
- Collects stream does not support Incremental Refresh based on datetime fields, only `since_id` is supported:
305
- 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
306
203
 
307
- The Collect stream is the link between Products and Collections, if the Collection is created for Products,
308
- the `collect` record is created, it's reasonable to Full Refresh all collects. As for Incremental refresh -
309
- 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`.
310
208
  """
311
209
 
312
210
  data_field = "collects"
313
- cursor_field = "id"
211
+ cursor_field = "updated_at"
314
212
  order_field = "id"
315
- 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)
316
241
 
317
242
 
318
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
- }