ingestr 0.7.7__py3-none-any.whl → 0.8.1__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,19 +1,17 @@
1
1
  """Fetches Shopify Orders and Products."""
2
2
 
3
- from typing import Any, Dict, Iterable, Optional
3
+ from typing import Iterable, Optional
4
4
 
5
5
  import dlt
6
- from dlt.common import jsonpath as jp
7
6
  from dlt.common import pendulum
8
7
  from dlt.common.time import ensure_pendulum_datetime
9
8
  from dlt.common.typing import TAnyDateTime, TDataItem
10
9
  from dlt.sources import DltResource
11
10
 
12
- from .helpers import ShopifyApi, ShopifyPartnerApi, TOrderStatus
11
+ from .helpers import ShopifyApi, ShopifyGraphQLApi, TOrderStatus
13
12
  from .settings import (
14
13
  DEFAULT_API_VERSION,
15
14
  DEFAULT_ITEMS_PER_PAGE,
16
- DEFAULT_PARTNER_API_VERSION,
17
15
  FIRST_DAY_OF_MILLENNIUM,
18
16
  )
19
17
 
@@ -63,8 +61,94 @@ def shopify_source(
63
61
  created_at_min_obj = ensure_pendulum_datetime(created_at_min)
64
62
 
65
63
  # define resources
66
- @dlt.resource(primary_key="id", write_disposition="merge")
67
- def products(
64
+ @dlt.resource(
65
+ primary_key="id",
66
+ write_disposition="merge",
67
+ columns={
68
+ "body_html": {
69
+ "data_type": "text",
70
+ "nullable": True,
71
+ "description": "A description of the product. Supports HTML formatting.",
72
+ },
73
+ "created_at": {
74
+ "data_type": "timestamp",
75
+ "nullable": False,
76
+ "description": "The date and time (ISO 8601 format) when the product was created.",
77
+ },
78
+ "handle": {
79
+ "data_type": "text",
80
+ "nullable": False,
81
+ "description": "A unique human-friendly string for the product. Automatically generated from the product's title. Used by the Liquid templating language to refer to objects.",
82
+ },
83
+ "id": {
84
+ "data_type": "bigint",
85
+ "nullable": False,
86
+ "primary_key": True,
87
+ "description": "An unsigned 64-bit integer that's used as a unique identifier for the product.",
88
+ },
89
+ "images": {
90
+ "data_type": "complex",
91
+ "nullable": True,
92
+ "description": "A list of product image objects, each one representing an image associated with the product.",
93
+ },
94
+ "options": {
95
+ "data_type": "complex",
96
+ "nullable": True,
97
+ "description": "The custom product properties. For example, Size, Color, and Material.",
98
+ },
99
+ "product_type": {
100
+ "data_type": "text",
101
+ "nullable": True,
102
+ "description": "A categorization for the product used for filtering and searching products.",
103
+ },
104
+ "published_at": {
105
+ "data_type": "timestamp",
106
+ "nullable": True,
107
+ "description": "The date and time (ISO 8601 format) when the product was published.",
108
+ },
109
+ "published_scope": {
110
+ "data_type": "text",
111
+ "nullable": True,
112
+ "description": "Whether the product is published to the Point of Sale channel.",
113
+ },
114
+ "status": {
115
+ "data_type": "text",
116
+ "nullable": False,
117
+ "description": "The status of the product.",
118
+ },
119
+ "tags": {
120
+ "data_type": "text",
121
+ "nullable": True,
122
+ "description": "A string of comma-separated tags used for filtering and search.",
123
+ },
124
+ "template_suffix": {
125
+ "data_type": "text",
126
+ "nullable": True,
127
+ "description": "The suffix of the Liquid template used for the product page.",
128
+ },
129
+ "title": {
130
+ "data_type": "text",
131
+ "nullable": False,
132
+ "description": "The name of the product.",
133
+ },
134
+ "updated_at": {
135
+ "data_type": "timestamp",
136
+ "nullable": True,
137
+ "description": "The date and time (ISO 8601 format) when the product was last modified.",
138
+ },
139
+ "variants": {
140
+ "data_type": "complex",
141
+ "nullable": True,
142
+ "description": "An array of product variants, each representing a different version of the product.",
143
+ },
144
+ "vendor": {
145
+ "data_type": "text",
146
+ "nullable": True,
147
+ "description": "The name of the product's vendor.",
148
+ },
149
+ },
150
+ )
151
+ def products_legacy(
68
152
  updated_at: dlt.sources.incremental[
69
153
  pendulum.DateTime
70
154
  ] = dlt.sources.incremental(
@@ -95,7 +179,423 @@ def shopify_source(
95
179
  params["updated_at_max"] = updated_at.end_value.isoformat()
96
180
  yield from client.get_pages("products", params)
97
181
 
98
- @dlt.resource(primary_key="id", write_disposition="merge")
182
+ @dlt.resource(
183
+ primary_key="id",
184
+ write_disposition="merge",
185
+ columns={
186
+ "app_id": {
187
+ "data_type": "bigint",
188
+ "nullable": True,
189
+ "description": "The ID of the app that created the order.",
190
+ },
191
+ "billing_address": {
192
+ "data_type": "complex",
193
+ "nullable": True,
194
+ "description": "The mailing address associated with the payment method.",
195
+ },
196
+ "browser_ip": {
197
+ "data_type": "text",
198
+ "nullable": True,
199
+ "description": "The IP address of the browser used by the customer when they placed the order.",
200
+ },
201
+ "buyer_accepts_marketing": {
202
+ "data_type": "bool",
203
+ "nullable": True,
204
+ "description": "Whether the customer consented to receive email updates from the shop.",
205
+ },
206
+ "cancel_reason": {
207
+ "data_type": "text",
208
+ "nullable": True,
209
+ "description": "The reason why the order was canceled.",
210
+ },
211
+ "cancelled_at": {
212
+ "data_type": "timestamp",
213
+ "nullable": True,
214
+ "description": "The date and time when the order was canceled.",
215
+ },
216
+ "cart_token": {
217
+ "data_type": "text",
218
+ "nullable": True,
219
+ "description": "A unique value referencing the cart associated with the order.",
220
+ },
221
+ "checkout_token": {
222
+ "data_type": "text",
223
+ "nullable": True,
224
+ "description": "A unique value referencing the checkout associated with the order.",
225
+ },
226
+ "client_details": {
227
+ "data_type": "complex",
228
+ "nullable": True,
229
+ "description": "Information about the browser the customer used when placing the order.",
230
+ },
231
+ "closed_at": {
232
+ "data_type": "timestamp",
233
+ "nullable": True,
234
+ "description": "The date and time when the order was closed.",
235
+ },
236
+ "company": {
237
+ "data_type": "complex",
238
+ "nullable": True,
239
+ "description": "Information about the purchasing company for the order.",
240
+ },
241
+ "confirmation_number": {
242
+ "data_type": "text",
243
+ "nullable": True,
244
+ "description": "A randomly generated identifier for the order.",
245
+ },
246
+ "confirmed": {
247
+ "data_type": "bool",
248
+ "nullable": True,
249
+ "description": "Whether inventory has been reserved for the order.",
250
+ },
251
+ "created_at": {
252
+ "data_type": "timestamp",
253
+ "nullable": False,
254
+ "description": "The autogenerated date and time when the order was created.",
255
+ },
256
+ "currency": {
257
+ "data_type": "text",
258
+ "nullable": False,
259
+ "description": "The three-letter code (ISO 4217 format) for the shop currency.",
260
+ },
261
+ "current_total_additional_fees_set": {
262
+ "data_type": "complex",
263
+ "nullable": True,
264
+ "description": "The current total additional fees on the order in shop and presentment currencies.",
265
+ },
266
+ "current_total_discounts": {
267
+ "data_type": "decimal",
268
+ "nullable": True,
269
+ "description": "The current total discounts on the order in the shop currency.",
270
+ },
271
+ "current_total_discounts_set": {
272
+ "data_type": "complex",
273
+ "nullable": True,
274
+ "description": "The current total discounts on the order in shop and presentment currencies.",
275
+ },
276
+ "current_total_duties_set": {
277
+ "data_type": "complex",
278
+ "nullable": True,
279
+ "description": "The current total duties charged on the order in shop and presentment currencies.",
280
+ },
281
+ "current_total_price": {
282
+ "data_type": "decimal",
283
+ "nullable": True,
284
+ "description": "The current total price of the order in the shop currency.",
285
+ },
286
+ "current_total_price_set": {
287
+ "data_type": "complex",
288
+ "nullable": True,
289
+ "description": "The current total price of the order in shop and presentment currencies.",
290
+ },
291
+ "current_subtotal_price": {
292
+ "data_type": "decimal",
293
+ "nullable": True,
294
+ "description": "The sum of prices for all line items after discounts and returns in the shop currency.",
295
+ },
296
+ "current_subtotal_price_set": {
297
+ "data_type": "complex",
298
+ "nullable": True,
299
+ "description": "The sum of the prices for all line items after discounts and returns in shop and presentment currencies.",
300
+ },
301
+ "current_total_tax": {
302
+ "data_type": "decimal",
303
+ "nullable": True,
304
+ "description": "The sum of the prices for all tax lines applied to the order in the shop currency.",
305
+ },
306
+ "current_total_tax_set": {
307
+ "data_type": "complex",
308
+ "nullable": True,
309
+ "description": "The sum of the prices for all tax lines applied to the order in shop and presentment currencies.",
310
+ },
311
+ "customer": {
312
+ "data_type": "complex",
313
+ "nullable": True,
314
+ "description": "Information about the customer.",
315
+ },
316
+ "customer_locale": {
317
+ "data_type": "text",
318
+ "nullable": True,
319
+ "description": "The two or three-letter language code, optionally followed by a region modifier.",
320
+ },
321
+ "discount_applications": {
322
+ "data_type": "complex",
323
+ "nullable": True,
324
+ "description": "An ordered list of stacked discount applications.",
325
+ },
326
+ "discount_codes": {
327
+ "data_type": "complex",
328
+ "nullable": True,
329
+ "description": "A list of discounts applied to the order.",
330
+ },
331
+ "email": {
332
+ "data_type": "text",
333
+ "nullable": True,
334
+ "description": "The customer's email address.",
335
+ },
336
+ "estimated_taxes": {
337
+ "data_type": "bool",
338
+ "nullable": True,
339
+ "description": "Whether taxes on the order are estimated.",
340
+ },
341
+ "financial_status": {
342
+ "data_type": "text",
343
+ "nullable": True,
344
+ "description": "The status of payments associated with the order.",
345
+ },
346
+ "fulfillments": {
347
+ "data_type": "complex",
348
+ "nullable": True,
349
+ "description": "An array of fulfillments associated with the order.",
350
+ },
351
+ "fulfillment_status": {
352
+ "data_type": "text",
353
+ "nullable": True,
354
+ "description": "The order's status in terms of fulfilled line items.",
355
+ },
356
+ "gateway": {
357
+ "data_type": "text",
358
+ "nullable": True,
359
+ "description": "The payment gateway used.",
360
+ },
361
+ "id": {
362
+ "data_type": "bigint",
363
+ "nullable": False,
364
+ "primary_key": True,
365
+ "description": "The ID of the order, used for API purposes.",
366
+ },
367
+ "landing_site": {
368
+ "data_type": "text",
369
+ "nullable": True,
370
+ "description": "The URL for the page where the buyer landed when they entered the shop.",
371
+ },
372
+ "line_items": {
373
+ "data_type": "complex",
374
+ "nullable": True,
375
+ "description": "A list of line item objects containing information about an item in the order.",
376
+ },
377
+ "location_id": {
378
+ "data_type": "bigint",
379
+ "nullable": True,
380
+ "description": "The ID of one of the locations assigned to fulfill the order.",
381
+ },
382
+ "merchant_of_record_app_id": {
383
+ "data_type": "bigint",
384
+ "nullable": True,
385
+ "description": "The application acting as Merchant of Record for the order.",
386
+ },
387
+ "name": {
388
+ "data_type": "text",
389
+ "nullable": True,
390
+ "description": "The order name, generated by combining the order_number with the order prefix and suffix.",
391
+ },
392
+ "note": {
393
+ "data_type": "text",
394
+ "nullable": True,
395
+ "description": "An optional note that a shop owner can attach to the order.",
396
+ },
397
+ "note_attributes": {
398
+ "data_type": "complex",
399
+ "nullable": True,
400
+ "description": "Extra information added to the order as key-value pairs.",
401
+ },
402
+ "number": {
403
+ "data_type": "bigint",
404
+ "nullable": True,
405
+ "description": "The order's position in the shop's count of orders.",
406
+ },
407
+ "order_number": {
408
+ "data_type": "bigint",
409
+ "nullable": True,
410
+ "description": "The order's position in the shop's count of orders, starting at 1001.",
411
+ },
412
+ "original_total_additional_fees_set": {
413
+ "data_type": "complex",
414
+ "nullable": True,
415
+ "description": "The original total additional fees on the order in shop and presentment currencies.",
416
+ },
417
+ "original_total_duties_set": {
418
+ "data_type": "complex",
419
+ "nullable": True,
420
+ "description": "The original total duties charged on the order in shop and presentment currencies.",
421
+ },
422
+ "payment_terms": {
423
+ "data_type": "complex",
424
+ "nullable": True,
425
+ "description": "The terms and conditions under which a payment should be processed.",
426
+ },
427
+ "payment_gateway_names": {
428
+ "data_type": "complex",
429
+ "nullable": True,
430
+ "description": "The list of payment gateways used for the order.",
431
+ },
432
+ "phone": {
433
+ "data_type": "text",
434
+ "nullable": True,
435
+ "description": "The customer's phone number for receiving SMS notifications.",
436
+ },
437
+ "po_number": {
438
+ "data_type": "text",
439
+ "nullable": True,
440
+ "description": "The purchase order number associated with the order.",
441
+ },
442
+ "presentment_currency": {
443
+ "data_type": "text",
444
+ "nullable": True,
445
+ "description": "The presentment currency used to display prices to the customer.",
446
+ },
447
+ "processed_at": {
448
+ "data_type": "timestamp",
449
+ "nullable": True,
450
+ "description": "The date and time when an order was processed.",
451
+ },
452
+ "referring_site": {
453
+ "data_type": "text",
454
+ "nullable": True,
455
+ "description": "The website where the customer clicked a link to the shop.",
456
+ },
457
+ "refunds": {
458
+ "data_type": "complex",
459
+ "nullable": True,
460
+ "description": "A list of refunds applied to the order.",
461
+ },
462
+ "shipping_address": {
463
+ "data_type": "complex",
464
+ "nullable": True,
465
+ "description": "The mailing address where the order will be shipped.",
466
+ },
467
+ "shipping_lines": {
468
+ "data_type": "complex",
469
+ "nullable": True,
470
+ "description": "An array detailing the shipping methods used.",
471
+ },
472
+ "source_name": {
473
+ "data_type": "text",
474
+ "nullable": True,
475
+ "description": "The source of the checkout.",
476
+ },
477
+ "source_identifier": {
478
+ "data_type": "text",
479
+ "nullable": True,
480
+ "description": "The ID of the order placed on the originating platform.",
481
+ },
482
+ "source_url": {
483
+ "data_type": "text",
484
+ "nullable": True,
485
+ "description": "A valid URL to the original order on the originating surface.",
486
+ },
487
+ "subtotal_price": {
488
+ "data_type": "decimal",
489
+ "nullable": True,
490
+ "description": "The price of the order in the shop currency after discounts but before shipping, duties, taxes, and tips.",
491
+ },
492
+ "subtotal_price_set": {
493
+ "data_type": "complex",
494
+ "nullable": True,
495
+ "description": "The subtotal of the order in shop and presentment currencies after discounts but before shipping, duties, taxes, and tips.",
496
+ },
497
+ "tags": {
498
+ "data_type": "text",
499
+ "nullable": True,
500
+ "description": "Tags attached to the order, formatted as a string of comma-separated values.",
501
+ },
502
+ "tax_lines": {
503
+ "data_type": "complex",
504
+ "nullable": True,
505
+ "description": "An array of tax line objects detailing taxes applied to the order.",
506
+ },
507
+ "taxes_included": {
508
+ "data_type": "bool",
509
+ "nullable": True,
510
+ "description": "Whether taxes are included in the order subtotal.",
511
+ },
512
+ "test": {
513
+ "data_type": "bool",
514
+ "nullable": True,
515
+ "description": "Whether this is a test order.",
516
+ },
517
+ "token": {
518
+ "data_type": "text",
519
+ "nullable": True,
520
+ "description": "A unique value referencing the order.",
521
+ },
522
+ "total_discounts": {
523
+ "data_type": "decimal",
524
+ "nullable": True,
525
+ "description": "The total discounts applied to the price of the order in the shop currency.",
526
+ },
527
+ "total_discounts_set": {
528
+ "data_type": "complex",
529
+ "nullable": True,
530
+ "description": "The total discounts applied to the price of the order in shop and presentment currencies.",
531
+ },
532
+ "total_line_items_price": {
533
+ "data_type": "decimal",
534
+ "nullable": True,
535
+ "description": "The sum of all line item prices in the shop currency.",
536
+ },
537
+ "total_line_items_price_set": {
538
+ "data_type": "complex",
539
+ "nullable": True,
540
+ "description": "The total of all line item prices in shop and presentment currencies.",
541
+ },
542
+ "total_outstanding": {
543
+ "data_type": "decimal",
544
+ "nullable": True,
545
+ "description": "The total outstanding amount of the order in the shop currency.",
546
+ },
547
+ "total_price": {
548
+ "data_type": "decimal",
549
+ "nullable": True,
550
+ "description": "The sum of all line item prices, discounts, shipping, taxes, and tips in the shop currency.",
551
+ },
552
+ "total_price_set": {
553
+ "data_type": "complex",
554
+ "nullable": True,
555
+ "description": "The total price of the order in shop and presentment currencies.",
556
+ },
557
+ "total_shipping_price_set": {
558
+ "data_type": "complex",
559
+ "nullable": True,
560
+ "description": "The total shipping price of the order in shop and presentment currencies.",
561
+ },
562
+ "total_tax": {
563
+ "data_type": "decimal",
564
+ "nullable": True,
565
+ "description": "The sum of the prices for all tax lines applied to the order in the shop currency.",
566
+ },
567
+ "total_tax_set": {
568
+ "data_type": "complex",
569
+ "nullable": True,
570
+ "description": "The sum of the prices for all tax lines applied to the order in shop and presentment currencies.",
571
+ },
572
+ "total_tip_received": {
573
+ "data_type": "decimal",
574
+ "nullable": True,
575
+ "description": "The sum of all the tips in the order in the shop currency.",
576
+ },
577
+ "total_weight": {
578
+ "data_type": "decimal",
579
+ "nullable": True,
580
+ "description": "The sum of all line item weights in grams.",
581
+ },
582
+ "updated_at": {
583
+ "data_type": "timestamp",
584
+ "nullable": True,
585
+ "description": "The date and time when the order was last modified.",
586
+ },
587
+ "user_id": {
588
+ "data_type": "bigint",
589
+ "nullable": True,
590
+ "description": "The ID of the user logged into Shopify POS who processed the order.",
591
+ },
592
+ "order_status_url": {
593
+ "data_type": "text",
594
+ "nullable": True,
595
+ "description": "The URL pointing to the order status web page, if applicable.",
596
+ },
597
+ },
598
+ )
99
599
  def orders(
100
600
  updated_at: dlt.sources.incremental[
101
601
  pendulum.DateTime
@@ -161,67 +661,1265 @@ def shopify_source(
161
661
  params["updated_at_max"] = updated_at.end_value.isoformat()
162
662
  yield from client.get_pages("customers", params)
163
663
 
164
- return (products, orders, customers)
664
+ @dlt.resource(primary_key="id", write_disposition="append")
665
+ def events(
666
+ created_at: dlt.sources.incremental[
667
+ pendulum.DateTime
668
+ ] = dlt.sources.incremental(
669
+ "created_at",
670
+ initial_value=start_date_obj,
671
+ end_value=end_date_obj,
672
+ ),
673
+ items_per_page: int = items_per_page,
674
+ ) -> Iterable[TDataItem]:
675
+ params = dict(
676
+ created_at_min=created_at.last_value.isoformat(),
677
+ limit=items_per_page,
678
+ order="created_at asc",
679
+ )
680
+ yield from client.get_pages("events", params)
165
681
 
682
+ @dlt.resource(primary_key="id", write_disposition="merge")
683
+ def price_rules(
684
+ updated_at: dlt.sources.incremental[
685
+ pendulum.DateTime
686
+ ] = dlt.sources.incremental(
687
+ "updated_at",
688
+ initial_value=start_date_obj,
689
+ end_value=end_date_obj,
690
+ ),
691
+ items_per_page: int = items_per_page,
692
+ ) -> Iterable[TDataItem]:
693
+ params = dict(
694
+ updated_at_min=updated_at.last_value.isoformat(),
695
+ limit=items_per_page,
696
+ order="updated_at asc",
697
+ )
698
+ yield from client.get_pages("price_rules", params)
166
699
 
167
- @dlt.resource
168
- def shopify_partner_query(
169
- query: str,
170
- data_items_path: jp.TJsonPath,
171
- pagination_cursor_path: jp.TJsonPath,
172
- pagination_variable_name: str = "after",
173
- variables: Optional[Dict[str, Any]] = None,
174
- access_token: str = dlt.secrets.value,
175
- organization_id: str = dlt.config.value,
176
- api_version: str = DEFAULT_PARTNER_API_VERSION,
177
- ) -> Iterable[TDataItem]:
178
- """
179
- Resource for getting paginated results from the Shopify Partner GraphQL API.
700
+ @dlt.resource(primary_key="id", write_disposition="merge")
701
+ def transactions(
702
+ since_id: dlt.sources.incremental[int] = dlt.sources.incremental(
703
+ "id",
704
+ initial_value=None,
705
+ ),
706
+ items_per_page: int = items_per_page,
707
+ ) -> Iterable[TDataItem]:
708
+ params = dict(
709
+ limit=items_per_page,
710
+ )
711
+ if since_id.start_value is not None:
712
+ params["since_id"] = since_id.start_value
713
+ yield from client.get_pages("shopify_payments/balance/transactions", params)
714
+
715
+ @dlt.resource(
716
+ primary_key="currency",
717
+ write_disposition={"disposition": "merge", "strategy": "scd2"},
718
+ )
719
+ def balance() -> Iterable[TDataItem]:
720
+ yield from client.get_pages("shopify_payments/balance", {})
180
721
 
181
- This resource will run the given GraphQL query and extract a list of data items from the result.
182
- It will then run the query again with a pagination cursor to get the next page of results.
722
+ @dlt.resource(primary_key="id", write_disposition="merge")
723
+ def inventory_items(
724
+ updated_at: dlt.sources.incremental[
725
+ pendulum.DateTime
726
+ ] = dlt.sources.incremental(
727
+ "updatedAt",
728
+ initial_value=start_date_obj,
729
+ end_value=end_date_obj,
730
+ allow_external_schedulers=True,
731
+ ),
732
+ items_per_page: int = items_per_page,
733
+ ) -> Iterable[TDataItem]:
734
+ client = ShopifyGraphQLApi(
735
+ base_url=shop_url,
736
+ access_token=private_app_password,
737
+ api_version="2024-07",
738
+ )
183
739
 
184
- Example:
185
- query = '''query Transactions($after: String) {
186
- transactions(after: $after, first: 100) {
740
+ query = """
741
+ query inventoryItems($after: String, $query: String, $first: Int) {
742
+ inventoryItems(after: $after, first: $first, query: $query) {
187
743
  edges {
188
- cursor
189
- node {
744
+ node {
745
+ id
746
+ countryCodeOfOrigin
747
+ createdAt
748
+ duplicateSkuCount
749
+ harmonizedSystemCode
750
+ inventoryHistoryUrl
751
+ legacyResourceId
752
+ measurement {
753
+ id
754
+ weight {
755
+ unit
756
+ value
757
+ }
758
+ }
759
+
760
+ provinceCodeOfOrigin
761
+ requiresShipping
762
+ sku
763
+ tracked
764
+ trackedEditable {
765
+ locked
766
+ reason
767
+ }
768
+ unitCost {
769
+ amount
770
+ currencyCode
771
+ }
772
+ updatedAt
773
+ variant {
774
+ id
775
+ availableForSale
776
+ barcode
777
+
778
+ compareAtPrice
779
+ createdAt
780
+ inventoryPolicy
781
+ inventoryQuantity
782
+ legacyResourceId
783
+
784
+ position
785
+ price
786
+ product {
190
787
  id
191
788
  }
789
+ requiresComponents
790
+
791
+ selectedOptions {
792
+ name
793
+ value
794
+ }
795
+ sellableOnlineQuantity
796
+
797
+ sellingPlanGroupsCount {
798
+ count
799
+ precision
800
+ }
801
+ sku
802
+
803
+ taxCode
804
+ taxable
805
+ title
806
+ updatedAt
807
+ }
808
+ }
809
+ }
810
+ pageInfo {
811
+ endCursor
192
812
  }
193
813
  }
194
- }'''
814
+ }"""
195
815
 
196
- partner_query_pages(
816
+ yield from client.get_graphql_pages(
197
817
  query,
198
- data_items_path="data.transactions.edges[*].node",
199
- pagination_cursor_path="data.transactions.edges[-1].cursor",
818
+ data_items_path="data.inventoryItems.edges[*].node",
819
+ pagination_cursor_path="data.inventoryItems.pageInfo.endCursor",
200
820
  pagination_variable_name="after",
821
+ variables={
822
+ "query": f"updated_at:>'{updated_at.last_value.isoformat()}'",
823
+ "first": items_per_page,
824
+ },
201
825
  )
202
826
 
203
- Args:
204
- query: The GraphQL query to run.
205
- data_items_path: The JSONPath to the data items in the query result. Should resolve to array items.
206
- pagination_cursor_path: The JSONPath to the pagination cursor in the query result, will be piped to the next query via variables.
207
- pagination_variable_name: The name of the variable to pass the pagination cursor to.
208
- variables: Mapping of extra variables used in the query.
209
- access_token: The Partner API Client access token, created in the Partner Dashboard.
210
- organization_id: Your Organization ID, found in the Partner Dashboard.
211
- api_version: The API version to use (e.g. 2024-01). Use `unstable` for the latest version.
212
- Returns:
213
- Iterable[TDataItem]: A generator of the query results.
214
- """
215
- client = ShopifyPartnerApi(
216
- access_token=access_token,
217
- organization_id=organization_id,
218
- api_version=api_version,
827
+ @dlt.resource(primary_key="id", write_disposition="merge")
828
+ def discounts(items_per_page: int = items_per_page) -> Iterable[TDataItem]:
829
+ client = ShopifyGraphQLApi(
830
+ base_url=shop_url,
831
+ access_token=private_app_password,
832
+ api_version="2024-07",
833
+ )
834
+
835
+ query = """
836
+ query discountNodes($after: String, $query: String, $first: Int) {
837
+ discountNodes(after: $after, first: $first, query: $query) {
838
+ nodes {
839
+ id
840
+ discount {
841
+ __typename
842
+ ... on DiscountCodeApp {
843
+ appDiscountType {
844
+ app {
845
+ id
846
+ }
847
+ functionId
848
+ targetType
849
+ }
850
+ appliesOncePerCustomer
851
+ asyncUsageCount
852
+ combinesWith {
853
+ orderDiscounts
854
+ productDiscounts
855
+ shippingDiscounts
856
+ }
857
+ codesCount {
858
+ count
859
+ precision
860
+ }
861
+ createdAt
862
+ customerSelection {
863
+ __typename
864
+ ... on DiscountCustomerAll {
865
+ allCustomers
866
+ }
867
+ ... on DiscountCustomerSegments {
868
+ segments {
869
+ creationDate
870
+ id
871
+ lastEditDate
872
+ name
873
+ query
874
+ }
875
+ }
876
+ ... on DiscountCustomers {
877
+ customers {
878
+ id
879
+ }
880
+ }
881
+ }
882
+ discountClass
883
+ discountId
884
+ endsAt
885
+ errorHistory {
886
+ errorsFirstOccurredAt
887
+ firstOccurredAt
888
+ hasBeenSharedSinceLastError
889
+ hasSharedRecentErrors
890
+ }
891
+ hasTimelineComment
892
+ recurringCycleLimit
893
+ shareableUrls {
894
+ targetItemImage {
895
+ id
896
+ url
897
+ }
898
+ targetType
899
+ title
900
+ url
901
+ }
902
+ startsAt
903
+ status
904
+ title
905
+ totalSales {
906
+ amount
907
+ currencyCode
908
+ }
909
+ updatedAt
910
+ usageLimit
911
+ }
912
+ ... on DiscountCodeBasic {
913
+ appliesOncePerCustomer
914
+ asyncUsageCount
915
+ codes: codes(first: 250) {
916
+ nodes {
917
+ id
918
+ code
919
+ }
920
+ }
921
+ codesCount {
922
+ count
923
+ precision
924
+ }
925
+ combinesWith {
926
+ orderDiscounts
927
+ productDiscounts
928
+ shippingDiscounts
929
+ }
930
+ createdAt
931
+ customerGets {
932
+ appliesOnOneTimePurchase
933
+ appliesOnSubscription
934
+ items {
935
+ __typename
936
+ ... on AllDiscountItems {
937
+ allItems
938
+ }
939
+ ... on DiscountCollections {
940
+ collectionsFirst250: collections(first: 250) {
941
+ nodes {
942
+ id
943
+ }
944
+ }
945
+ }
946
+ ... on DiscountProducts {
947
+ productsFirst250: products(first: 250) {
948
+ nodes {
949
+ id
950
+ }
951
+ }
952
+ productVariantsFirst250: productVariants(first: 250) {
953
+ nodes {
954
+ id
955
+ }
956
+ }
957
+ }
958
+ }
959
+ value {
960
+ __typename
961
+ ... on DiscountAmount {
962
+ amount {
963
+ amount
964
+ currencyCode
965
+ }
966
+ appliesOnEachItem
967
+ }
968
+ ... on DiscountOnQuantity {
969
+ effect {
970
+ ... on DiscountAmount {
971
+ amount {
972
+ amount
973
+ currencyCode
974
+ }
975
+ appliesOnEachItem
976
+ }
977
+ ... on DiscountPercentage {
978
+ percentage
979
+ }
980
+ }
981
+ quantity {
982
+ quantity
983
+ }
984
+ }
985
+ ... on DiscountPercentage {
986
+ percentage
987
+ }
988
+ }
989
+ }
990
+ customerSelection {
991
+ __typename
992
+ ... on DiscountCustomerAll {
993
+ allCustomers
994
+ }
995
+ ... on DiscountCustomerSegments {
996
+ segments {
997
+ creationDate
998
+ id
999
+ lastEditDate
1000
+ name
1001
+ query
1002
+ }
1003
+ }
1004
+ ... on DiscountCustomers {
1005
+ customers {
1006
+ id
1007
+ }
1008
+ }
1009
+ }
1010
+ discountClass
1011
+ endsAt
1012
+ hasTimelineComment
1013
+ minimumRequirement {
1014
+ __typename
1015
+ ... on DiscountMinimumQuantity {
1016
+ greaterThanOrEqualToQuantity
1017
+ }
1018
+ ... on DiscountMinimumSubtotal {
1019
+ greaterThanOrEqualToSubtotal {
1020
+ amount
1021
+ currencyCode
1022
+ }
1023
+ }
1024
+ }
1025
+ recurringCycleLimit
1026
+ shareableUrls {
1027
+ url
1028
+ title
1029
+ }
1030
+ shortSummary
1031
+ startsAt
1032
+ status
1033
+ summary
1034
+ title
1035
+ totalSales {
1036
+ amount
1037
+ currencyCode
1038
+ }
1039
+ updatedAt
1040
+ usageLimit
1041
+ }
1042
+ ... on DiscountCodeBxgy {
1043
+ appliesOncePerCustomer
1044
+ asyncUsageCount
1045
+ codesFirst50: codes(first: 50) {
1046
+ nodes {
1047
+ id
1048
+ code
1049
+ }
1050
+ }
1051
+ codesCount {
1052
+ count
1053
+ precision
1054
+ }
1055
+ combinesWith {
1056
+ orderDiscounts
1057
+ productDiscounts
1058
+ shippingDiscounts
1059
+ }
1060
+ createdAt
1061
+ customerBuys {
1062
+ items {
1063
+ __typename
1064
+ ... on AllDiscountItems {
1065
+ allItems
1066
+ }
1067
+ ... on DiscountCollections {
1068
+ collectionsFirst250: collections(first: 250) {
1069
+ nodes {
1070
+ id
1071
+ }
1072
+ }
1073
+ }
1074
+ ... on DiscountProducts {
1075
+ productsFirst250: products(first: 250) {
1076
+ nodes {
1077
+ id
1078
+ }
1079
+ }
1080
+ productVariantsFirst250: productVariants(first: 250) {
1081
+ nodes {
1082
+ id
1083
+ }
1084
+ }
1085
+ }
1086
+ }
1087
+ value {
1088
+ __typename
1089
+ ... on DiscountPurchaseAmount {
1090
+ amount
1091
+ }
1092
+ ... on DiscountQuantity {
1093
+ quantity
1094
+ }
1095
+ }
1096
+ }
1097
+ customerGets {
1098
+ appliesOnOneTimePurchase
1099
+ appliesOnSubscription
1100
+ items {
1101
+ __typename
1102
+ ... on AllDiscountItems {
1103
+ allItems
1104
+ }
1105
+ ... on DiscountCollections {
1106
+ collectionsFirst250: collections(first: 250) {
1107
+ nodes {
1108
+ id
1109
+ }
1110
+ }
1111
+ }
1112
+ ... on DiscountProducts {
1113
+ productsFirst250: products(first: 250) {
1114
+ nodes {
1115
+ id
1116
+ }
1117
+ }
1118
+ productVariantsFirst250: productVariants(first: 250) {
1119
+ nodes {
1120
+ id
1121
+ }
1122
+ }
1123
+ }
1124
+ }
1125
+ value {
1126
+ __typename
1127
+ ... on DiscountAmount {
1128
+ amount {
1129
+ amount
1130
+ currencyCode
1131
+ }
1132
+ appliesOnEachItem
1133
+ }
1134
+ ... on DiscountOnQuantity {
1135
+ effect {
1136
+ __typename
1137
+ ... on DiscountAmount {
1138
+ amount {
1139
+ amount
1140
+ currencyCode
1141
+ }
1142
+ appliesOnEachItem
1143
+ }
1144
+ ... on DiscountPercentage {
1145
+ percentage
1146
+ }
1147
+ }
1148
+ quantity {
1149
+ quantity
1150
+ }
1151
+ }
1152
+ ... on DiscountPercentage {
1153
+ percentage
1154
+ }
1155
+ }
1156
+ }
1157
+ customerSelection {
1158
+ __typename
1159
+ ... on DiscountCustomerAll {
1160
+ allCustomers
1161
+ }
1162
+ ... on DiscountCustomerSegments {
1163
+ segments {
1164
+ creationDate
1165
+ id
1166
+ lastEditDate
1167
+ name
1168
+ query
1169
+ }
1170
+ }
1171
+ ... on DiscountCustomers {
1172
+ customers {
1173
+ id
1174
+ }
1175
+ }
1176
+ }
1177
+ discountClass
1178
+ endsAt
1179
+ hasTimelineComment
1180
+ shareableUrls {
1181
+ url
1182
+ title
1183
+ }
1184
+ startsAt
1185
+ status
1186
+ summary
1187
+ title
1188
+ totalSales {
1189
+ amount
1190
+ currencyCode
1191
+ }
1192
+ updatedAt
1193
+ usageLimit
1194
+ usesPerOrderLimit
1195
+ }
1196
+ ... on DiscountCodeFreeShipping {
1197
+ appliesOncePerCustomer
1198
+ appliesOnOneTimePurchase
1199
+ appliesOnSubscription
1200
+ asyncUsageCount
1201
+ codesFirst250: codes(first: 250) {
1202
+ nodes {
1203
+ id
1204
+ code
1205
+ }
1206
+ }
1207
+ codesCount {
1208
+ count
1209
+ precision
1210
+ }
1211
+ combinesWith {
1212
+ orderDiscounts
1213
+ productDiscounts
1214
+ shippingDiscounts
1215
+ }
1216
+ createdAt
1217
+ customerSelection {
1218
+ __typename
1219
+ ... on DiscountCustomerAll {
1220
+ allCustomers
1221
+ }
1222
+ ... on DiscountCustomerSegments {
1223
+ segments {
1224
+ creationDate
1225
+ id
1226
+ lastEditDate
1227
+ name
1228
+ query
1229
+ }
1230
+ }
1231
+ ... on DiscountCustomers {
1232
+ customers {
1233
+ id
1234
+ }
1235
+ }
1236
+ }
1237
+ destinationSelection {
1238
+ __typename
1239
+ ... on DiscountCountries {
1240
+ countries
1241
+ includeRestOfWorld
1242
+ }
1243
+ ... on DiscountCountryAll {
1244
+ allCountries
1245
+ }
1246
+ }
1247
+ discountClass
1248
+ endsAt
1249
+ hasTimelineComment
1250
+ maximumShippingPrice {
1251
+ amount
1252
+ currencyCode
1253
+ }
1254
+ minimumRequirement {
1255
+ __typename
1256
+ ... on DiscountMinimumQuantity {
1257
+ greaterThanOrEqualToQuantity
1258
+ }
1259
+ ... on DiscountMinimumSubtotal {
1260
+ greaterThanOrEqualToSubtotal {
1261
+ amount
1262
+ currencyCode
1263
+ }
1264
+ }
1265
+ }
1266
+ recurringCycleLimit
1267
+ shareableUrls {
1268
+ targetItemImage {
1269
+ id
1270
+ url
1271
+ }
1272
+ targetType
1273
+ title
1274
+ url
1275
+ }
1276
+ shortSummary
1277
+ startsAt
1278
+ status
1279
+ summary
1280
+ title
1281
+ totalSales {
1282
+ amount
1283
+ currencyCode
1284
+ }
1285
+ updatedAt
1286
+ usageLimit
1287
+ }
1288
+ ... on DiscountAutomaticApp {
1289
+ appDiscountType {
1290
+ app {
1291
+ apiKey
1292
+ }
1293
+ appBridge {
1294
+ createPath
1295
+ detailsPath
1296
+ }
1297
+ appKey
1298
+ description
1299
+ discountClass
1300
+ functionId
1301
+ targetType
1302
+ title
1303
+ }
1304
+ asyncUsageCount
1305
+ combinesWith {
1306
+ orderDiscounts
1307
+ productDiscounts
1308
+ shippingDiscounts
1309
+ }
1310
+ createdAt
1311
+ discountClass
1312
+ discountId
1313
+ startsAt
1314
+ endsAt
1315
+ errorHistory {
1316
+ errorsFirstOccurredAt
1317
+ firstOccurredAt
1318
+ hasBeenSharedSinceLastError
1319
+ hasSharedRecentErrors
1320
+ }
1321
+ status
1322
+ title
1323
+ updatedAt
1324
+ }
1325
+ ... on DiscountAutomaticBasic {
1326
+ asyncUsageCount
1327
+ combinesWith {
1328
+ orderDiscounts
1329
+ productDiscounts
1330
+ shippingDiscounts
1331
+ }
1332
+ createdAt
1333
+ customerGets {
1334
+ appliesOnOneTimePurchase
1335
+ appliesOnSubscription
1336
+ items {
1337
+ __typename
1338
+ ... on AllDiscountItems {
1339
+ allItems
1340
+ }
1341
+ ... on DiscountCollections {
1342
+ collectionsFirst250: collections(first: 250) {
1343
+ nodes {
1344
+ id
1345
+ }
1346
+ }
1347
+ }
1348
+ ... on DiscountProducts {
1349
+ productsFirst250: products(first: 250) {
1350
+ nodes {
1351
+ id
1352
+ }
1353
+ }
1354
+ productVariantsFirst250: productVariants(first: 250) {
1355
+ nodes {
1356
+ id
1357
+ }
1358
+ }
1359
+ }
1360
+ }
1361
+ value {
1362
+ __typename
1363
+ ... on DiscountAmount {
1364
+ amount {
1365
+ amount
1366
+ currencyCode
1367
+ }
1368
+ appliesOnEachItem
1369
+ }
1370
+ ... on DiscountOnQuantity {
1371
+ effect {
1372
+ __typename
1373
+ ... on DiscountAmount {
1374
+ amount {
1375
+ amount
1376
+ currencyCode
1377
+ }
1378
+ appliesOnEachItem
1379
+ }
1380
+ ... on DiscountPercentage {
1381
+ percentage
1382
+ }
1383
+ }
1384
+ quantity {
1385
+ quantity
1386
+ }
1387
+ }
1388
+ ... on DiscountPercentage {
1389
+ percentage
1390
+ }
1391
+ }
1392
+ }
1393
+ discountClass
1394
+ endsAt
1395
+ minimumRequirement {
1396
+ __typename
1397
+ ... on DiscountMinimumQuantity {
1398
+ greaterThanOrEqualToQuantity
1399
+ }
1400
+ ... on DiscountMinimumSubtotal {
1401
+ greaterThanOrEqualToSubtotal {
1402
+ amount
1403
+ currencyCode
1404
+ }
1405
+ }
1406
+ }
1407
+ recurringCycleLimit
1408
+ shortSummary
1409
+ startsAt
1410
+ status
1411
+ summary
1412
+ title
1413
+ updatedAt
1414
+ }
1415
+ ... on DiscountAutomaticBxgy {
1416
+ asyncUsageCount
1417
+ combinesWith {
1418
+ orderDiscounts
1419
+ productDiscounts
1420
+ shippingDiscounts
1421
+ }
1422
+ createdAt
1423
+ customerBuys {
1424
+ items {
1425
+ __typename
1426
+ ... on AllDiscountItems {
1427
+ allItems
1428
+ }
1429
+ ... on DiscountCollections {
1430
+ collectionsFirst250: collections(first: 250) {
1431
+ nodes {
1432
+ id
1433
+ }
1434
+ }
1435
+ }
1436
+ ... on DiscountProducts {
1437
+ productsFirst250: products(first: 250) {
1438
+ nodes {
1439
+ id
1440
+ }
1441
+ }
1442
+ productVariantsFirst250: productVariants(first: 250) {
1443
+ nodes {
1444
+ id
1445
+ }
1446
+ }
1447
+ }
1448
+ }
1449
+ value {
1450
+ __typename
1451
+ ... on DiscountPurchaseAmount {
1452
+ amount
1453
+ }
1454
+ ... on DiscountQuantity {
1455
+ quantity
1456
+ }
1457
+ }
1458
+ }
1459
+ customerGets {
1460
+ appliesOnOneTimePurchase
1461
+ appliesOnSubscription
1462
+ items {
1463
+ __typename
1464
+ ... on AllDiscountItems {
1465
+ allItems
1466
+ }
1467
+ ... on DiscountCollections {
1468
+ collectionsFirst250: collections(first: 250) {
1469
+ nodes {
1470
+ id
1471
+ }
1472
+ }
1473
+ }
1474
+ ... on DiscountProducts {
1475
+ productsFirst250: products(first: 250) {
1476
+ nodes {
1477
+ id
1478
+ }
1479
+ }
1480
+ productVariantsFirst250: productVariants(first: 250) {
1481
+ nodes {
1482
+ id
1483
+ }
1484
+ }
1485
+ }
1486
+ }
1487
+ value {
1488
+ __typename
1489
+ ... on DiscountAmount {
1490
+ amount {
1491
+ amount
1492
+ currencyCode
1493
+ }
1494
+ appliesOnEachItem
1495
+ }
1496
+ ... on DiscountOnQuantity {
1497
+ effect {
1498
+ __typename
1499
+ ... on DiscountAmount {
1500
+ amount {
1501
+ amount
1502
+ currencyCode
1503
+ }
1504
+ appliesOnEachItem
1505
+ }
1506
+ ... on DiscountPercentage {
1507
+ percentage
1508
+ }
1509
+ }
1510
+ quantity {
1511
+ quantity
1512
+ }
1513
+ }
1514
+ ... on DiscountPercentage {
1515
+ percentage
1516
+ }
1517
+ }
1518
+ }
1519
+ discountClass
1520
+ endsAt
1521
+ startsAt
1522
+ status
1523
+ summary
1524
+ title
1525
+ updatedAt
1526
+ usesPerOrderLimit
1527
+ }
1528
+ ... on DiscountAutomaticFreeShipping {
1529
+ appliesOnOneTimePurchase
1530
+ appliesOnSubscription
1531
+ asyncUsageCount
1532
+ combinesWith {
1533
+ orderDiscounts
1534
+ productDiscounts
1535
+ shippingDiscounts
1536
+ }
1537
+ createdAt
1538
+ destinationSelection
1539
+ discountClass
1540
+ endsAt
1541
+ hasTimelineComment
1542
+ maximumShippingPrice {
1543
+ amount
1544
+ currencyCode
1545
+ }
1546
+ minimumRequirement {
1547
+ __typename
1548
+ ... on DiscountMinimumQuantity {
1549
+ greaterThanOrEqualToQuantity
1550
+ }
1551
+ ... on DiscountMinimumSubtotal {
1552
+ greaterThanOrEqualToSubtotal {
1553
+ amount
1554
+ currencyCode
1555
+ }
1556
+ }
1557
+ }
1558
+ recurringCycleLimit
1559
+ shortSummary
1560
+ startsAt
1561
+ status
1562
+ summary
1563
+ title
1564
+ totalSales {
1565
+ amount
1566
+ currencyCode
1567
+ }
1568
+ updatedAt
1569
+ }
1570
+ }
1571
+ metafieldsFirst250: metafields(first: 250) {
1572
+ nodes {
1573
+ id
1574
+ key
1575
+ value
1576
+ }
1577
+ }
1578
+ }
1579
+ pageInfo {
1580
+ endCursor
1581
+ hasNextPage
1582
+ hasPreviousPage
1583
+ startCursor
1584
+ }
1585
+ }
1586
+ }
1587
+ """
1588
+
1589
+ yield from client.get_graphql_pages(
1590
+ query,
1591
+ data_items_path="data.discountNodes.nodes[*]",
1592
+ pagination_cursor_path="data.discountNodes.pageInfo.endCursor",
1593
+ pagination_variable_name="after",
1594
+ variables={
1595
+ "first": items_per_page,
1596
+ },
1597
+ )
1598
+
1599
+ @dlt.resource(primary_key="id", write_disposition="merge")
1600
+ def taxonomy(items_per_page: int = items_per_page) -> Iterable[TDataItem]:
1601
+ client = ShopifyGraphQLApi(
1602
+ base_url=shop_url,
1603
+ access_token=private_app_password,
1604
+ api_version="2024-07",
1605
+ )
1606
+
1607
+ query = """
1608
+ {
1609
+ taxonomy {
1610
+ categories(first: 250) {
1611
+ nodes {
1612
+ id
1613
+ isArchived
1614
+ isLeaf
1615
+ isRoot
1616
+ level
1617
+ name
1618
+ parentId
1619
+ fullName
1620
+ ancestorIds
1621
+ attributes(first: 250) {
1622
+ nodes {
1623
+ ... on TaxonomyAttribute {
1624
+ id
1625
+ }
1626
+ ... on TaxonomyChoiceListAttribute {
1627
+ id
1628
+ name
1629
+ }
1630
+ ... on TaxonomyMeasurementAttribute {
1631
+ id
1632
+ name
1633
+ }
1634
+ }
1635
+ }
1636
+ }
1637
+ pageInfo {
1638
+ endCursor
1639
+ hasNextPage
1640
+ hasPreviousPage
1641
+ startCursor
1642
+ }
1643
+ }
1644
+ }
1645
+ }
1646
+ """
1647
+
1648
+ yield from client.get_graphql_pages(
1649
+ query,
1650
+ data_items_path="data.taxonomy.categories.nodes[*]",
1651
+ pagination_cursor_path="data.taxonomy.categories.pageInfo.endCursor",
1652
+ pagination_cursor_has_next_page_path="data.taxonomy.categories.pageInfo.hasNextPage",
1653
+ pagination_variable_name="after",
1654
+ variables={
1655
+ "first": items_per_page,
1656
+ },
1657
+ )
1658
+
1659
+ @dlt.resource(
1660
+ primary_key="id",
1661
+ write_disposition="merge",
1662
+ columns={
1663
+ "id": {
1664
+ "data_type": "text",
1665
+ "nullable": False,
1666
+ "primary_key": True,
1667
+ "description": "A globally unique ID for the product.",
1668
+ },
1669
+ "category": {
1670
+ "data_type": "complex",
1671
+ "nullable": True,
1672
+ "description": "The category of the product from Shopify's Standard Product Taxonomy.",
1673
+ },
1674
+ "combinedListing": {
1675
+ "data_type": "complex",
1676
+ "nullable": True,
1677
+ "description": "A special product type that combines separate products into a single product listing.",
1678
+ },
1679
+ "combinedListingRole": {
1680
+ "data_type": "complex",
1681
+ "nullable": True,
1682
+ "description": "The role of the product in a combined listing.",
1683
+ },
1684
+ "compareAtPriceRange": {
1685
+ "data_type": "complex",
1686
+ "nullable": True,
1687
+ "description": "The compare-at price range of the product in the shop's default currency.",
1688
+ },
1689
+ "createdAt": {
1690
+ "data_type": "timestamp",
1691
+ "nullable": False,
1692
+ "description": "The date and time when the product was created.",
1693
+ },
1694
+ "defaultCursor": {
1695
+ "data_type": "text",
1696
+ "nullable": False,
1697
+ "description": "A default cursor that returns the next record sorted by ID.",
1698
+ },
1699
+ "description": {
1700
+ "data_type": "text",
1701
+ "nullable": False,
1702
+ "description": "A single-line description of the product, with HTML tags removed.",
1703
+ },
1704
+ "descriptionHtml": {
1705
+ "data_type": "text",
1706
+ "nullable": False,
1707
+ "description": "The description of the product, with HTML tags.",
1708
+ },
1709
+ "handle": {
1710
+ "data_type": "text",
1711
+ "nullable": False,
1712
+ "description": "A unique, human-readable string of the product's title.",
1713
+ },
1714
+ "metafields": {
1715
+ "data_type": "complex",
1716
+ "nullable": True,
1717
+ "description": "A list of custom fields associated with the product.",
1718
+ },
1719
+ "options": {
1720
+ "data_type": "complex",
1721
+ "nullable": True,
1722
+ "description": "A list of product options, e.g., size, color.",
1723
+ },
1724
+ "priceRangeV2": {
1725
+ "data_type": "complex",
1726
+ "nullable": False,
1727
+ "description": "The minimum and maximum prices of a product.",
1728
+ },
1729
+ "productType": {
1730
+ "data_type": "text",
1731
+ "nullable": False,
1732
+ "description": "The product type defined by the merchant.",
1733
+ },
1734
+ "publishedAt": {
1735
+ "data_type": "timestamp",
1736
+ "nullable": True,
1737
+ "description": "The date and time when the product was published.",
1738
+ },
1739
+ "status": {
1740
+ "data_type": "text",
1741
+ "nullable": False,
1742
+ "description": "The product status, which controls visibility across all sales channels.",
1743
+ },
1744
+ "tags": {
1745
+ "data_type": "text",
1746
+ "nullable": True,
1747
+ "description": "A comma-separated list of searchable keywords associated with the product.",
1748
+ },
1749
+ "templateSuffix": {
1750
+ "data_type": "text",
1751
+ "nullable": True,
1752
+ "description": "The theme template used when customers view the product in a store.",
1753
+ },
1754
+ "title": {
1755
+ "data_type": "text",
1756
+ "nullable": False,
1757
+ "description": "The name for the product that displays to customers.",
1758
+ },
1759
+ "totalInventory": {
1760
+ "data_type": "bigint",
1761
+ "nullable": False,
1762
+ "description": "The quantity of inventory that's in stock.",
1763
+ },
1764
+ "totalVariants": {
1765
+ "data_type": "bigint",
1766
+ "nullable": False,
1767
+ "description": "The number of variants associated with the product.",
1768
+ },
1769
+ "tracksInventory": {
1770
+ "data_type": "bool",
1771
+ "nullable": False,
1772
+ "description": "Whether inventory tracking is enabled for the product.",
1773
+ },
1774
+ "updatedAt": {
1775
+ "data_type": "timestamp",
1776
+ "nullable": False,
1777
+ "description": "The date and time when the product was last modified.",
1778
+ },
1779
+ "vendor": {
1780
+ "data_type": "text",
1781
+ "nullable": False,
1782
+ "description": "The name of the product's vendor.",
1783
+ },
1784
+ },
219
1785
  )
1786
+ def products(
1787
+ updated_at: dlt.sources.incremental[
1788
+ pendulum.DateTime
1789
+ ] = dlt.sources.incremental(
1790
+ "updatedAt",
1791
+ initial_value=start_date_obj,
1792
+ end_value=end_date_obj,
1793
+ ),
1794
+ items_per_page: int = items_per_page,
1795
+ ) -> Iterable[TDataItem]:
1796
+ client = ShopifyGraphQLApi(
1797
+ base_url=shop_url,
1798
+ access_token=private_app_password,
1799
+ api_version="2024-07",
1800
+ )
1801
+
1802
+ query = """
1803
+ query products($after: String, $query: String, $first: Int) {
1804
+ products(after: $after, first: $first, query: $query) {
1805
+ nodes {
1806
+ availablePublicationsCount {
1807
+ count
1808
+ precision
1809
+ }
1810
+ category {
1811
+ id
1812
+ }
1813
+ combinedListing {
1814
+ parentProduct {
1815
+ id
1816
+ }
1817
+ }
1818
+ combinedListingRole
1819
+ compareAtPriceRange {
1820
+ maxVariantCompareAtPrice {
1821
+ amount
1822
+ currencyCode
1823
+ }
1824
+ minVariantCompareAtPrice {
1825
+ amount
1826
+ currencyCode
1827
+ }
1828
+ }
1829
+ createdAt
1830
+ defaultCursor
1831
+ description
1832
+ descriptionHtml
1833
+ handle
1834
+ id
1835
+ metafields(first: 250) {
1836
+ nodes {
1837
+ id
1838
+ key
1839
+ value
1840
+ }
1841
+ }
1842
+ options {
1843
+ linkedMetafield {
1844
+ key
1845
+ namespace
1846
+ }
1847
+ name
1848
+ optionValues {
1849
+ hasVariants
1850
+ id
1851
+ linkedMetafieldValue
1852
+ name
1853
+ }
1854
+ values
1855
+ id
1856
+ position
1857
+ }
1858
+ priceRangeV2 {
1859
+ maxVariantPrice {
1860
+ amount
1861
+ currencyCode
1862
+ }
1863
+ minVariantPrice {
1864
+ amount
1865
+ currencyCode
1866
+ }
1867
+ }
1868
+ productType
1869
+ publishedAt
1870
+ requiresSellingPlan
1871
+ status
1872
+ tags
1873
+ templateSuffix
1874
+ totalInventory
1875
+ title
1876
+ tracksInventory
1877
+ updatedAt
1878
+ vendor
1879
+ variantsCount {
1880
+ count
1881
+ precision
1882
+ }
1883
+ variants(first: 250) {
1884
+ nodes {
1885
+ id
1886
+ sku
1887
+ storefrontId
1888
+ }
1889
+ }
1890
+ }
1891
+ pageInfo {
1892
+ endCursor
1893
+ hasNextPage
1894
+ hasPreviousPage
1895
+ }
1896
+ }
1897
+ }
1898
+ """
1899
+ variables = {
1900
+ "first": items_per_page,
1901
+ "query": f"updated_at:>'{updated_at.last_value.isoformat()}'",
1902
+ }
1903
+
1904
+ yield from client.get_graphql_pages(
1905
+ query,
1906
+ data_items_path="data.products.nodes[*]",
1907
+ pagination_cursor_path="data.products.pageInfo.endCursor",
1908
+ pagination_cursor_has_next_page_path="data.products.pageInfo.hasNextPage",
1909
+ pagination_variable_name="after",
1910
+ variables=variables,
1911
+ )
220
1912
 
221
- yield from client.get_graphql_pages(
222
- query,
223
- data_items_path=data_items_path,
224
- pagination_cursor_path=pagination_cursor_path,
225
- pagination_variable_name=pagination_variable_name,
226
- variables=variables,
1913
+ return (
1914
+ products,
1915
+ products_legacy,
1916
+ orders,
1917
+ customers,
1918
+ inventory_items,
1919
+ transactions,
1920
+ balance,
1921
+ events,
1922
+ price_rules,
1923
+ discounts,
1924
+ taxonomy,
227
1925
  )