airbyte-source-shopify 3.1.0__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.
- {airbyte_source_shopify-3.1.0.dist-info → airbyte_source_shopify-3.1.0.dev202512181916.dist-info}/METADATA +1 -1
- {airbyte_source_shopify-3.1.0.dist-info → airbyte_source_shopify-3.1.0.dev202512181916.dist-info}/RECORD +7 -8
- source_shopify/scopes.py +0 -1
- source_shopify/source.py +0 -2
- source_shopify/streams/streams.py +36 -111
- source_shopify/schemas/deleted_products.json +0 -27
- {airbyte_source_shopify-3.1.0.dist-info → airbyte_source_shopify-3.1.0.dev202512181916.dist-info}/WHEEL +0 -0
- {airbyte_source_shopify-3.1.0.dist-info → airbyte_source_shopify-3.1.0.dev202512181916.dist-info}/entry_points.txt +0 -0
|
@@ -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=
|
|
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=
|
|
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=
|
|
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.1.0.dist-info/METADATA,sha256=
|
|
69
|
-
airbyte_source_shopify-3.1.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
70
|
-
airbyte_source_shopify-3.1.0.dist-info/entry_points.txt,sha256=SyTwKSsPk9MCdPf01saWpnp8hcmZOgBssVcSIvMbBeQ,57
|
|
71
|
-
airbyte_source_shopify-3.1.0.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,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
|
|
305
|
-
|
|
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
|
|
308
|
-
|
|
309
|
-
|
|
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 = "
|
|
211
|
+
cursor_field = "updated_at"
|
|
314
212
|
order_field = "id"
|
|
315
|
-
|
|
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
|
-
}
|
|
File without changes
|
|
File without changes
|