ingestr 0.13.46__py3-none-any.whl → 0.13.48__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/src/buildinfo.py +1 -1
- ingestr/src/factory.py +2 -0
- ingestr/src/solidgate/__init__.py +97 -0
- ingestr/src/solidgate/helpers.py +78 -0
- ingestr/src/sources.py +46 -1
- {ingestr-0.13.46.dist-info → ingestr-0.13.48.dist-info}/METADATA +1 -1
- {ingestr-0.13.46.dist-info → ingestr-0.13.48.dist-info}/RECORD +10 -8
- {ingestr-0.13.46.dist-info → ingestr-0.13.48.dist-info}/WHEEL +0 -0
- {ingestr-0.13.46.dist-info → ingestr-0.13.48.dist-info}/entry_points.txt +0 -0
- {ingestr-0.13.46.dist-info → ingestr-0.13.48.dist-info}/licenses/LICENSE.md +0 -0
ingestr/src/buildinfo.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
version = "v0.13.
|
|
1
|
+
version = "v0.13.48"
|
ingestr/src/factory.py
CHANGED
|
@@ -54,6 +54,7 @@ from ingestr.src.sources import (
|
|
|
54
54
|
SalesforceSource,
|
|
55
55
|
ShopifySource,
|
|
56
56
|
SlackSource,
|
|
57
|
+
SolidgateSource,
|
|
57
58
|
SqlSource,
|
|
58
59
|
StripeAnalyticsSource,
|
|
59
60
|
TikTokSource,
|
|
@@ -159,6 +160,7 @@ class SourceDestinationFactory:
|
|
|
159
160
|
"phantombuster": PhantombusterSource,
|
|
160
161
|
"elasticsearch": ElasticsearchSource,
|
|
161
162
|
"attio": AttioSource,
|
|
163
|
+
"solidgate": SolidgateSource,
|
|
162
164
|
}
|
|
163
165
|
destinations: Dict[str, Type[DestinationProtocol]] = {
|
|
164
166
|
"bigquery": BigQueryDestination,
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from typing import Iterable, Iterator
|
|
2
|
+
|
|
3
|
+
import dlt
|
|
4
|
+
import pendulum
|
|
5
|
+
from dlt.sources import DltResource
|
|
6
|
+
|
|
7
|
+
from .helpers import SolidgateClient
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dlt.source(max_table_nesting=0)
|
|
11
|
+
def solidgate_source(
|
|
12
|
+
start_date: pendulum.DateTime,
|
|
13
|
+
end_date: pendulum.DateTime | None,
|
|
14
|
+
public_key: str,
|
|
15
|
+
secret_key: str,
|
|
16
|
+
) -> Iterable[DltResource]:
|
|
17
|
+
solidgate_client = SolidgateClient(public_key, secret_key)
|
|
18
|
+
|
|
19
|
+
@dlt.resource(
|
|
20
|
+
name="subscriptions",
|
|
21
|
+
write_disposition="merge",
|
|
22
|
+
primary_key="id",
|
|
23
|
+
columns={
|
|
24
|
+
"created_at": {"data_type": "timestamp", "partition": True},
|
|
25
|
+
},
|
|
26
|
+
)
|
|
27
|
+
def fetch_all_subscriptions(
|
|
28
|
+
dateTime=dlt.sources.incremental(
|
|
29
|
+
"updated_at",
|
|
30
|
+
initial_value=start_date,
|
|
31
|
+
end_value=end_date,
|
|
32
|
+
range_start="closed",
|
|
33
|
+
range_end="closed",
|
|
34
|
+
),
|
|
35
|
+
) -> Iterator[dict]:
|
|
36
|
+
path = "subscriptions"
|
|
37
|
+
if dateTime.end_value is None:
|
|
38
|
+
end_dt = pendulum.now(tz="UTC")
|
|
39
|
+
else:
|
|
40
|
+
end_dt = dateTime.end_value
|
|
41
|
+
|
|
42
|
+
start_dt = dateTime.last_value
|
|
43
|
+
yield solidgate_client.fetch_data(path, date_from=start_dt, date_to=end_dt)
|
|
44
|
+
|
|
45
|
+
@dlt.resource(
|
|
46
|
+
name="apm-orders",
|
|
47
|
+
write_disposition="merge",
|
|
48
|
+
primary_key="order_id",
|
|
49
|
+
columns={
|
|
50
|
+
"created_at": {"data_type": "timestamp", "partition": True},
|
|
51
|
+
},
|
|
52
|
+
)
|
|
53
|
+
def fetch_apm_orders(
|
|
54
|
+
dateTime=dlt.sources.incremental(
|
|
55
|
+
"updated_at",
|
|
56
|
+
initial_value=start_date,
|
|
57
|
+
end_value=end_date,
|
|
58
|
+
range_start="closed",
|
|
59
|
+
range_end="closed",
|
|
60
|
+
),
|
|
61
|
+
) -> Iterator[dict]:
|
|
62
|
+
path = "apm-orders"
|
|
63
|
+
if dateTime.end_value is None:
|
|
64
|
+
end_dt = pendulum.now(tz="UTC")
|
|
65
|
+
else:
|
|
66
|
+
end_dt = dateTime.end_value
|
|
67
|
+
|
|
68
|
+
start_dt = dateTime.last_value
|
|
69
|
+
yield solidgate_client.fetch_data(path, date_from=start_dt, date_to=end_dt)
|
|
70
|
+
|
|
71
|
+
@dlt.resource(
|
|
72
|
+
name="card-orders",
|
|
73
|
+
write_disposition="merge",
|
|
74
|
+
primary_key="order_id",
|
|
75
|
+
columns={
|
|
76
|
+
"created_at": {"data_type": "timestamp", "partition": True},
|
|
77
|
+
},
|
|
78
|
+
)
|
|
79
|
+
def fetch_card_orders(
|
|
80
|
+
dateTime=dlt.sources.incremental(
|
|
81
|
+
"updated_at",
|
|
82
|
+
initial_value=start_date,
|
|
83
|
+
end_value=end_date,
|
|
84
|
+
range_start="closed",
|
|
85
|
+
range_end="closed",
|
|
86
|
+
),
|
|
87
|
+
) -> Iterator[dict]:
|
|
88
|
+
path = "card-orders"
|
|
89
|
+
if dateTime.end_value is None:
|
|
90
|
+
end_dt = pendulum.now(tz="UTC")
|
|
91
|
+
else:
|
|
92
|
+
end_dt = dateTime.end_value
|
|
93
|
+
|
|
94
|
+
start_dt = dateTime.last_value
|
|
95
|
+
yield solidgate_client.fetch_data(path, date_from=start_dt, date_to=end_dt)
|
|
96
|
+
|
|
97
|
+
return fetch_all_subscriptions, fetch_apm_orders, fetch_card_orders
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import hashlib
|
|
3
|
+
import hmac
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
import pendulum
|
|
7
|
+
|
|
8
|
+
from ingestr.src.http_client import create_client
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SolidgateClient:
|
|
12
|
+
def __init__(self, public_key, secret_key):
|
|
13
|
+
self.base_url = "https://reports.solidgate.com/api/v1"
|
|
14
|
+
self.public_key = public_key
|
|
15
|
+
self.secret_key = secret_key
|
|
16
|
+
self.client = create_client()
|
|
17
|
+
|
|
18
|
+
def fetch_data(
|
|
19
|
+
self,
|
|
20
|
+
path: str,
|
|
21
|
+
date_from: pendulum.DateTime,
|
|
22
|
+
date_to: pendulum.DateTime,
|
|
23
|
+
):
|
|
24
|
+
request_payload = {
|
|
25
|
+
"date_from": date_from.format("YYYY-MM-DD HH:mm:ss"),
|
|
26
|
+
"date_to": date_to.format("YYYY-MM-DD HH:mm:ss"),
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
json_string = json.dumps(request_payload)
|
|
30
|
+
signature = self.generateSignature(json_string)
|
|
31
|
+
headers = {
|
|
32
|
+
"merchant": self.public_key,
|
|
33
|
+
"Signature": signature,
|
|
34
|
+
"Content-Type": "application/json",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
next_page_iterator = None
|
|
38
|
+
url = f"{self.base_url}/{path}"
|
|
39
|
+
|
|
40
|
+
while True:
|
|
41
|
+
payload = request_payload.copy()
|
|
42
|
+
if next_page_iterator:
|
|
43
|
+
payload["page_iterator"] = next_page_iterator
|
|
44
|
+
|
|
45
|
+
response = self.client.post(url, headers=headers, json=payload)
|
|
46
|
+
response.raise_for_status()
|
|
47
|
+
response_json = response.json()
|
|
48
|
+
|
|
49
|
+
if path == "subscriptions":
|
|
50
|
+
data = response_json["subscriptions"]
|
|
51
|
+
for _, value in data.items():
|
|
52
|
+
if "updated_at" in value:
|
|
53
|
+
value["updated_at"] = pendulum.parse(
|
|
54
|
+
value["updated_at"]
|
|
55
|
+
)
|
|
56
|
+
yield value
|
|
57
|
+
|
|
58
|
+
else:
|
|
59
|
+
data = response_json["orders"]
|
|
60
|
+
for value in data:
|
|
61
|
+
if "updated_at" in value:
|
|
62
|
+
value["updated_at"] = pendulum.parse(
|
|
63
|
+
value["updated_at"]
|
|
64
|
+
)
|
|
65
|
+
yield value
|
|
66
|
+
|
|
67
|
+
next_page_iterator = response_json.get("metadata", {}).get(
|
|
68
|
+
"next_page_iterator"
|
|
69
|
+
)
|
|
70
|
+
if not next_page_iterator or next_page_iterator == "None":
|
|
71
|
+
break
|
|
72
|
+
|
|
73
|
+
def generateSignature(self, json_string):
|
|
74
|
+
data = self.public_key + json_string + self.public_key
|
|
75
|
+
hmac_hash = hmac.new(
|
|
76
|
+
self.secret_key.encode("utf-8"), data.encode("utf-8"), hashlib.sha512
|
|
77
|
+
).digest()
|
|
78
|
+
return base64.b64encode(hmac_hash.hex().encode("utf-8")).decode("utf-8")
|
ingestr/src/sources.py
CHANGED
|
@@ -698,7 +698,10 @@ class StripeAnalyticsSource:
|
|
|
698
698
|
raise ValueError("api_key in the URI is required to connect to Stripe")
|
|
699
699
|
|
|
700
700
|
endpoint = None
|
|
701
|
-
table
|
|
701
|
+
if table == "balancetransaction":
|
|
702
|
+
table = "BalanceTransaction"
|
|
703
|
+
else:
|
|
704
|
+
table = table.capitalize()
|
|
702
705
|
|
|
703
706
|
if table in [
|
|
704
707
|
"Subscription",
|
|
@@ -2428,3 +2431,45 @@ class AttioSource:
|
|
|
2428
2431
|
)
|
|
2429
2432
|
except ResourcesNotFoundError:
|
|
2430
2433
|
raise UnsupportedResourceError(table_name, "Attio")
|
|
2434
|
+
|
|
2435
|
+
|
|
2436
|
+
class SolidgateSource:
|
|
2437
|
+
def handles_incrementality(self) -> bool:
|
|
2438
|
+
return True
|
|
2439
|
+
|
|
2440
|
+
def dlt_source(self, uri: str, table: str, **kwargs):
|
|
2441
|
+
parsed_uri = urlparse(uri)
|
|
2442
|
+
query_params = parse_qs(parsed_uri.query)
|
|
2443
|
+
public_key = query_params.get("public_key")
|
|
2444
|
+
secret_key = query_params.get("secret_key")
|
|
2445
|
+
|
|
2446
|
+
if public_key is None:
|
|
2447
|
+
raise MissingValueError("public_key", "Solidgate")
|
|
2448
|
+
|
|
2449
|
+
if secret_key is None:
|
|
2450
|
+
raise MissingValueError("secret_key", "Solidgate")
|
|
2451
|
+
|
|
2452
|
+
table_name = table.replace(" ", "")
|
|
2453
|
+
|
|
2454
|
+
start_date = kwargs.get("interval_start")
|
|
2455
|
+
if start_date is None:
|
|
2456
|
+
start_date = pendulum.yesterday().in_tz("UTC")
|
|
2457
|
+
else:
|
|
2458
|
+
start_date = ensure_pendulum_datetime(start_date).in_tz("UTC")
|
|
2459
|
+
|
|
2460
|
+
end_date = kwargs.get("interval_end")
|
|
2461
|
+
|
|
2462
|
+
if end_date is not None:
|
|
2463
|
+
end_date = ensure_pendulum_datetime(end_date).in_tz("UTC")
|
|
2464
|
+
|
|
2465
|
+
from ingestr.src.solidgate import solidgate_source
|
|
2466
|
+
|
|
2467
|
+
try:
|
|
2468
|
+
return solidgate_source(
|
|
2469
|
+
public_key=public_key[0],
|
|
2470
|
+
secret_key=secret_key[0],
|
|
2471
|
+
start_date=start_date,
|
|
2472
|
+
end_date=end_date,
|
|
2473
|
+
).with_resources(table_name)
|
|
2474
|
+
except ResourcesNotFoundError:
|
|
2475
|
+
raise UnsupportedResourceError(table_name, "Solidgate")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ingestr
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.48
|
|
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
|
|
@@ -2,16 +2,16 @@ ingestr/conftest.py,sha256=Q03FIJIZpLBbpj55cfCHIKEjc1FCvWJhMF2cidUJKQU,1748
|
|
|
2
2
|
ingestr/main.py,sha256=Pe_rzwcDRKIYa7baEVUAAPOHyqQbX29RUexMl0F_S1k,25273
|
|
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=yhRWZauFAWKFe7lRKiLr-5OUeP2iQLHekroxm2J_e1c,21
|
|
6
6
|
ingestr/src/destinations.py,sha256=41Bj1UgxR8a2KcZWqtGw74AKZKnSBrueQRnBdrf3c-A,16003
|
|
7
7
|
ingestr/src/errors.py,sha256=Ufs4_DfE77_E3vnA1fOQdi6cmuLVNm7_SbFLkL1XPGk,686
|
|
8
|
-
ingestr/src/factory.py,sha256=
|
|
8
|
+
ingestr/src/factory.py,sha256=FXWJLFfBsKMzUwtsyaaruZU-_OLFKivobj6Olse9vSI,5741
|
|
9
9
|
ingestr/src/filters.py,sha256=C-_TIVkF_cxZBgG-Run2Oyn0TAhJgA8IWXZ-OPY3uek,1136
|
|
10
10
|
ingestr/src/http_client.py,sha256=UwPiv95EfHPdT4525xeLFJ1AYlf-cyaHKRU-2QnZt2o,435
|
|
11
11
|
ingestr/src/loader.py,sha256=9NaWAyfkXdqAZSS-N72Iwo36Lbx4PyqIfaaH1dNdkFs,1712
|
|
12
12
|
ingestr/src/partition.py,sha256=BrIP6wFJvyR7Nus_3ElnfxknUXeCipK_E_bB8kZowfc,969
|
|
13
13
|
ingestr/src/resource.py,sha256=XG-sbBapFVEM7OhHQFQRTdTLlh-mHB-N4V1t8F8Tsww,543
|
|
14
|
-
ingestr/src/sources.py,sha256=
|
|
14
|
+
ingestr/src/sources.py,sha256=kT5XtFQyPPE0dEBXuPaPfEVTqCi0l-6Fa2DWWFCUMeg,85205
|
|
15
15
|
ingestr/src/table_definition.py,sha256=REbAbqdlmUMUuRh8nEQRreWjPVOQ5ZcfqGkScKdCrmk,390
|
|
16
16
|
ingestr/src/time.py,sha256=H_Fk2J4ShXyUM-EMY7MqCLZQhlnZMZvO952bmZPc4yE,254
|
|
17
17
|
ingestr/src/version.py,sha256=J_2xgZ0mKlvuHcjdKCx2nlioneLH0I47JiU_Slr_Nwc,189
|
|
@@ -108,6 +108,8 @@ ingestr/src/shopify/settings.py,sha256=StY0EPr7wFJ7KzRRDN4TKxV0_gkIS1wPj2eR4AYSs
|
|
|
108
108
|
ingestr/src/slack/__init__.py,sha256=pyDukxcilqTAe_bBzfWJ8Vxi83S-XEdEFBH2pEgILrM,10113
|
|
109
109
|
ingestr/src/slack/helpers.py,sha256=08TLK7vhFvH_uekdLVOLF3bTDe1zgH0QxHObXHzk1a8,6545
|
|
110
110
|
ingestr/src/slack/settings.py,sha256=NhKn4y1zokEa5EmIZ05wtj_-I0GOASXZ5V81M1zXCtY,457
|
|
111
|
+
ingestr/src/solidgate/__init__.py,sha256=vpoXu0Ox3zE_WPSzdsA6iUG1_XBa9OaA5F7eFBbZYuQ,2819
|
|
112
|
+
ingestr/src/solidgate/helpers.py,sha256=0TP71RQDo6ub-q0goivQOPiUxWIQU7SgQLUSXVctoVI,2532
|
|
111
113
|
ingestr/src/sql_database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
112
114
|
ingestr/src/sql_database/callbacks.py,sha256=sEFFmXxAURY3yeBjnawigDtq9LBCvi8HFqG4kLd7tMU,2002
|
|
113
115
|
ingestr/src/stripe_analytics/__init__.py,sha256=0HCL0qsrh_si1RR3a4k9XS94VWQ4v9aG7CqXF-V-57M,4593
|
|
@@ -131,8 +133,8 @@ ingestr/testdata/delete_insert_part2.csv,sha256=B_KUzpzbNdDY_n7wWop1mT2cz36TmayS
|
|
|
131
133
|
ingestr/testdata/merge_expected.csv,sha256=DReHqWGnQMsf2PBv_Q2pfjsgvikYFnf1zYcQZ7ZqYN0,276
|
|
132
134
|
ingestr/testdata/merge_part1.csv,sha256=Pw8Z9IDKcNU0qQHx1z6BUf4rF_-SxKGFOvymCt4OY9I,185
|
|
133
135
|
ingestr/testdata/merge_part2.csv,sha256=T_GiWxA81SN63_tMOIuemcvboEFeAmbKc7xRXvL9esw,287
|
|
134
|
-
ingestr-0.13.
|
|
135
|
-
ingestr-0.13.
|
|
136
|
-
ingestr-0.13.
|
|
137
|
-
ingestr-0.13.
|
|
138
|
-
ingestr-0.13.
|
|
136
|
+
ingestr-0.13.48.dist-info/METADATA,sha256=wSTbG4xvQuRvVBXVzF0Q_0w0J9uVCKfJmyVmkQDbg50,13852
|
|
137
|
+
ingestr-0.13.48.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
138
|
+
ingestr-0.13.48.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
|
|
139
|
+
ingestr-0.13.48.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
|
|
140
|
+
ingestr-0.13.48.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|