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.
@@ -14,6 +14,53 @@ from .settings import DEFAULT_API_VERSION, DEFAULT_PARTNER_API_VERSION
14
14
  TOrderStatus = Literal["open", "closed", "cancelled", "any"]
15
15
 
16
16
 
17
+ def convert_datetime_fields(item: Dict[str, Any]) -> Dict[str, Any]:
18
+ """Convert timestamp fields in the item to pendulum datetime objects
19
+
20
+ The item is modified in place, including nested items.
21
+
22
+ Args:
23
+ item: The item to convert
24
+
25
+ Returns:
26
+ The same data item (for convenience)
27
+ """
28
+ fields = ["created_at", "updated_at", "createdAt", "updatedAt"]
29
+
30
+ def convert_nested(obj: Any) -> Any:
31
+ if isinstance(obj, dict):
32
+ for key, value in obj.items():
33
+ if key in fields and isinstance(value, str):
34
+ obj[key] = ensure_pendulum_datetime(value)
35
+ else:
36
+ obj[key] = convert_nested(value)
37
+ elif isinstance(obj, list):
38
+ return [convert_nested(elem) for elem in obj]
39
+ return obj
40
+
41
+ return convert_nested(item)
42
+
43
+
44
+ def remove_nodes_key(item: Any) -> Any:
45
+ """
46
+ Recursively remove the 'nodes' key from dictionaries if it's the only key and its value is an array.
47
+
48
+ Args:
49
+ item: The item to process (can be a dict, list, or any other type)
50
+
51
+ Returns:
52
+ The processed item
53
+ """
54
+ if isinstance(item, dict):
55
+ if len(item) == 1 and "nodes" in item and isinstance(item["nodes"], list):
56
+ return [remove_nodes_key(node) for node in item["nodes"]]
57
+ return {k: remove_nodes_key(v) for k, v in item.items()}
58
+ elif isinstance(item, list):
59
+ return [remove_nodes_key(element) for element in item]
60
+ else:
61
+ return item
62
+
63
+
17
64
  class ShopifyApi:
18
65
  """
19
66
  A Shopify API client that can be used to get pages of data from Shopify.
@@ -50,57 +97,38 @@ class ShopifyApi:
50
97
  """
51
98
  url = urljoin(self.shop_url, f"/admin/api/{self.api_version}/{resource}.json")
52
99
 
100
+ resource_last = resource.split("/")[-1]
101
+
53
102
  headers = {"X-Shopify-Access-Token": self.private_app_password}
54
103
  while url:
55
104
  response = requests.get(url, params=params, headers=headers)
56
105
  response.raise_for_status()
57
106
  json = response.json()
58
- # Get item list from the page
59
- yield [self._convert_datetime_fields(item) for item in json[resource]]
107
+ yield [convert_datetime_fields(item) for item in json[resource_last]]
60
108
  url = response.links.get("next", {}).get("url")
61
109
  # Query params are included in subsequent page URLs
62
110
  params = None
63
111
 
64
- def _convert_datetime_fields(self, item: Dict[str, Any]) -> Dict[str, Any]:
65
- """Convert timestamp fields in the item to pendulum datetime objects
66
-
67
- The item is modified in place.
68
112
 
69
- Args:
70
- item: The item to convert
71
-
72
- Returns:
73
- The same data item (for convenience)
74
- """
75
- fields = ["created_at", "updated_at"]
76
- for field in fields:
77
- if field in item:
78
- item[field] = ensure_pendulum_datetime(item[field])
79
- return item
80
-
81
-
82
- class ShopifyPartnerApi:
83
- """Client for Shopify Partner grapql API"""
113
+ class ShopifyGraphQLApi:
114
+ """Client for Shopify GraphQL API"""
84
115
 
85
116
  def __init__(
86
117
  self,
87
118
  access_token: str,
88
- organization_id: str,
89
119
  api_version: str = DEFAULT_PARTNER_API_VERSION,
120
+ base_url: str = "partners.shopify.com",
90
121
  ) -> None:
91
- """
92
- Args:
93
- access_token: The access token to use
94
- organization_id: The organization id to query
95
- api_version: The API version to use (e.g. 2023-01)
96
- """
97
122
  self.access_token = access_token
98
- self.organization_id = organization_id
99
123
  self.api_version = api_version
124
+ self.base_url = base_url
100
125
 
101
126
  @property
102
127
  def graphql_url(self) -> str:
103
- return f"https://partners.shopify.com/{self.organization_id}/api/{self.api_version}/graphql.json"
128
+ if self.base_url.startswith("https://"):
129
+ return f"{self.base_url}/admin/api/{self.api_version}/graphql.json"
130
+
131
+ return f"https://{self.base_url}/admin/api/{self.api_version}/graphql.json"
104
132
 
105
133
  def run_graphql_query(
106
134
  self, query: str, variables: Optional[DictStrAny] = None
@@ -130,18 +158,31 @@ class ShopifyPartnerApi:
130
158
  query: str,
131
159
  data_items_path: jsonpath.TJsonPath,
132
160
  pagination_cursor_path: jsonpath.TJsonPath,
161
+ pagination_cursor_has_next_page_path: jsonpath.TJsonPath,
133
162
  pagination_variable_name: str,
134
163
  variables: Optional[DictStrAny] = None,
135
164
  ) -> Iterable[TDataItems]:
136
165
  variables = dict(variables or {})
137
166
  while True:
138
167
  data = self.run_graphql_query(query, variables)
139
- print(data)
140
168
  data_items = jsonpath.find_values(data_items_path, data)
169
+
141
170
  if not data_items:
142
171
  break
143
- yield data_items
172
+
173
+ yield [
174
+ remove_nodes_key(convert_datetime_fields(item)) for item in data_items
175
+ ]
176
+
144
177
  cursors = jsonpath.find_values(pagination_cursor_path, data)
145
178
  if not cursors:
146
179
  break
180
+
181
+ if pagination_cursor_has_next_page_path:
182
+ has_next_page = jsonpath.find_values(
183
+ pagination_cursor_has_next_page_path, data
184
+ )
185
+ if not has_next_page or not has_next_page[0]:
186
+ break
187
+
147
188
  variables[pagination_variable_name] = cursors[-1]
ingestr/src/sources.py CHANGED
@@ -1,17 +1,23 @@
1
1
  import base64
2
2
  import csv
3
3
  import json
4
- from datetime import date
4
+ from datetime import date, datetime
5
5
  from typing import Any, Callable, Optional
6
6
  from urllib.parse import parse_qs, urlparse
7
7
 
8
8
  import dlt
9
9
 
10
+ from ingestr.src.adjust._init_ import adjust_source
10
11
  from ingestr.src.airtable import airtable_source
12
+ from ingestr.src.appsflyer._init_ import appsflyer_source
11
13
  from ingestr.src.chess import source
14
+ from ingestr.src.facebook_ads import facebook_ads_source, facebook_insights_source
12
15
  from ingestr.src.google_sheets import google_spreadsheet
13
16
  from ingestr.src.gorgias import gorgias_source
14
17
  from ingestr.src.hubspot import hubspot
18
+ from ingestr.src.kafka import kafka_consumer
19
+ from ingestr.src.kafka.helpers import KafkaCredentials
20
+ from ingestr.src.klaviyo._init_ import klaviyo_source
15
21
  from ingestr.src.mongodb import mongodb_collection
16
22
  from ingestr.src.notion import notion_databases
17
23
  from ingestr.src.shopify import shopify_source
@@ -184,11 +190,6 @@ class ShopifySource:
184
190
  return True
185
191
 
186
192
  def dlt_source(self, uri: str, table: str, **kwargs):
187
- if kwargs.get("incremental_key"):
188
- raise ValueError(
189
- "Shopify takes care of incrementality on its own, you should not provide incremental_key"
190
- )
191
-
192
193
  source_fields = urlparse(uri)
193
194
  source_params = parse_qs(source_fields.query)
194
195
  api_key = source_params.get("api_key")
@@ -203,7 +204,19 @@ class ShopifySource:
203
204
  date_args["end_date"] = kwargs.get("interval_end")
204
205
 
205
206
  resource = None
206
- if table in ["products", "orders", "customers"]:
207
+ if table in [
208
+ "products",
209
+ "products_legacy",
210
+ "orders",
211
+ "customers",
212
+ "inventory_items",
213
+ "transactions",
214
+ "balance",
215
+ "events",
216
+ "price_rules",
217
+ "discounts",
218
+ "taxonomy",
219
+ ]:
207
220
  resource = table
208
221
  else:
209
222
  raise ValueError(
@@ -405,6 +418,48 @@ class StripeAnalyticsSource:
405
418
  ).with_resources(endpoint)
406
419
 
407
420
 
421
+ class FacebookAdsSource:
422
+ def handles_incrementality(self) -> bool:
423
+ return True
424
+
425
+ def dlt_source(self, uri: str, table: str, **kwargs):
426
+ # facebook_ads://?access_token=abcd&account_id=1234
427
+ if kwargs.get("incremental_key"):
428
+ raise ValueError(
429
+ "Facebook Ads takes care of incrementality on its own, you should not provide incremental_key"
430
+ )
431
+
432
+ access_token = None
433
+ account_id = None
434
+ source_field = urlparse(uri)
435
+ source_params = parse_qs(source_field.query)
436
+ access_token = source_params.get("access_token")
437
+ account_id = source_params.get("account_id")
438
+
439
+ if not access_token or not account_id:
440
+ raise ValueError(
441
+ "access_token and accound_id are required to connect to Facebook Ads."
442
+ )
443
+
444
+ endpoint = None
445
+ if table in ["campaigns", "ad_sets", "ad_creatives", "ads", "leads"]:
446
+ endpoint = table
447
+ elif table in "facebook_insights":
448
+ return facebook_insights_source(
449
+ access_token=access_token[0],
450
+ account_id=account_id[0],
451
+ ).with_resources("facebook_insights")
452
+ else:
453
+ raise ValueError(
454
+ "fResource '{table}' is not supported for Facebook Ads source yet, if you are interested in it please create a GitHub issue at https://github.com/bruin-data/ingestr"
455
+ )
456
+
457
+ return facebook_ads_source(
458
+ access_token=access_token[0],
459
+ account_id=account_id[0],
460
+ ).with_resources(endpoint)
461
+
462
+
408
463
  class SlackSource:
409
464
  def handles_incrementality(self) -> bool:
410
465
  return True
@@ -511,3 +566,171 @@ class AirtableSource:
511
566
  return airtable_source(
512
567
  base_id=base_id[0], table_names=tables, access_token=access_token[0]
513
568
  )
569
+
570
+
571
+ class KlaviyoSource:
572
+ def handles_incrementality(self) -> bool:
573
+ return True
574
+
575
+ def dlt_source(self, uri: str, table: str, **kwargs):
576
+ if kwargs.get("incremental_key"):
577
+ raise ValueError(
578
+ "klaviyo_source takes care of incrementality on its own, you should not provide incremental_key"
579
+ )
580
+
581
+ source_fields = urlparse(uri)
582
+ source_params = parse_qs(source_fields.query)
583
+ api_key = source_params.get("api_key")
584
+
585
+ if not api_key:
586
+ raise ValueError("api_key in the URI is required to connect to klaviyo")
587
+
588
+ resource = None
589
+ if table in [
590
+ "events",
591
+ "profiles",
592
+ "campaigns",
593
+ "metrics",
594
+ "tags",
595
+ "coupons",
596
+ "catalog-variants",
597
+ "catalog-categories",
598
+ "catalog-items",
599
+ "forms",
600
+ "lists",
601
+ "images",
602
+ "segments",
603
+ "flows",
604
+ "templates",
605
+ ]:
606
+ resource = table
607
+ else:
608
+ raise ValueError(
609
+ f"Resource '{table}' is not supported for Klaviyo source yet, if you are interested in it please create a GitHub issue at https://github.com/bruin-data/ingestr"
610
+ )
611
+
612
+ start_date = kwargs.get("interval_start") or "2000-01-01"
613
+ return klaviyo_source(
614
+ api_key=api_key[0],
615
+ start_date=start_date,
616
+ ).with_resources(resource)
617
+
618
+
619
+ class KafkaSource:
620
+ def handles_incrementality(self) -> bool:
621
+ return False
622
+
623
+ def dlt_source(self, uri: str, table: str, **kwargs):
624
+ # kafka://?bootstrap_servers=localhost:9092&group_id=test_group&security_protocol=SASL_SSL&sasl_mechanisms=PLAIN&sasl_username=example_username&sasl_password=example_secret
625
+ source_fields = urlparse(uri)
626
+ source_params = parse_qs(source_fields.query)
627
+
628
+ bootstrap_servers = source_params.get("bootstrap_servers")
629
+ group_id = source_params.get("group_id")
630
+ security_protocol = source_params.get("security_protocol", [])
631
+ sasl_mechanisms = source_params.get("sasl_mechanisms", [])
632
+ sasl_username = source_params.get("sasl_username", [])
633
+ sasl_password = source_params.get("sasl_password", [])
634
+ batch_size = source_params.get("batch_size", [3000])
635
+ batch_timeout = source_params.get("batch_timeout", [3])
636
+
637
+ if not bootstrap_servers:
638
+ raise ValueError(
639
+ "bootstrap_servers in the URI is required to connect to kafka"
640
+ )
641
+
642
+ if not group_id:
643
+ raise ValueError("group_id in the URI is required to connect to kafka")
644
+
645
+ start_date = kwargs.get("interval_start")
646
+ return kafka_consumer(
647
+ topics=[table],
648
+ credentials=KafkaCredentials(
649
+ bootstrap_servers=bootstrap_servers[0],
650
+ group_id=group_id[0],
651
+ security_protocol=security_protocol[0]
652
+ if len(security_protocol) > 0
653
+ else None, # type: ignore
654
+ sasl_mechanisms=sasl_mechanisms[0]
655
+ if len(sasl_mechanisms) > 0
656
+ else None, # type: ignore
657
+ sasl_username=sasl_username[0] if len(sasl_username) > 0 else None, # type: ignore
658
+ sasl_password=sasl_password[0] if len(sasl_password) > 0 else None, # type: ignore
659
+ ),
660
+ start_from=start_date,
661
+ batch_size=int(batch_size[0]),
662
+ batch_timeout=int(batch_timeout[0]),
663
+ )
664
+
665
+
666
+ class AdjustSource:
667
+ def handles_incrementality(self) -> bool:
668
+ return True
669
+
670
+ def dlt_source(self, uri: str, table: str, **kwargs):
671
+ if kwargs.get("incremental_key"):
672
+ raise ValueError(
673
+ "Adjust takes care of incrementality on its own, you should not provide incremental_key"
674
+ )
675
+
676
+ source_part = urlparse(uri)
677
+ source_params = parse_qs(source_part.query)
678
+ api_key = source_params.get("api_key")
679
+
680
+ if not api_key:
681
+ raise ValueError("api_key in the URI is required to connect to Adjust")
682
+
683
+ interval_start = kwargs.get("interval_start")
684
+ interval_end = kwargs.get("interval_end")
685
+
686
+ start_date = (
687
+ interval_start.strftime("%Y-%m-%d") if interval_start else "2000-01-01"
688
+ )
689
+ end_date = (
690
+ interval_end.strftime("%Y-%m-%d")
691
+ if interval_end
692
+ else datetime.now().strftime("%Y-%m-%d")
693
+ )
694
+
695
+ Endpoint = None
696
+ if table in ["campaigns", "creatives"]:
697
+ Endpoint = table
698
+
699
+ return adjust_source(
700
+ start_date=start_date, end_date=end_date, api_key=api_key[0]
701
+ ).with_resources(Endpoint)
702
+
703
+
704
+ class AppsflyerSource:
705
+ def handles_incrementality(self) -> bool:
706
+ return True
707
+
708
+ def dlt_source(self, uri: str, table: str, **kwargs):
709
+ if kwargs.get("incremental_key"):
710
+ raise ValueError(
711
+ "Appsflyer_Source takes care of incrementality on its own, you should not provide incremental_key"
712
+ )
713
+
714
+ source_fields = urlparse(uri)
715
+ source_params = parse_qs(source_fields.query)
716
+ api_key = source_params.get("api_key")
717
+
718
+ if not api_key:
719
+ raise ValueError("api_key in the URI is required to connect to Appsflyer")
720
+
721
+ resource = None
722
+ if table in ["campaigns", "creatives"]:
723
+ resource = table
724
+ else:
725
+ raise ValueError(
726
+ f"Resource '{table}' is not supported for Appsflyer source yet, if you are interested in it please create a GitHub issue at https://github.com/bruin-data/ingestr"
727
+ )
728
+
729
+ start_date = kwargs.get("interval_start") or "2024-01-02"
730
+ end_date = kwargs.get("interval_end") or "2024-01-29"
731
+
732
+ return appsflyer_source(
733
+ api_key=api_key[0],
734
+ start_date=start_date,
735
+ end_date=end_date,
736
+ ).with_resources(resource)
ingestr/src/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.7.7"
1
+ __version__ = "0.8.1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ingestr
3
- Version: 0.7.7
3
+ Version: 0.8.1
4
4
  Summary: ingestr is a command-line application that ingests data from various sources and stores them in any database.
5
5
  Project-URL: Homepage, https://github.com/bruin-data/ingestr
6
6
  Project-URL: Issues, https://github.com/bruin-data/ingestr/issues
@@ -14,11 +14,13 @@ Classifier: Operating System :: OS Independent
14
14
  Classifier: Programming Language :: Python :: 3
15
15
  Classifier: Topic :: Database
16
16
  Requires-Python: >=3.9
17
+ Requires-Dist: confluent-kafka>=2.3.0
17
18
  Requires-Dist: cx-oracle==8.3.0
18
19
  Requires-Dist: databricks-sql-connector==2.9.3
19
20
  Requires-Dist: dlt==0.5.1
20
21
  Requires-Dist: duckdb-engine==0.11.5
21
22
  Requires-Dist: duckdb==0.10.2
23
+ Requires-Dist: facebook-business==20.0.0
22
24
  Requires-Dist: google-api-python-client==2.130.0
23
25
  Requires-Dist: google-cloud-bigquery-storage==2.24.0
24
26
  Requires-Dist: mysql-connector-python==9.0.0
@@ -42,6 +44,7 @@ Requires-Dist: sqlalchemy==1.4.52
42
44
  Requires-Dist: stripe==10.7.0
43
45
  Requires-Dist: tqdm==4.66.2
44
46
  Requires-Dist: typer==0.12.3
47
+ Requires-Dist: types-requests==2.32.0.20240907
45
48
  Description-Content-Type: text/markdown
46
49
 
47
50
  <div align="center">
@@ -176,15 +179,28 @@ Join our Slack community [here](https://join.slack.com/t/bruindatacommunity/shar
176
179
  <tr>
177
180
  <td colspan="3" style='text-align:center;'><strong>Platforms</strong></td>
178
181
  </tr>
182
+ <td>Adjust</td>
183
+ <td>✅</td>
184
+ <td>-</td>
179
185
  <tr>
180
186
  <td>Airtable</td>
181
187
  <td>✅</td>
182
188
  <td>-</td>
189
+ </tr>
190
+ <tr>
191
+ <td>AppsFlyer</td>
192
+ <td>✅</td>
193
+ <td>-</td>
183
194
  </tr>
184
195
  <tr>
185
196
  <td>Chess.com</td>
186
197
  <td>✅</td>
187
198
  <td>-</td>
199
+ </tr>
200
+ <tr>
201
+ <td>Facebook Ads</td>
202
+ <td>✅</td>
203
+ <td>-</td>
188
204
  </tr>
189
205
  <tr>
190
206
  <td>Gorgias</td>
@@ -200,6 +216,11 @@ Join our Slack community [here](https://join.slack.com/t/bruindatacommunity/shar
200
216
  <td>HubSpot</td>
201
217
  <td>✅</td>
202
218
  <td>-</td>
219
+ </tr>
220
+ <tr>
221
+ <td>Klaviyo</td>
222
+ <td>✅</td>
223
+ <td>-</td>
203
224
  </tr>
204
225
  <tr>
205
226
  <td>Notion</td>
@@ -1,13 +1,22 @@
1
- ingestr/main.py,sha256=dUh9D-i5EJJKJQOu29okpPebJ6y9w0unTCNm6f7k2Bk,17184
1
+ ingestr/main.py,sha256=U66TM57ePv-RdoAftQ0pFZx8woZUQnLepKa50C-bA5I,17655
2
+ ingestr/src/.gitignore,sha256=8cX1AZTSI0TcdZFGTmS_oyBjpfCzhOEt0DdAo2dFIY8,203
2
3
  ingestr/src/destinations.py,sha256=2SfPMjtTelPmzQmc3zNs8xGcKIPuGn_hoZFIBUuhjXI,6338
3
- ingestr/src/factory.py,sha256=OOHlQ4TZNG-1FVK-w7nnKfYVmQE7GLJlqw-gPxCUfg0,4136
4
- ingestr/src/sources.py,sha256=Wi74uPHjPzdoIXBY5Ha4ZW1Mdh2L3xAiMWnZekPu4ws,17307
4
+ ingestr/src/factory.py,sha256=-_KwBpbNAegv_oXIB57klyjUb3K6e0lw_xdi5bwmarI,4645
5
+ ingestr/src/sources.py,sha256=0IGguMm85E3Rahu6zVLawoe2d4lqRY31uHuxlqCsiQc,25324
5
6
  ingestr/src/table_definition.py,sha256=REbAbqdlmUMUuRh8nEQRreWjPVOQ5ZcfqGkScKdCrmk,390
6
- ingestr/src/version.py,sha256=eOm8myGPtPLNpkuxL0xhVmstPQbwXv3Ok7FbH0re-TA,22
7
+ ingestr/src/version.py,sha256=Ocl79hbbH8_jdr5dGC90VR1cAvZc05Rc0tkZttUnMjo,22
8
+ ingestr/src/adjust/_init_.py,sha256=_jJE3Ywvv-YyJ7ywICdht_X2Gnd1cKm6F1wAfnpXuWM,890
9
+ ingestr/src/adjust/helpers.py,sha256=kkYC3MqMHLNucuQ50klZWrvd3o8VfUysNtZTQSsKZ_c,2588
7
10
  ingestr/src/airtable/__init__.py,sha256=GHWYrjI2qhs_JihdNJysB0Ni3bzqT_MLXn_S9_Q5zRA,2775
11
+ ingestr/src/appsflyer/_init_.py,sha256=ne2-9FQ654Drtd3GkKQv8Bwb6LEqCnJw49MfO5Jyzgs,739
12
+ ingestr/src/appsflyer/client.py,sha256=TNmwakLzmO6DZW3wcfLfQRl7aNBHgFqSsk4ef-MmJ1w,3084
8
13
  ingestr/src/chess/__init__.py,sha256=PaxT2DObudOGlhyoENE5LjR6rTdsxiqKKpAZeyzVLCA,6791
9
14
  ingestr/src/chess/helpers.py,sha256=v1HTImOMjAF7AzZUPDIuHu00e7ut0o5y1kWcVYo4QZw,549
10
15
  ingestr/src/chess/settings.py,sha256=p0RlCGgtXUacPDEvZmwzSWmzX0Apj1riwfz-nrMK89k,158
16
+ ingestr/src/facebook_ads/__init__.py,sha256=ZZyogV48gmhDcC3CYQEsC4qT3Q6JI9IOnMff2NS1M-A,9207
17
+ ingestr/src/facebook_ads/exceptions.py,sha256=4Nlbc0Mv3i5g-9AoyT-n1PIa8IDi3VCTfEAzholx4Wc,115
18
+ ingestr/src/facebook_ads/helpers.py,sha256=ZLbNHiKer5lPb4g3_435XeBJr57Wv0o1KTyBA1mQ100,9068
19
+ ingestr/src/facebook_ads/settings.py,sha256=1IxZeP_4rN3IBvAncNHOoqpzAirx0Hz-MUK_tl6UTFk,4881
11
20
  ingestr/src/google_sheets/README.md,sha256=wFQhvmGpRA38Ba2N_WIax6duyD4c7c_pwvvprRfQDnw,5470
12
21
  ingestr/src/google_sheets/__init__.py,sha256=5qlX-6ilx5MW7klC7B_0jGSxloQSLkSESTh4nlY3Aos,6643
13
22
  ingestr/src/google_sheets/helpers/__init__.py,sha256=5hXZrZK8cMO3UOuL-s4OKOpdACdihQD0hYYlSEu-iQ8,35
@@ -18,6 +27,11 @@ ingestr/src/gorgias/helpers.py,sha256=DamuijnvhGY9hysQO4txrVMf4izkGbh5qfBKImdOIN
18
27
  ingestr/src/hubspot/__init__.py,sha256=eSD_lEIEd16YijAtUATFG8FGO8YGPm-MtAk94KKsx6o,9740
19
28
  ingestr/src/hubspot/helpers.py,sha256=PTn-UHJv1ENIvA5azUTaHCmFXgmHLJC1tUatQ1N-KFE,6727
20
29
  ingestr/src/hubspot/settings.py,sha256=9P1OKiRL88kl_m8n1HhuG-Qpq9VGbqPLn5Q0QYneToU,2193
30
+ ingestr/src/kafka/__init__.py,sha256=wMCXdiraeKd1Kssi9WcVCGZaNGm2tJEtnNyuB4aR5_k,3541
31
+ ingestr/src/kafka/helpers.py,sha256=V9WcVn3PKnEpggArHda4vnAcaV8VDuh__dSmRviJb5Y,7502
32
+ ingestr/src/klaviyo/_init_.py,sha256=nq2T1p3Xc7yiwGabsZBp2Jy2fa8_n5oxqxBnUGhKOgg,6592
33
+ ingestr/src/klaviyo/client.py,sha256=tPj79ia7AW0ZOJhzlKNPCliGbdojRNwUFp8HvB2ym5s,7434
34
+ ingestr/src/klaviyo/helpers.py,sha256=_i-SHffhv25feLDcjy6Blj1UxYLISCwVCMgGtrlnYHk,496
21
35
  ingestr/src/mongodb/__init__.py,sha256=E7SDeCyYNkYZZ_RFhjCRDZUGpKtaxpPG5sFSmKJV62U,4336
22
36
  ingestr/src/mongodb/helpers.py,sha256=80vtAeNyUn1iMN0CeLrTlKqYN6I6fHF81Kd2UuE8Kns,5653
23
37
  ingestr/src/notion/__init__.py,sha256=36wUui8finbc85ObkRMq8boMraXMUehdABN_AMe_hzA,1834
@@ -25,9 +39,9 @@ ingestr/src/notion/settings.py,sha256=MwQVZViJtnvOegfjXYc_pJ50oUYgSRPgwqu7TvpeMO
25
39
  ingestr/src/notion/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
40
  ingestr/src/notion/helpers/client.py,sha256=QXuudkf5Zzff98HRsCqA1g1EZWIrnfn1falPrnKg_y4,5500
27
41
  ingestr/src/notion/helpers/database.py,sha256=gigPibTeVefP3lA-8w4aOwX67pj7RlciPk5koDs1ry8,2737
28
- ingestr/src/shopify/__init__.py,sha256=EWjpvZz7K6Pms7uUoqqkM4Wj0XeE2NrDvVp4BNM8dPk,9163
42
+ ingestr/src/shopify/__init__.py,sha256=uqYoodwLHLuZHwvQx_lwOI23TzxnzHbGuZUUNzTA2Os,62031
29
43
  ingestr/src/shopify/exceptions.py,sha256=BhV3lIVWeBt8Eh4CWGW_REFJpGCzvW6-62yZrBWa3nQ,50
30
- ingestr/src/shopify/helpers.py,sha256=OO_Tw-HwVLnRhwT3vqUWEQEEcWIS9KWE6VDDe8BCC2w,4972
44
+ ingestr/src/shopify/helpers.py,sha256=2MlqyCc7VJxpYlGAxw-bYpSuCZNfat0E5zDe1jQYVP4,6279
31
45
  ingestr/src/shopify/settings.py,sha256=StY0EPr7wFJ7KzRRDN4TKxV0_gkIS1wPj2eR4AYSsDk,141
32
46
  ingestr/src/slack/__init__.py,sha256=UfUhkS6FnCKJeXkkJ5QrmdT5nZm5czjtomsQu_x9WUM,9987
33
47
  ingestr/src/slack/helpers.py,sha256=08TLK7vhFvH_uekdLVOLF3bTDe1zgH0QxHObXHzk1a8,6545
@@ -50,8 +64,8 @@ ingestr/testdata/delete_insert_part2.csv,sha256=B_KUzpzbNdDY_n7wWop1mT2cz36TmayS
50
64
  ingestr/testdata/merge_expected.csv,sha256=DReHqWGnQMsf2PBv_Q2pfjsgvikYFnf1zYcQZ7ZqYN0,276
51
65
  ingestr/testdata/merge_part1.csv,sha256=Pw8Z9IDKcNU0qQHx1z6BUf4rF_-SxKGFOvymCt4OY9I,185
52
66
  ingestr/testdata/merge_part2.csv,sha256=T_GiWxA81SN63_tMOIuemcvboEFeAmbKc7xRXvL9esw,287
53
- ingestr-0.7.7.dist-info/METADATA,sha256=zzWW5as6aboO_I7_iD02tMTDOS_yiAWHqLddbjxOLEY,6307
54
- ingestr-0.7.7.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
55
- ingestr-0.7.7.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
56
- ingestr-0.7.7.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
57
- ingestr-0.7.7.dist-info/RECORD,,
67
+ ingestr-0.8.1.dist-info/METADATA,sha256=r3eBIXMFdnmLPzlK7mh0BZWRJoHvXyZjls7UxkPb93Q,6755
68
+ ingestr-0.8.1.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
69
+ ingestr-0.8.1.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
70
+ ingestr-0.8.1.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
71
+ ingestr-0.8.1.dist-info/RECORD,,