airbyte-source-shopify 3.0.13.dev202512232215__py3-none-any.whl → 3.0.14.dev202512192257__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.13.dev202512232215
3
+ Version: 3.0.14.dev202512192257
4
4
  Summary: Source CDK implementation for Shopify.
5
5
  Home-page: https://airbyte.com
6
6
  License: ELv2
@@ -14,6 +14,7 @@ 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
17
18
  source_shopify/schemas/discount_codes.json,sha256=YxGptdpjXwM46fmzs5zoi3m96xAWKXrfllC_oIZW0vo,3730
18
19
  source_shopify/schemas/disputes.json,sha256=Uvzy1Gi954cLxij6H0Vbz0OWS6GS10YaJI4DJfY2x8I,1775
19
20
  source_shopify/schemas/draft_orders.json,sha256=3NaZt9Qk4mY8VO9pB5UBzw3GZibL8MvI0z_tlnMsdBI,24211
@@ -49,7 +50,7 @@ source_shopify/schemas/shop.json,sha256=vEGiTvEYX7qnMq06MRVBycqih49h49xjTNC6gJux
49
50
  source_shopify/schemas/smart_collections.json,sha256=kv7dINsvgzJ0RyKfFNKjU0apdNDXwQaHfnNZfQsshcU,2009
50
51
  source_shopify/schemas/tender_transactions.json,sha256=U8fdT-eflycEPzYSpBDiB0lp9wxmJHgioHTrICflh78,2006
51
52
  source_shopify/schemas/transactions.json,sha256=vbwscH3UcAtbSsC70mBka4oNaFR4S3S6IFBmzR7t37U,10226
52
- source_shopify/scopes.py,sha256=N0njfMHn3Q1AQXuTj5VfjQOio10jaDarpC_oLYnWvqc,6490
53
+ source_shopify/scopes.py,sha256=78f9QL3PJZ9UDx1gIWzNwx5fYJE9OB3vPi9RahB_kFw,6533
53
54
  source_shopify/shopify_graphql/bulk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
55
  source_shopify/shopify_graphql/bulk/exceptions.py,sha256=4dj7Za4xIfwL-zf8joT9svF_RSoGlE3GviMiIl1e1rs,2532
55
56
  source_shopify/shopify_graphql/bulk/job.py,sha256=c3Cg70_Io9jTD-rU-5MvjHaPmJCtcpeqEYnRtFECGOo,28673
@@ -58,13 +59,13 @@ source_shopify/shopify_graphql/bulk/record.py,sha256=X6VGngugv7a_S8UEeDo121BkdCV
58
59
  source_shopify/shopify_graphql/bulk/retry.py,sha256=R5rSJJE8D5zcj6mN-OmmNO2aFZEIdjAlWclDDVW5KPI,2626
59
60
  source_shopify/shopify_graphql/bulk/status.py,sha256=RmuQ2XsYL3iRCpVGxea9F1wXGmbwasDCSXjaTyL4LMA,328
60
61
  source_shopify/shopify_graphql/bulk/tools.py,sha256=nUQ2ZmPTKJNJdfLToR6KJtLKcJFCChSifkAOvwg0Vss,4065
61
- source_shopify/source.py,sha256=txb3wIm-3xXd8-5QLSeu2TeHBSnppwy5PEIOEl40mVw,8517
62
+ source_shopify/source.py,sha256=oikoM-VPNk62zlmeAQR59PMxfuXq2s42N7zaqLM6_lo,8575
62
63
  source_shopify/spec.json,sha256=ITYWiQ-NrI5VISk5qmUQhp9ChUE2FV18d8xzVzPwvAg,6144
63
64
  source_shopify/streams/base_streams.py,sha256=k_4uLaLADLRTUcSmP8uA_830uuzRvnqUaCVGcb0Zpd8,42625
64
- source_shopify/streams/streams.py,sha256=-f0C9foKExBp7WSJi09-DByfkz2EWoLFOOB8rBoF8Xc,14818
65
+ source_shopify/streams/streams.py,sha256=F_otpKWbwn4Xn_wYVIdPF1MVjaOGke3BlvPG336Ip28,18105
65
66
  source_shopify/transform.py,sha256=mn0htL812_90zc_YszGQa0hHcIZQpYYdmk8IqpZm5TI,4685
66
67
  source_shopify/utils.py,sha256=DSqEchu-MQJ7zust7CNfqOkGIv9OSR-5UUsuD-bsDa8,16224
67
- airbyte_source_shopify-3.0.13.dev202512232215.dist-info/METADATA,sha256=5HFMIblhXaOlVKoqHULi8aHJ22gbUv7vwQu9b_21Zn0,5314
68
- airbyte_source_shopify-3.0.13.dev202512232215.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
69
- airbyte_source_shopify-3.0.13.dev202512232215.dist-info/entry_points.txt,sha256=SyTwKSsPk9MCdPf01saWpnp8hcmZOgBssVcSIvMbBeQ,57
70
- airbyte_source_shopify-3.0.13.dev202512232215.dist-info/RECORD,,
68
+ airbyte_source_shopify-3.0.14.dev202512192257.dist-info/METADATA,sha256=YtLc8y1hLx7E1BjYCj3XvskFi68Pvkzl5qHOnaf4nkU,5314
69
+ airbyte_source_shopify-3.0.14.dev202512192257.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
70
+ airbyte_source_shopify-3.0.14.dev202512192257.dist-info/entry_points.txt,sha256=SyTwKSsPk9MCdPf01saWpnp8hcmZOgBssVcSIvMbBeQ,57
71
+ airbyte_source_shopify-3.0.14.dev202512192257.dist-info/RECORD,,
@@ -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
+ }
source_shopify/scopes.py CHANGED
@@ -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",),
source_shopify/source.py CHANGED
@@ -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),
@@ -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.config['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
@@ -407,10 +501,7 @@ class Countries(HttpSubStream, FullRefreshShopifyGraphQlBulkStream):
407
501
  stream_slice: Optional[Mapping[str, Any]] = None,
408
502
  next_page_token: Optional[Mapping[str, Any]] = None,
409
503
  ) -> Optional[Mapping[str, Any]]:
410
- profile_location_groups = stream_slice["parent"]["profile_location_groups"]
411
- if not profile_location_groups:
412
- return None
413
- location_group_id = profile_location_groups[0]["locationGroup"]["id"]
504
+ location_group_id = stream_slice["parent"]["profile_location_groups"][0]["locationGroup"]["id"]
414
505
  return {
415
506
  "query": self.query(location_group_id=location_group_id, location_group_zones_cursor=self._sub_page_cursor).get(
416
507
  query_args={