ingestr 0.13.27__py3-none-any.whl → 0.13.29__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.
Potentially problematic release.
This version of ingestr might be problematic. Click here for more details.
- ingestr/main.py +10 -0
- ingestr/src/buildinfo.py +1 -1
- ingestr/src/hubspot/__init__.py +30 -3
- ingestr/src/hubspot/helpers.py +25 -14
- ingestr/src/hubspot/settings.py +13 -7
- ingestr/src/sources.py +14 -1
- {ingestr-0.13.27.dist-info → ingestr-0.13.29.dist-info}/METADATA +1 -1
- {ingestr-0.13.27.dist-info → ingestr-0.13.29.dist-info}/RECORD +11 -11
- {ingestr-0.13.27.dist-info → ingestr-0.13.29.dist-info}/WHEEL +0 -0
- {ingestr-0.13.27.dist-info → ingestr-0.13.29.dist-info}/entry_points.txt +0 -0
- {ingestr-0.13.27.dist-info → ingestr-0.13.29.dist-info}/licenses/LICENSE.md +0 -0
ingestr/main.py
CHANGED
|
@@ -302,6 +302,13 @@ def ingest(
|
|
|
302
302
|
envvar=["COLUMNS", "INGESTR_COLUMNS"],
|
|
303
303
|
),
|
|
304
304
|
] = None, # type: ignore
|
|
305
|
+
yield_limit: Annotated[
|
|
306
|
+
Optional[int],
|
|
307
|
+
typer.Option(
|
|
308
|
+
help="Limit the number of pages yielded from the source",
|
|
309
|
+
envvar=["YIELD_LIMIT", "INGESTR_YIELD_LIMIT"],
|
|
310
|
+
),
|
|
311
|
+
] = None, # type: ignore
|
|
305
312
|
):
|
|
306
313
|
import hashlib
|
|
307
314
|
import tempfile
|
|
@@ -556,6 +563,9 @@ def ingest(
|
|
|
556
563
|
if factory.source_scheme.startswith("mysql"):
|
|
557
564
|
resource.for_each(dlt_source, lambda x: x.add_map(handle_mysql_empty_dates))
|
|
558
565
|
|
|
566
|
+
if yield_limit:
|
|
567
|
+
resource.for_each(dlt_source, lambda x: x.add_limit(yield_limit))
|
|
568
|
+
|
|
559
569
|
def col_h(x):
|
|
560
570
|
if column_hints:
|
|
561
571
|
x.apply_hints(columns=column_hints)
|
ingestr/src/buildinfo.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
version = "v0.13.
|
|
1
|
+
version = "v0.13.29"
|
ingestr/src/hubspot/__init__.py
CHANGED
|
@@ -56,6 +56,7 @@ def hubspot(
|
|
|
56
56
|
api_key: str = dlt.secrets.value,
|
|
57
57
|
include_history: bool = False,
|
|
58
58
|
include_custom_props: bool = True,
|
|
59
|
+
custom_object: str = None,
|
|
59
60
|
) -> Sequence[DltResource]:
|
|
60
61
|
"""
|
|
61
62
|
A DLT source that retrieves data from the HubSpot API using the
|
|
@@ -162,13 +163,13 @@ def hubspot(
|
|
|
162
163
|
props,
|
|
163
164
|
include_custom_props,
|
|
164
165
|
)
|
|
165
|
-
|
|
166
|
+
|
|
166
167
|
@dlt.resource(name="schemas", write_disposition="merge", primary_key="id")
|
|
167
168
|
def schemas(
|
|
168
169
|
api_key: str = api_key,
|
|
169
170
|
) -> Iterator[TDataItems]:
|
|
170
171
|
"""Hubspot schemas resource"""
|
|
171
|
-
yield from fetch_data(CRM_SCHEMAS_ENDPOINT, api_key)
|
|
172
|
+
yield from fetch_data(CRM_SCHEMAS_ENDPOINT, api_key, resource_name="schemas")
|
|
172
173
|
|
|
173
174
|
@dlt.resource(name="quotes", write_disposition="replace")
|
|
174
175
|
def quotes(
|
|
@@ -186,7 +187,33 @@ def hubspot(
|
|
|
186
187
|
include_custom_props,
|
|
187
188
|
)
|
|
188
189
|
|
|
189
|
-
|
|
190
|
+
@dlt.resource(write_disposition="merge", primary_key="hs_object_id")
|
|
191
|
+
def custom(
|
|
192
|
+
api_key: str = api_key,
|
|
193
|
+
custom_object_name: str = custom_object,
|
|
194
|
+
) -> Iterator[TDataItems]:
|
|
195
|
+
get_custom_object = schemas(api_key)
|
|
196
|
+
object_type_id = None
|
|
197
|
+
for custom_object in get_custom_object:
|
|
198
|
+
if custom_object["name"] == custom_object_name.capitalize():
|
|
199
|
+
object_type_id = custom_object["objectTypeId"]
|
|
200
|
+
break
|
|
201
|
+
if object_type_id is None:
|
|
202
|
+
raise ValueError(f"There is no such custom object as {custom_object_name}")
|
|
203
|
+
custom_object_properties = f"crm/v3/properties/{object_type_id}"
|
|
204
|
+
|
|
205
|
+
props_pages = fetch_data(custom_object_properties, api_key)
|
|
206
|
+
props = []
|
|
207
|
+
for page in props_pages:
|
|
208
|
+
props.extend([prop["name"] for prop in page])
|
|
209
|
+
props = ",".join(sorted(list(set(props))))
|
|
210
|
+
|
|
211
|
+
custom_object_endpoint = f"crm/v3/objects/{object_type_id}/?properties={props}"
|
|
212
|
+
|
|
213
|
+
"""Hubspot custom object details resource"""
|
|
214
|
+
yield from fetch_data(custom_object_endpoint, api_key, resource_name="custom")
|
|
215
|
+
|
|
216
|
+
return companies, contacts, deals, tickets, products, quotes, schemas, custom
|
|
190
217
|
|
|
191
218
|
|
|
192
219
|
def crm_objects(
|
ingestr/src/hubspot/helpers.py
CHANGED
|
@@ -90,7 +90,10 @@ def fetch_property_history(
|
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
def fetch_data(
|
|
93
|
-
endpoint: str,
|
|
93
|
+
endpoint: str,
|
|
94
|
+
api_key: str,
|
|
95
|
+
params: Optional[Dict[str, Any]] = None,
|
|
96
|
+
resource_name: str = None,
|
|
94
97
|
) -> Iterator[List[Dict[str, Any]]]:
|
|
95
98
|
"""
|
|
96
99
|
Fetch data from HUBSPOT endpoint using a specified API key and yield the properties of each result.
|
|
@@ -132,16 +135,22 @@ def fetch_data(
|
|
|
132
135
|
if "results" in _data:
|
|
133
136
|
_objects: List[Dict[str, Any]] = []
|
|
134
137
|
for _result in _data["results"]:
|
|
135
|
-
if
|
|
136
|
-
_objects.append(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
138
|
+
if resource_name == "schemas":
|
|
139
|
+
_objects.append(
|
|
140
|
+
{
|
|
141
|
+
"name": _result["labels"].get("singular", ""),
|
|
142
|
+
"objectTypeId": _result.get("objectTypeId", ""),
|
|
143
|
+
"id": _result.get("id", ""),
|
|
144
|
+
"fullyQualifiedName": _result.get("fullyQualifiedName", ""),
|
|
145
|
+
"properties": _result.get("properties", ""),
|
|
146
|
+
"createdAt": _result.get("createdAt", ""),
|
|
147
|
+
"updatedAt": _result.get("updatedAt", ""),
|
|
148
|
+
}
|
|
149
|
+
)
|
|
150
|
+
elif resource_name == "custom":
|
|
151
|
+
_objects.append(
|
|
152
|
+
_result.get("properties", ""),
|
|
153
|
+
)
|
|
145
154
|
else:
|
|
146
155
|
_obj = _result.get("properties", _result)
|
|
147
156
|
if "id" not in _obj and "id" in _result:
|
|
@@ -153,9 +162,11 @@ def fetch_data(
|
|
|
153
162
|
{
|
|
154
163
|
"value": _obj["hs_object_id"],
|
|
155
164
|
f"{association}_id": __r["id"],
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
165
|
+
}
|
|
166
|
+
for __r in _result["associations"][association][
|
|
167
|
+
"results"
|
|
168
|
+
]
|
|
169
|
+
]
|
|
159
170
|
|
|
160
171
|
# remove duplicates from list of dicts
|
|
161
172
|
__values = [
|
ingestr/src/hubspot/settings.py
CHANGED
|
@@ -5,15 +5,21 @@ from dlt.common import pendulum
|
|
|
5
5
|
STARTDATE = pendulum.datetime(year=2000, month=1, day=1)
|
|
6
6
|
|
|
7
7
|
CRM_CONTACTS_ENDPOINT = (
|
|
8
|
-
"/crm/v3/objects/contacts?associations=deals,products,tickets,quotes"
|
|
8
|
+
"/crm/v3/objects/contacts?associations=companies,deals,products,tickets,quotes"
|
|
9
9
|
)
|
|
10
|
-
CRM_COMPANIES_ENDPOINT =
|
|
11
|
-
|
|
10
|
+
CRM_COMPANIES_ENDPOINT = "/crm/v3/objects/companies?associations=products"
|
|
11
|
+
CRM_DEALS_ENDPOINT = (
|
|
12
|
+
"/crm/v3/objects/deals?associations=companies,contacts,products,tickets,quotes"
|
|
13
|
+
)
|
|
14
|
+
CRM_PRODUCTS_ENDPOINT = (
|
|
15
|
+
"/crm/v3/objects/products?associations=companies,contacts,deals,tickets,quotes"
|
|
16
|
+
)
|
|
17
|
+
CRM_TICKETS_ENDPOINT = (
|
|
18
|
+
"/crm/v3/objects/tickets?associations=companies,contacts,deals,products,quotes"
|
|
19
|
+
)
|
|
20
|
+
CRM_QUOTES_ENDPOINT = (
|
|
21
|
+
"/crm/v3/objects/quotes?associations=companies,contacts,deals,products,tickets"
|
|
12
22
|
)
|
|
13
|
-
CRM_DEALS_ENDPOINT = "/crm/v3/objects/deals"
|
|
14
|
-
CRM_PRODUCTS_ENDPOINT = "/crm/v3/objects/products"
|
|
15
|
-
CRM_TICKETS_ENDPOINT = "/crm/v3/objects/tickets"
|
|
16
|
-
CRM_QUOTES_ENDPOINT = "/crm/v3/objects/quotes"
|
|
17
23
|
CRM_SCHEMAS_ENDPOINT = "/crm/v3/schemas"
|
|
18
24
|
|
|
19
25
|
CRM_OBJECT_ENDPOINTS = {
|
ingestr/src/sources.py
CHANGED
|
@@ -803,7 +803,20 @@ class HubspotSource:
|
|
|
803
803
|
raise ValueError("api_key in the URI is required to connect to Hubspot")
|
|
804
804
|
|
|
805
805
|
endpoint = None
|
|
806
|
-
|
|
806
|
+
|
|
807
|
+
if table.startswith("custom:"):
|
|
808
|
+
fields = table.split(":", 2)
|
|
809
|
+
if len(fields) != 2:
|
|
810
|
+
raise ValueError(
|
|
811
|
+
"Invalid Hubspot custom table format. Expected format: custom:<custom_object_type>"
|
|
812
|
+
)
|
|
813
|
+
endpoint = fields[1]
|
|
814
|
+
return hubspot(
|
|
815
|
+
api_key=api_key[0],
|
|
816
|
+
custom_object=endpoint,
|
|
817
|
+
).with_resources("custom")
|
|
818
|
+
|
|
819
|
+
elif table in ["contacts", "companies", "deals", "tickets", "products", "quotes", "schemas"]:
|
|
807
820
|
endpoint = table
|
|
808
821
|
else:
|
|
809
822
|
raise ValueError(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ingestr
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.29
|
|
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
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
ingestr/conftest.py,sha256=Q03FIJIZpLBbpj55cfCHIKEjc1FCvWJhMF2cidUJKQU,1748
|
|
2
|
-
ingestr/main.py,sha256=
|
|
2
|
+
ingestr/main.py,sha256=Vt5NFN59BUlkYrOwiF9xF5MCFYp9r-aVSnQ99f1ypRE,25964
|
|
3
3
|
ingestr/src/.gitignore,sha256=8cX1AZTSI0TcdZFGTmS_oyBjpfCzhOEt0DdAo2dFIY8,203
|
|
4
4
|
ingestr/src/blob.py,sha256=onMe5ZHxPXTdcB_s2oGNdMo-XQJ3ajwOsWE9eSTGFmc,1495
|
|
5
|
-
ingestr/src/buildinfo.py,sha256=
|
|
5
|
+
ingestr/src/buildinfo.py,sha256=fraAO4VHAeBDwo-MJW7EhZKMHMio-7qqLE2lULqrTHA,21
|
|
6
6
|
ingestr/src/destinations.py,sha256=vrGij4qMPCdXTMIimROWBJFqzOqCM4DFmgyubgSHejA,11279
|
|
7
7
|
ingestr/src/errors.py,sha256=Ufs4_DfE77_E3vnA1fOQdi6cmuLVNm7_SbFLkL1XPGk,686
|
|
8
8
|
ingestr/src/factory.py,sha256=659h_sVRBhtPv2dvtOK8tf3PtUhlK3KsWLrb20_iQKw,5333
|
|
@@ -10,7 +10,7 @@ ingestr/src/filters.py,sha256=5LNpBgm8FJXdrFHGyM7dLVyphKykSpPk7yuQAZ8GML4,1133
|
|
|
10
10
|
ingestr/src/loader.py,sha256=9NaWAyfkXdqAZSS-N72Iwo36Lbx4PyqIfaaH1dNdkFs,1712
|
|
11
11
|
ingestr/src/partition.py,sha256=E0WHqh1FTheQAIVK_-jWUx0dgyYZCD1VxlAm362gao4,964
|
|
12
12
|
ingestr/src/resource.py,sha256=XG-sbBapFVEM7OhHQFQRTdTLlh-mHB-N4V1t8F8Tsww,543
|
|
13
|
-
ingestr/src/sources.py,sha256=
|
|
13
|
+
ingestr/src/sources.py,sha256=GNX3dLFFqNxe6-humD46PZI9nh84tyuiwh9Cl0RwUg8,74309
|
|
14
14
|
ingestr/src/table_definition.py,sha256=REbAbqdlmUMUuRh8nEQRreWjPVOQ5ZcfqGkScKdCrmk,390
|
|
15
15
|
ingestr/src/time.py,sha256=H_Fk2J4ShXyUM-EMY7MqCLZQhlnZMZvO952bmZPc4yE,254
|
|
16
16
|
ingestr/src/version.py,sha256=J_2xgZ0mKlvuHcjdKCx2nlioneLH0I47JiU_Slr_Nwc,189
|
|
@@ -61,9 +61,9 @@ ingestr/src/google_sheets/helpers/api_calls.py,sha256=RiVfdacbaneszhmuhYilkJnkc9
|
|
|
61
61
|
ingestr/src/google_sheets/helpers/data_processing.py,sha256=RNt2MYfdJhk4bRahnQVezpNg2x9z0vx60YFq2ukZ8vI,11004
|
|
62
62
|
ingestr/src/gorgias/__init__.py,sha256=_mFkMYwlY5OKEY0o_FK1OKol03A-8uk7bm1cKlmt5cs,21432
|
|
63
63
|
ingestr/src/gorgias/helpers.py,sha256=DamuijnvhGY9hysQO4txrVMf4izkGbh5qfBKImdOINE,5427
|
|
64
|
-
ingestr/src/hubspot/__init__.py,sha256=
|
|
65
|
-
ingestr/src/hubspot/helpers.py,sha256=
|
|
66
|
-
ingestr/src/hubspot/settings.py,sha256=
|
|
64
|
+
ingestr/src/hubspot/__init__.py,sha256=rSYmN8h6qqxhWCW6elD-pC7iqHXlIofb1F9wvTzziUE,10962
|
|
65
|
+
ingestr/src/hubspot/helpers.py,sha256=fscilfO_K7HS2XQxzf7MeZwVXLTP0WdqnV-NhdeqQAA,7748
|
|
66
|
+
ingestr/src/hubspot/settings.py,sha256=i73MkSiJfRLMFLfiJgYdhp-rhymHTfoqFzZ4uOJdFJM,2456
|
|
67
67
|
ingestr/src/kafka/__init__.py,sha256=wMCXdiraeKd1Kssi9WcVCGZaNGm2tJEtnNyuB4aR5_k,3541
|
|
68
68
|
ingestr/src/kafka/helpers.py,sha256=V9WcVn3PKnEpggArHda4vnAcaV8VDuh__dSmRviJb5Y,7502
|
|
69
69
|
ingestr/src/kinesis/__init__.py,sha256=u5ThH1y8uObZKXgIo71em1UnX6MsVHWOjcf1jKqKbE8,6205
|
|
@@ -121,8 +121,8 @@ ingestr/testdata/delete_insert_part2.csv,sha256=B_KUzpzbNdDY_n7wWop1mT2cz36TmayS
|
|
|
121
121
|
ingestr/testdata/merge_expected.csv,sha256=DReHqWGnQMsf2PBv_Q2pfjsgvikYFnf1zYcQZ7ZqYN0,276
|
|
122
122
|
ingestr/testdata/merge_part1.csv,sha256=Pw8Z9IDKcNU0qQHx1z6BUf4rF_-SxKGFOvymCt4OY9I,185
|
|
123
123
|
ingestr/testdata/merge_part2.csv,sha256=T_GiWxA81SN63_tMOIuemcvboEFeAmbKc7xRXvL9esw,287
|
|
124
|
-
ingestr-0.13.
|
|
125
|
-
ingestr-0.13.
|
|
126
|
-
ingestr-0.13.
|
|
127
|
-
ingestr-0.13.
|
|
128
|
-
ingestr-0.13.
|
|
124
|
+
ingestr-0.13.29.dist-info/METADATA,sha256=bHCOtgvVHv64YRJ9hylunKHeARE2caXv7SuBfD9x1fc,13659
|
|
125
|
+
ingestr-0.13.29.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
126
|
+
ingestr-0.13.29.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
|
|
127
|
+
ingestr-0.13.29.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
|
|
128
|
+
ingestr-0.13.29.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|