ingestr 0.13.68__py3-none-any.whl → 0.13.69__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.
ingestr/conftest.py CHANGED
@@ -2,6 +2,7 @@ import os
2
2
  import tempfile
3
3
  from concurrent.futures import ThreadPoolExecutor
4
4
 
5
+ import pytest
5
6
  from main_test import DESTINATIONS, SOURCES # type: ignore
6
7
 
7
8
 
@@ -15,6 +16,14 @@ def pytest_configure_node(node):
15
16
  node.workerinput["shared_directory"] = node.config.shared_directory
16
17
 
17
18
 
19
+ @pytest.fixture(scope="session")
20
+ def shared_directory(request):
21
+ if is_master(request.config):
22
+ return request.config.shared_directory
23
+ else:
24
+ return request.config.workerinput["shared_directory"]
25
+
26
+
18
27
  def is_master(config):
19
28
  """True if the code running the given pytest.config object is running in a xdist master
20
29
  node or not running xdist at all.
ingestr/main.py CHANGED
@@ -256,7 +256,7 @@ def ingest(
256
256
  Optional[list[str]],
257
257
  typer.Option(
258
258
  help="The column types to be used for the destination table in the format of 'column_name:column_type'",
259
- envvar=["COLUMNS", "INGESTR_COLUMNS"],
259
+ envvar=["INGESTR_COLUMNS"],
260
260
  ),
261
261
  ] = None, # type: ignore
262
262
  yield_limit: Annotated[
ingestr/src/buildinfo.py CHANGED
@@ -1 +1 @@
1
- version = "v0.13.68"
1
+ version = "v0.13.69"
@@ -6,6 +6,110 @@ from dlt.sources import DltResource
6
6
 
7
7
  from .helpers import SolidgateClient
8
8
 
9
+ COLUMN_HINTS = {
10
+ "subscriptions": {
11
+ "id": {"data_type": "text", "nullable": False, "primary_key": True},
12
+ "created_at": {"data_type": "timestamp", "partition": True},
13
+ "status": {"data_type": "text"},
14
+ "started_at": {"data_type": "timestamp"},
15
+ "updated_at": {"data_type": "timestamp"},
16
+ "expired_at": {"data_type": "timestamp"},
17
+ "next_charge_at": {"data_type": "timestamp"},
18
+ "payment_type": {"data_type": "text"},
19
+ "trial": {"data_type": "bool"},
20
+ "cancelled_at": {"data_type": "timestamp"},
21
+ "cancellation_requested_at": {"data_type": "timestamp"},
22
+ "cancel_code": {"data_type": "text"},
23
+ "cancel_message": {"data_type": "text"},
24
+ "customer": {"data_type": "json"},
25
+ "product": {"data_type": "json"},
26
+ "invoices": {"data_type": "json"},
27
+ },
28
+ "apm_orders": {
29
+ "order_id": {"data_type": "text", "nullable": False, "primary_key": True},
30
+ "created_at": {"data_type": "timestamp", "partition": True},
31
+ "updated_at": {"data_type": "timestamp"},
32
+ "order_description": {"data_type": "text"},
33
+ "method": {"data_type": "text"},
34
+ "amount": {"data_type": "bigint"},
35
+ "currency": {"data_type": "text"},
36
+ "processing_amount": {"data_type": "bigint"},
37
+ "processing_currency": {"data_type": "text"},
38
+ "status": {"data_type": "text"},
39
+ "customer_account_id": {"data_type": "text"},
40
+ "customer_email": {"data_type": "text"},
41
+ "ip_address": {"data_type": "text"},
42
+ "geo_country": {"data_type": "text"},
43
+ "error_code": {"data_type": "text"},
44
+ "transactions": {"data_type": "json"},
45
+ "order_metadata": {"data_type": "json"},
46
+ },
47
+ "card_orders": {
48
+ "order_id": {"data_type": "text", "nullable": False, "primary_key": True},
49
+ "created_at": {"data_type": "timestamp", "partition": True},
50
+ "updated_at": {"data_type": "timestamp"},
51
+ "order_description": {"data_type": "text"},
52
+ "psp_order_id": {"data_type": "text"},
53
+ "provider_payment_id": {"data_type": "text"},
54
+ "amount": {"data_type": "bigint"},
55
+ "currency": {"data_type": "text"},
56
+ "processing_amount": {"data_type": "bigint"},
57
+ "processing_currency": {"data_type": "text"},
58
+ "status": {"data_type": "text"},
59
+ "payment_type": {"data_type": "text"},
60
+ "type": {"data_type": "text"},
61
+ "is_secured": {"data_type": "bool"},
62
+ "routing": {"data_type": "json"},
63
+ "customer_account_id": {"data_type": "text"},
64
+ "customer_email": {"data_type": "text"},
65
+ "customer_first_name": {"data_type": "text"},
66
+ "customer_last_name": {"data_type": "text"},
67
+ "ip_address": {"data_type": "text"},
68
+ "mid": {"data_type": "text"},
69
+ "traffic_source": {"data_type": "text"},
70
+ "platform": {"data_type": "text"},
71
+ "geo_country": {"data_type": "text"},
72
+ "error_code": {"data_type": "text"},
73
+ "transactions": {"data_type": "json"},
74
+ "order_metadata": {"data_type": "json"},
75
+ "fraudulent": {"data_type": "bool"},
76
+ },
77
+ "financial_entries": {
78
+ "id": {
79
+ "data_type": "text",
80
+ "nullable": False,
81
+ "primary_key": True,
82
+ },
83
+ "order_id": {"data_type": "text"},
84
+ "external_psp_order_id": {"data_type": "text"},
85
+ "created_at": {"data_type": "timestamp", "partition": True},
86
+ "transaction_datetime_provider": {"data_type": "timestamp"},
87
+ "transaction_datetime_utc": {"data_type": "timestamp"},
88
+ "accounting_date": {"data_type": "date"},
89
+ "amount": {"data_type": "decimal", "precision": 18, "scale": 4},
90
+ "amount_in_major_units": {"data_type": "decimal", "precision": 18, "scale": 4},
91
+ "currency": {"data_type": "text"},
92
+ "currency_minor_units": {"data_type": "bigint"},
93
+ "payout_amount": {"data_type": "decimal", "precision": 18, "scale": 4},
94
+ "payout_amount_in_major_units": {
95
+ "data_type": "decimal",
96
+ "precision": 18,
97
+ "scale": 4,
98
+ },
99
+ "payout_currency": {"data_type": "text"},
100
+ "payout_currency_minor_units": {"data_type": "bigint"},
101
+ "record_type_key": {"data_type": "text"},
102
+ "provider": {"data_type": "text"},
103
+ "payment_method": {"data_type": "text"},
104
+ "card_brand": {"data_type": "text"},
105
+ "geo_country": {"data_type": "text"},
106
+ "issuing_country": {"data_type": "text"},
107
+ "transaction_id": {"data_type": "text"},
108
+ "chargeback_id": {"data_type": "text"},
109
+ "legal_entity": {"data_type": "text"},
110
+ },
111
+ }
112
+
9
113
 
10
114
  @dlt.source(max_table_nesting=0)
11
115
  def solidgate_source(
@@ -20,9 +124,7 @@ def solidgate_source(
20
124
  name="subscriptions",
21
125
  write_disposition="merge",
22
126
  primary_key="id",
23
- columns={
24
- "created_at": {"data_type": "timestamp", "partition": True},
25
- },
127
+ columns=COLUMN_HINTS["subscriptions"], # type: ignore
26
128
  )
27
129
  def fetch_all_subscriptions(
28
130
  dateTime=dlt.sources.incremental(
@@ -46,9 +148,7 @@ def solidgate_source(
46
148
  name="apm_orders",
47
149
  write_disposition="merge",
48
150
  primary_key="order_id",
49
- columns={
50
- "created_at": {"data_type": "timestamp", "partition": True},
51
- },
151
+ columns=COLUMN_HINTS["apm_orders"], # type: ignore
52
152
  )
53
153
  def fetch_apm_orders(
54
154
  dateTime=dlt.sources.incremental(
@@ -72,9 +172,7 @@ def solidgate_source(
72
172
  name="card_orders",
73
173
  write_disposition="merge",
74
174
  primary_key="order_id",
75
- columns={
76
- "created_at": {"data_type": "timestamp", "partition": True},
77
- },
175
+ columns=COLUMN_HINTS["card_orders"], # type: ignore
78
176
  )
79
177
  def fetch_card_orders(
80
178
  dateTime=dlt.sources.incremental(
@@ -98,9 +196,7 @@ def solidgate_source(
98
196
  name="financial_entries",
99
197
  write_disposition="merge",
100
198
  primary_key="id",
101
- columns={
102
- "created_at": {"data_type": "timestamp", "partition": True},
103
- },
199
+ columns=COLUMN_HINTS["financial_entries"], # type: ignore
104
200
  )
105
201
  def fetch_financial_entries(
106
202
  dateTime=dlt.sources.incremental(
@@ -92,9 +92,6 @@ class SolidgateClient:
92
92
  if not report_url:
93
93
  return f"Report URL not found in the response: {post_response.json()}", 400
94
94
 
95
- # Wait for 5 seconds before attempting to download the report as report may not be immediately available
96
- time.sleep(5)
97
-
98
95
  data = self.public_key + self.public_key
99
96
  hmac_hash = hmac.new(
100
97
  self.secret_key.encode("utf-8"), data.encode("utf-8"), hashlib.sha512
@@ -109,27 +106,38 @@ class SolidgateClient:
109
106
  "Content-Type": "application/json",
110
107
  }
111
108
 
112
- get_response = self.client.get(report_url, headers=headers_get)
109
+ # Retry getting the report for up to 10 minutes (600 seconds) with 5-second intervals
110
+ max_retries = 120 # 10 minutes / 5 seconds = 120 attempts
111
+ retry_count = 0
112
+
113
+ while retry_count < max_retries:
114
+ get_response = self.client.get(report_url, headers=headers_get)
113
115
 
114
- if get_response.status_code == 200:
115
- try:
116
- response_json = json.loads(get_response.content)
117
- if "error" in response_json:
118
- raise Exception(f"API Error: {response_json['error']['messages']}")
119
- except json.JSONDecodeError:
116
+ if get_response.status_code == 200:
120
117
  try:
121
- csv_data = get_response.content.decode("utf-8")
122
- df = pd.read_csv(StringIO(csv_data))
123
- df["created_at"] = df["created_at"].apply(
124
- lambda x: pendulum.parse(x)
118
+ response_json = json.loads(get_response.content)
119
+ if "error" in response_json:
120
+ raise Exception(
121
+ f"API Error: {response_json['error']['messages']}"
122
+ )
123
+ except json.JSONDecodeError:
124
+ try:
125
+ csv_data = get_response.content.decode("utf-8")
126
+ df = pd.read_csv(StringIO(csv_data))
127
+ df["created_at"] = df["created_at"].apply(
128
+ lambda x: pendulum.parse(x)
129
+ )
130
+ return df
131
+ except Exception as e:
132
+ raise Exception(f"Error reading CSV: {e}")
133
+ else:
134
+ # Report might not be ready yet, wait and retry
135
+ retry_count += 1
136
+ if retry_count >= max_retries:
137
+ raise Exception(
138
+ f"Failed to get report after {max_retries} attempts. Status code: {get_response.status_code}"
125
139
  )
126
- return df
127
- except Exception as e:
128
- raise Exception(f"Error reading CSV: {e}")
129
- else:
130
- raise Exception(
131
- f"Failed to get report. Status code: {get_response.status_code}"
132
- )
140
+ time.sleep(5) # Wait 5 seconds before retrying
133
141
 
134
142
  def generateSignature(self, json_string):
135
143
  data = self.public_key + json_string + self.public_key
ingestr/src/sources.py CHANGED
@@ -80,18 +80,18 @@ class SqlSource:
80
80
  if uri.startswith("snowflake://"):
81
81
  parsed_uri = urlparse(uri)
82
82
  query_params = parse_qs(parsed_uri.query)
83
-
83
+
84
84
  if "private_key" in query_params:
85
85
  from dlt.common.libs.cryptography import decode_private_key
86
-
86
+
87
87
  private_key = query_params["private_key"][0]
88
88
  passphrase = query_params.get("private_key_passphrase", [None])[0]
89
89
  decoded_key = decode_private_key(private_key, passphrase)
90
-
90
+
91
91
  query_params["private_key"] = [base64.b64encode(decoded_key).decode()]
92
92
  if "private_key_passphrase" in query_params:
93
93
  del query_params["private_key_passphrase"]
94
-
94
+
95
95
  # Rebuild URI
96
96
  uri = parsed_uri._replace(
97
97
  query=urlencode(query_params, doseq=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ingestr
3
- Version: 0.13.68
3
+ Version: 0.13.69
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
- ingestr/conftest.py,sha256=Q03FIJIZpLBbpj55cfCHIKEjc1FCvWJhMF2cidUJKQU,1748
2
- ingestr/main.py,sha256=8EduMJdPnj6Z8p5xMzCJ8KZPq-STaPhIzhVTzoSz_sw,26380
1
+ ingestr/conftest.py,sha256=OE2yxeTCosS9CUFVuqNypm-2ftYvVBeeq7egm3878cI,1981
2
+ ingestr/main.py,sha256=wu4c_4nit3c5v2K1af68DjT2hIfEiCvzQ8EZJxOkJjo,26369
3
3
  ingestr/src/.gitignore,sha256=8cX1AZTSI0TcdZFGTmS_oyBjpfCzhOEt0DdAo2dFIY8,203
4
4
  ingestr/src/blob.py,sha256=UUWMjHUuoR9xP1XZQ6UANQmnMVyDx3d0X4-2FQC271I,2138
5
- ingestr/src/buildinfo.py,sha256=AmQ_QOtiF5_x4B8nPBudbz-zX_sJrDJElSgNVFmAdKM,21
5
+ ingestr/src/buildinfo.py,sha256=Ke17ObaAtICcxQMwBjBfKvBywicwCyo9pp8D2NmSsjk,21
6
6
  ingestr/src/destinations.py,sha256=ivTPio0zzqLRx22i597pxZHMNClz-XvYSyCaCPuGd8g,22248
7
7
  ingestr/src/errors.py,sha256=Ufs4_DfE77_E3vnA1fOQdi6cmuLVNm7_SbFLkL1XPGk,686
8
8
  ingestr/src/factory.py,sha256=AJCvlK4M1sIpAAks1K-xsR_uxziIxru74mj572zixhg,6546
@@ -11,7 +11,7 @@ ingestr/src/http_client.py,sha256=bxqsk6nJNXCo-79gW04B53DQO-yr25vaSsqP0AKtjx4,73
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=ZqmZxFQVGlF8rFPhBiUB08HES0yoTj8sZ--jKfaaVps,1164
14
- ingestr/src/sources.py,sha256=IGFRqH1f9x2W7GUnAHoVYtrBYvvJbuDcHAhULCXn5i8,104352
14
+ ingestr/src/sources.py,sha256=Ey3fI-SZ08fVMZf5_uCdLku87UuT3bjM8SmesgoSSY8,104292
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
@@ -117,8 +117,8 @@ ingestr/src/slack/__init__.py,sha256=pyDukxcilqTAe_bBzfWJ8Vxi83S-XEdEFBH2pEgILrM
117
117
  ingestr/src/slack/helpers.py,sha256=08TLK7vhFvH_uekdLVOLF3bTDe1zgH0QxHObXHzk1a8,6545
118
118
  ingestr/src/slack/settings.py,sha256=NhKn4y1zokEa5EmIZ05wtj_-I0GOASXZ5V81M1zXCtY,457
119
119
  ingestr/src/smartsheets/__init__.py,sha256=pdzSV7rA0XYD5Xa1u4zb6vziy5iFXIQNROkpJ9oYas0,1623
120
- ingestr/src/solidgate/__init__.py,sha256=JdaXvAu5QGuf9-FY294vwCQCEmfrqIld9oqbzqCJS3g,3626
121
- ingestr/src/solidgate/helpers.py,sha256=oePEc9nnvmN3IaKrfJCvyKL79xdGM0-gRTN3-8tY4Fc,4952
120
+ ingestr/src/solidgate/__init__.py,sha256=as3m-lrs8teI3BW_8__5SY7BY8Af7SetzuU8Cj3L80U,8170
121
+ ingestr/src/solidgate/helpers.py,sha256=j8FjYHyUJTwkJ8VY8nre2qMesLeRq3eTwU22Ff-2K8M,5426
122
122
  ingestr/src/sql_database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
123
123
  ingestr/src/sql_database/callbacks.py,sha256=sEFFmXxAURY3yeBjnawigDtq9LBCvi8HFqG4kLd7tMU,2002
124
124
  ingestr/src/stripe_analytics/__init__.py,sha256=mK8dGKAlMPVqGE9gG30XfbvOvgVD0yWhNpt-D3iavDY,6385
@@ -147,8 +147,8 @@ ingestr/testdata/merge_expected.csv,sha256=DReHqWGnQMsf2PBv_Q2pfjsgvikYFnf1zYcQZ
147
147
  ingestr/testdata/merge_part1.csv,sha256=Pw8Z9IDKcNU0qQHx1z6BUf4rF_-SxKGFOvymCt4OY9I,185
148
148
  ingestr/testdata/merge_part2.csv,sha256=T_GiWxA81SN63_tMOIuemcvboEFeAmbKc7xRXvL9esw,287
149
149
  ingestr/tests/unit/test_smartsheets.py,sha256=eiC2CCO4iNJcuN36ONvqmEDryCA1bA1REpayHpu42lk,5058
150
- ingestr-0.13.68.dist-info/METADATA,sha256=l2t9KyoQ_YqRie-2IItPzZSzHCIzk26wW16ZZ5SA4d8,15022
151
- ingestr-0.13.68.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
152
- ingestr-0.13.68.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
153
- ingestr-0.13.68.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
154
- ingestr-0.13.68.dist-info/RECORD,,
150
+ ingestr-0.13.69.dist-info/METADATA,sha256=DNrbX4KACx_XBhLJuMTig_hG3m_jjPw9GQrOXlsG6BQ,15022
151
+ ingestr-0.13.69.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
152
+ ingestr-0.13.69.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
153
+ ingestr-0.13.69.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
154
+ ingestr-0.13.69.dist-info/RECORD,,