airbyte-source-shopify 4.2.0__tar.gz → 4.2.2__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 (74) hide show
  1. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/PKG-INFO +1 -1
  2. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/pyproject.toml +1 -1
  3. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/shopify_graphql/bulk/job.py +50 -4
  4. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/streams/base_streams.py +3 -1
  5. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/streams/streams.py +5 -2
  6. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/.airbyte-pypi-readme.md +0 -0
  7. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/__init__.py +0 -0
  8. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/auth.py +0 -0
  9. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/config_migrations.py +0 -0
  10. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/http_request.py +0 -0
  11. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/run.py +0 -0
  12. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/abandoned_checkouts.json +0 -0
  13. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/articles.json +0 -0
  14. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/balance_transactions.json +0 -0
  15. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/blogs.json +0 -0
  16. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/collection_products.json +0 -0
  17. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/collections.json +0 -0
  18. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/collects.json +0 -0
  19. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/countries.json +0 -0
  20. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/custom_collections.json +0 -0
  21. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/customer_address.json +0 -0
  22. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/customer_journey_summary.json +0 -0
  23. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/customers.json +0 -0
  24. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/deleted_products.json +0 -0
  25. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/discount_codes.json +0 -0
  26. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/discount_codes_sync.json +0 -0
  27. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/disputes.json +0 -0
  28. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/draft_orders.json +0 -0
  29. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/fulfillment_orders.json +0 -0
  30. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/fulfillments.json +0 -0
  31. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/inventory_items.json +0 -0
  32. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/inventory_levels.json +0 -0
  33. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/locations.json +0 -0
  34. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/market_countries.json +0 -0
  35. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/metafield_articles.json +0 -0
  36. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/metafield_blogs.json +0 -0
  37. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/metafield_collections.json +0 -0
  38. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/metafield_customers.json +0 -0
  39. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/metafield_draft_orders.json +0 -0
  40. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/metafield_locations.json +0 -0
  41. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/metafield_orders.json +0 -0
  42. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/metafield_pages.json +0 -0
  43. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/metafield_product_images.json +0 -0
  44. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/metafield_product_variants.json +0 -0
  45. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/metafield_products.json +0 -0
  46. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/metafield_shops.json +0 -0
  47. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/metafield_smart_collections.json +0 -0
  48. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/order_agreements.json +0 -0
  49. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/order_refunds.json +0 -0
  50. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/order_risks.json +0 -0
  51. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/orders.json +0 -0
  52. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/pages.json +0 -0
  53. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/price_rules.json +0 -0
  54. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/product_images.json +0 -0
  55. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/product_variants.json +0 -0
  56. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/products.json +0 -0
  57. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/profile_location_groups.json +0 -0
  58. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/shop.json +0 -0
  59. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/smart_collections.json +0 -0
  60. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/tender_transactions.json +0 -0
  61. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/schemas/transactions.json +0 -0
  62. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/scopes.py +0 -0
  63. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/shopify_graphql/bulk/__init__.py +0 -0
  64. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/shopify_graphql/bulk/exceptions.py +0 -0
  65. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/shopify_graphql/bulk/external_sort.py +0 -0
  66. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/shopify_graphql/bulk/query.py +0 -0
  67. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/shopify_graphql/bulk/record.py +0 -0
  68. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/shopify_graphql/bulk/retry.py +0 -0
  69. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/shopify_graphql/bulk/status.py +0 -0
  70. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/shopify_graphql/bulk/tools.py +0 -0
  71. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/source.py +0 -0
  72. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/spec.json +0 -0
  73. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/transform.py +0 -0
  74. {airbyte_source_shopify-4.2.0 → airbyte_source_shopify-4.2.2}/source_shopify/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: airbyte-source-shopify
3
- Version: 4.2.0
3
+ Version: 4.2.2
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 = "4.2.0"
6
+ version = "4.2.2"
7
7
  name = "airbyte-source-shopify"
8
8
  description = "Source CDK implementation for Shopify."
9
9
  authors = [ "Airbyte <contact@airbyte.io>",]
@@ -75,6 +75,8 @@ class ShopifyBulkManager:
75
75
  _job_created_at: Optional[str] = field(init=False, default=None)
76
76
  # indicated whether or not we manually force-cancel the current job
77
77
  _job_self_canceled: bool = field(init=False, default=False)
78
+ # indicates whether or not the current job was self-canceled to checkpoint (not because it was running too long)
79
+ _job_canceled_on_checkpoint: bool = field(init=False, default=False)
78
80
  # time between job status checks
79
81
  _job_check_interval: Final[int] = 3
80
82
 
@@ -87,8 +89,14 @@ class ShopifyBulkManager:
87
89
  _job_adjust_slice_from_checkpoint: bool = field(init=False, default=False)
88
90
  # keeps the last checkpointed cursor value for supported streams
89
91
  _job_last_checkpoint_cursor_value: str | None = field(init=False, default=None)
92
+ # the flag to re-run the current slice, because the checkpointed job returned no data
93
+ _job_retry_slice_without_checkpoint: bool = field(init=False, default=False)
94
+ # the flag to disable the checkpointing for the rest of the sync, once the checkpointed job returned no data
95
+ _job_checkpoint_disabled: bool = field(init=False, default=False)
90
96
  # indicates whether or not the checkpoint comes from the partial result of the FAILED job
91
97
  _job_checkpoint_from_failed_job: bool = field(init=False, default=False)
98
+ # indicates whether or not the checkpoint comes from the result of the job self-canceled on checkpointing
99
+ _job_checkpoint_from_self_cancel: bool = field(init=False, default=False)
92
100
 
93
101
  # expand slice factor
94
102
  _job_size_expand_factor: int = field(init=False, default=2)
@@ -179,7 +187,9 @@ class ShopifyBulkManager:
179
187
 
180
188
  @property
181
189
  def _job_should_checkpoint(self) -> bool:
182
- return self._supports_checkpointing and self._job_last_rec_count >= self._job_checkpoint_interval
190
+ return (
191
+ self._supports_checkpointing and not self._job_checkpoint_disabled and self._job_last_rec_count >= self._job_checkpoint_interval
192
+ )
183
193
 
184
194
  @property
185
195
  def _job_any_lines_collected(self) -> bool:
@@ -216,6 +226,7 @@ class ShopifyBulkManager:
216
226
  self._job_result_filename = None
217
227
  # setting self-cancelation to default
218
228
  self._job_self_canceled = False
229
+ self._job_canceled_on_checkpoint = False
219
230
  # set the running job message counter to default
220
231
  self._log_job_msg_count = 0
221
232
  # set the running job object count to default
@@ -229,6 +240,7 @@ class ShopifyBulkManager:
229
240
  # reseting the checkpoint flag, if bulk job has completed normally
230
241
  self._job_adjust_slice_from_checkpoint = False
231
242
  self._job_checkpoint_from_failed_job = False
243
+ self._job_checkpoint_from_self_cancel = False
232
244
 
233
245
  def _set_last_checkpoint_cursor_value(self, checkpointed_cursor: str) -> None:
234
246
  """
@@ -315,10 +327,28 @@ class ShopifyBulkManager:
315
327
 
316
328
  def _job_get_checkpointed_result(self, response: Optional[requests.Response]) -> None:
317
329
  if self._job_any_lines_collected or self._job_should_checkpoint:
318
- # set the flag to adjust the next slice from the checkpointed cursor value
319
- self._set_checkpointing()
320
330
  # fetch the collected records from CANCELED Job on checkpointing
321
331
  self._job_result_filename = self._job_get_result(response)
332
+ if self._job_result_filename:
333
+ # set the flag to adjust the next slice from the checkpointed cursor value
334
+ self._set_checkpointing()
335
+
336
+ def _on_checkpoint_without_result(self) -> None:
337
+ """
338
+ The job was self-canceled on checkpointing, but the API returned no `url` / `partialDataUrl`.
339
+ The slice must not be treated as consumed: it is re-run, and the checkpointing is disabled
340
+ for the rest of the sync, since the API is unlikely to return the partial data for the next jobs either.
341
+ """
342
+ self._job_retry_slice_without_checkpoint = True
343
+ self._disable_checkpointing(f"after `{self._job_last_rec_count}` rows collected, but no result was returned by the API")
344
+
345
+ def _disable_checkpointing(self, reason: str) -> None:
346
+ if not self._job_checkpoint_disabled:
347
+ self._job_checkpoint_disabled = True
348
+ LOGGER.warning(
349
+ f"Stream: `{self.http_client.name}`, the BULK Job: `{self._job_id}` was canceled on checkpointing {reason}. "
350
+ f"The slice will be re-run and the checkpointing is disabled for the rest of this sync. Consider increasing the `BULK Job checkpoint (rows collected)` value to avoid this."
351
+ )
322
352
 
323
353
  def _job_update_state(self, response: Optional[requests.Response] = None) -> None:
324
354
  if response:
@@ -344,6 +374,12 @@ class ShopifyBulkManager:
344
374
  )
345
375
  else:
346
376
  self._job_get_checkpointed_result(response)
377
+ if self._job_canceled_on_checkpoint:
378
+ if self._job_result_filename:
379
+ # the next slice resumes from the checkpointed cursor, see `get_adjusted_job_end`
380
+ self._job_checkpoint_from_self_cancel = True
381
+ else:
382
+ self._on_checkpoint_without_result()
347
383
 
348
384
  def _on_canceling_job(self, **kwargs) -> None:
349
385
  sleep(self._job_check_interval)
@@ -356,7 +392,7 @@ class ShopifyBulkManager:
356
392
 
357
393
  def _cancel_on_checkpointing(self) -> None:
358
394
  LOGGER.info(f"Stream: `{self.http_client.name}`, checkpointing after >= `{self._job_checkpoint_interval}` rows collected.")
359
- # set the flag to adjust the next slice from the checkpointed cursor value
395
+ self._job_canceled_on_checkpoint = True
360
396
  self._job_cancel()
361
397
 
362
398
  def _on_running_job(self, **kwargs) -> None:
@@ -599,13 +635,23 @@ class ShopifyBulkManager:
599
635
  checkpointed_cursor: Optional[str] = None,
600
636
  filter_checkpointed_cursor: Optional[str] = None,
601
637
  ) -> datetime:
638
+ if self._job_retry_slice_without_checkpoint:
639
+ # re-run the same slice, the checkpointing stays disabled (see `_on_checkpoint_without_result`)
640
+ self._job_retry_slice_without_checkpoint = False
641
+ return slice_start
642
+
602
643
  if self._job_adjust_slice_from_checkpoint:
603
644
  checkpoint_from_failed_job = self._job_checkpoint_from_failed_job
645
+ checkpoint_from_self_cancel = self._job_checkpoint_from_self_cancel
604
646
  # set the checkpointing to default, before the next slice is emitted, to avoid inf.loop
605
647
  self._reset_checkpointing()
606
648
  if checkpoint_from_failed_job and not checkpointed_cursor:
607
649
  # the partial result has no records to resume from, the rest of the slice must not be skipped
608
650
  self._raise_job_without_result("exited with FAILED and its partial result has no records to checkpoint from")
651
+ if checkpoint_from_self_cancel and not checkpointed_cursor:
652
+ # the result has no records to resume from, the slice is re-run with the checkpointing disabled
653
+ self._disable_checkpointing("but its result has no records to checkpoint from")
654
+ return slice_start
609
655
  return self._adjust_slice_end(slice_end, checkpointed_cursor, filter_checkpointed_cursor)
610
656
 
611
657
  if self._is_long_running_job:
@@ -831,7 +831,9 @@ class IncrementalShopifyGraphQlBulkStream(IncrementalShopifyStream):
831
831
  slice_size_message = f"Slice size: `P{round(self.job_manager._job_size, 1)}D`"
832
832
  slice_message = f"Stream: `{self.name}` requesting BULK Job for period: {slice_start} -- {slice_end}. {slice_size_message}."
833
833
 
834
- if self.job_manager._supports_checkpointing:
834
+ if self.job_manager._job_checkpoint_disabled:
835
+ checkpointing_message = f" The BULK checkpointing is disabled for the rest of this sync."
836
+ elif self.job_manager._supports_checkpointing:
835
837
  checkpointing_message = f" The BULK checkpoint after `{self.job_manager.job_checkpoint_interval}` lines."
836
838
  else:
837
839
  checkpointing_message = f" The BULK checkpointing is not supported."
@@ -770,11 +770,14 @@ class Countries(HttpSubStream, FullRefreshShopifyGraphQlBulkStream):
770
770
  _page_cursor = None
771
771
  _sub_page_cursor = None
772
772
 
773
- _synced_countries_ids = []
774
-
775
773
  query = DeliveryProfile
776
774
  response_field = "deliveryProfiles"
777
775
 
776
+ def __init__(self, *args, **kwargs) -> None:
777
+ super().__init__(*args, **kwargs)
778
+ # per instance: a class-level list is shared by every stream built in the same process
779
+ self._synced_countries_ids = []
780
+
778
781
  def stream_slices(
779
782
  self,
780
783
  stream_state: Optional[Mapping[str, Any]] = None,