ingestr 0.13.50__py3-none-any.whl → 0.13.51__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 CHANGED
@@ -1 +1 @@
1
- version = "v0.13.50"
1
+ version = "v0.13.51"
ingestr/src/factory.py CHANGED
@@ -59,6 +59,7 @@ from ingestr.src.sources import (
59
59
  StripeAnalyticsSource,
60
60
  TikTokSource,
61
61
  ZendeskSource,
62
+ SmartsheetSource,
62
63
  )
63
64
 
64
65
  SQL_SOURCE_SCHEMES = [
@@ -161,6 +162,7 @@ class SourceDestinationFactory:
161
162
  "elasticsearch": ElasticsearchSource,
162
163
  "attio": AttioSource,
163
164
  "solidgate": SolidgateSource,
165
+ "smartsheet": SmartsheetSource,
164
166
  }
165
167
  destinations: Dict[str, Type[DestinationProtocol]] = {
166
168
  "bigquery": BigQueryDestination,
@@ -2,17 +2,23 @@ import requests
2
2
  from dlt.sources.helpers.requests import Client
3
3
 
4
4
 
5
- def create_client() -> requests.Session:
5
+ def create_client(retry_status_codes: list[int] | None = None) -> requests.Session:
6
+ if retry_status_codes is None:
7
+ retry_status_codes = [502]
6
8
  return Client(
7
9
  raise_for_status=False,
8
- retry_condition=retry_on_limit,
10
+ retry_condition=retry_on_status_code(retry_status_codes),
9
11
  request_max_attempts=12,
12
+ request_backoff_factor=10,
10
13
  ).session
11
14
 
12
15
 
13
- def retry_on_limit(
14
- response: requests.Response | None, exception: BaseException | None
15
- ) -> bool:
16
- if response is None:
17
- return False
18
- return response.status_code == 502
16
+ def retry_on_status_code(retry_status_codes: list[int]):
17
+ def retry_on_limit(
18
+ response: requests.Response | None, exception: BaseException | None
19
+ ) -> bool:
20
+ if response is None:
21
+ return False
22
+ return response.status_code in retry_status_codes
23
+
24
+ return retry_on_limit
@@ -94,4 +94,34 @@ def solidgate_source(
94
94
  start_dt = dateTime.last_value
95
95
  yield solidgate_client.fetch_data(path, date_from=start_dt, date_to=end_dt)
96
96
 
97
- return fetch_all_subscriptions, fetch_apm_orders, fetch_card_orders
97
+ @dlt.resource(
98
+ name="financial-entries",
99
+ write_disposition="merge",
100
+ primary_key="id",
101
+ columns={
102
+ "created_at": {"data_type": "timestamp", "partition": True},
103
+ },
104
+ )
105
+ def fetch_financial_entries(
106
+ dateTime=dlt.sources.incremental(
107
+ "created_at",
108
+ initial_value=start_date,
109
+ end_value=end_date,
110
+ range_start="closed",
111
+ range_end="closed",
112
+ ),
113
+ ):
114
+ if dateTime.end_value is None:
115
+ end_date = pendulum.now(tz="UTC")
116
+ else:
117
+ end_date = dateTime.end_value
118
+
119
+ start_date = dateTime.last_value
120
+ yield solidgate_client.fetch_financial_entry_data(start_date, end_date)
121
+
122
+ return (
123
+ fetch_all_subscriptions,
124
+ fetch_apm_orders,
125
+ fetch_card_orders,
126
+ fetch_financial_entries,
127
+ )
@@ -2,7 +2,10 @@ import base64
2
2
  import hashlib
3
3
  import hmac
4
4
  import json
5
+ import time
6
+ from io import StringIO
5
7
 
8
+ import pandas as pd # type: ignore
6
9
  import pendulum
7
10
 
8
11
  from ingestr.src.http_client import create_client
@@ -13,7 +16,7 @@ class SolidgateClient:
13
16
  self.base_url = "https://reports.solidgate.com/api/v1"
14
17
  self.public_key = public_key
15
18
  self.secret_key = secret_key
16
- self.client = create_client()
19
+ self.client = create_client(retry_status_codes=[204])
17
20
 
18
21
  def fetch_data(
19
22
  self,
@@ -66,6 +69,68 @@ class SolidgateClient:
66
69
  if not next_page_iterator or next_page_iterator == "None":
67
70
  break
68
71
 
72
+ def fetch_financial_entry_data(
73
+ self, date_from: pendulum.DateTime, date_to: pendulum.DateTime
74
+ ):
75
+ request_payload = {
76
+ "date_from": date_from.format("YYYY-MM-DD HH:mm:ss"),
77
+ "date_to": date_to.format("YYYY-MM-DD HH:mm:ss"),
78
+ }
79
+
80
+ json_string = json.dumps(request_payload)
81
+ signature = self.generateSignature(json_string)
82
+ headers = {
83
+ "merchant": self.public_key,
84
+ "Signature": signature,
85
+ "Content-Type": "application/json",
86
+ }
87
+
88
+ url = f"{self.base_url}/finance/financial_entries"
89
+ post_response = self.client.post(url, headers=headers, json=request_payload)
90
+ post_response.raise_for_status()
91
+ report_url = post_response.json().get("report_url")
92
+ if not report_url:
93
+ return f"Report URL not found in the response: {post_response.json()}", 400
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
+ data = self.public_key + self.public_key
99
+ hmac_hash = hmac.new(
100
+ self.secret_key.encode("utf-8"), data.encode("utf-8"), hashlib.sha512
101
+ ).digest()
102
+ signature_get = base64.b64encode(hmac_hash.hex().encode("utf-8")).decode(
103
+ "utf-8"
104
+ )
105
+
106
+ headers_get = {
107
+ "merchant": self.public_key,
108
+ "Signature": signature_get,
109
+ "Content-Type": "application/json",
110
+ }
111
+
112
+ get_response = self.client.get(report_url, headers=headers_get)
113
+
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:
120
+ 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)
125
+ )
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
+ )
133
+
69
134
  def generateSignature(self, json_string):
70
135
  data = self.public_key + json_string + self.public_key
71
136
  hmac_hash = hmac.new(
@@ -3,24 +3,71 @@
3
3
  # the most popular endpoints
4
4
  # Full list of the Stripe API endpoints you can find here: https://stripe.com/docs/api.
5
5
  ENDPOINTS = {
6
- "subscription": "Subscription",
7
6
  "account": "Account",
7
+ "applepaydomain": "ApplePayDomain",
8
+ "apple_pay_domain": "ApplePayDomain",
9
+ "applicationfee": "ApplicationFee",
10
+ "application_fee": "ApplicationFee",
11
+ "checkoutsession": "CheckoutSession",
12
+ "checkout_session": "CheckoutSession",
8
13
  "coupon": "Coupon",
9
14
  "customer": "Customer",
10
- "product": "Product",
15
+ "dispute": "Dispute",
16
+ "paymentintent": "PaymentIntent",
17
+ "payment_intent": "PaymentIntent",
18
+ "paymentlink": "PaymentLink",
19
+ "payment_link": "PaymentLink",
20
+ "paymentmethod": "PaymentMethod",
21
+ "payment_method": "PaymentMethod",
22
+ "paymentmethoddomain": "PaymentMethodDomain",
23
+ "payment_method_domain": "PaymentMethodDomain",
24
+ "payout": "Payout",
25
+ "plan": "Plan",
11
26
  "price": "Price",
27
+ "product": "Product",
28
+ "promotioncode": "PromotionCode",
29
+ "promotion_code": "PromotionCode",
30
+ "quote": "Quote",
31
+ "refund": "Refund",
32
+ "review": "Review",
33
+ "setupattempt": "SetupAttempt",
34
+ "setup_attempt": "SetupAttempt",
35
+ "setupintent": "SetupIntent",
36
+ "setup_intent": "SetupIntent",
12
37
  "shippingrate": "ShippingRate",
13
- "dispute": "Dispute",
38
+ "shipping_rate": "ShippingRate",
39
+ "subscription": "Subscription",
14
40
  "subscriptionitem": "SubscriptionItem",
15
- "checkoutsession": "CheckoutSession",
41
+ "subscription_item": "SubscriptionItem",
42
+ "subscriptionschedule": "SubscriptionSchedule",
43
+ "subscription_schedule": "SubscriptionSchedule",
44
+ "transfer": "Transfer",
45
+ "taxcode": "TaxCode",
46
+ "tax_code": "TaxCode",
47
+ "taxid": "TaxId",
48
+ "tax_id": "TaxId",
49
+ "taxrate": "TaxRate",
50
+ "tax_rate": "TaxRate",
51
+ "topup": "Topup",
52
+ "top_up": "Topup",
53
+ "webhookendpoint": "WebhookEndpoint",
54
+ "webhook_endpoint": "WebhookEndpoint",
16
55
  }
17
56
  # possible incremental endpoints
18
57
  INCREMENTAL_ENDPOINTS = {
19
- "event": "Event",
20
- "invoice": "Invoice",
58
+ "applicationfee": "ApplicationFee",
59
+ "application_fee": "ApplicationFee",
21
60
  "balancetransaction": "BalanceTransaction",
61
+ "balance_transaction": "BalanceTransaction",
22
62
  "charge": "Charge",
23
- "applicationfee": "ApplicationFee",
24
- "setupattempt": "SetupAttempt",
25
63
  "creditnote": "CreditNote",
64
+ "credit_note": "CreditNote",
65
+ "event": "Event",
66
+ "invoice": "Invoice",
67
+ "invoiceitem": "InvoiceItem",
68
+ "invoice_item": "InvoiceItem",
69
+ "invoicelineitem": "InvoiceLineItem",
70
+ "invoice_line_item": "InvoiceLineItem",
71
+ "setupattempt": "SetupAttempt",
72
+ "setup_attempt": "SetupAttempt",
26
73
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ingestr
3
- Version: 0.13.50
3
+ Version: 0.13.51
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,12 +2,12 @@ ingestr/conftest.py,sha256=Q03FIJIZpLBbpj55cfCHIKEjc1FCvWJhMF2cidUJKQU,1748
2
2
  ingestr/main.py,sha256=rHxHQAbd0ccW2e2kQSWlv7-5qcc2ZB6Eh3vyjm4Nzns,25550
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=SIygmTZiIS9bHiKi-Fr3gXHmpPssVhCeEF_SCAtF8Ww,21
5
+ ingestr/src/buildinfo.py,sha256=ZgugQZPIlr-_7kRH5qIupQ5e48JTOHSlRWEsmNx4wXw,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=FXWJLFfBsKMzUwtsyaaruZU-_OLFKivobj6Olse9vSI,5741
8
+ ingestr/src/factory.py,sha256=JeK0M8C05-_KxxIhtevs-MJGLlngXUQupd-Pm4Fg904,5803
9
9
  ingestr/src/filters.py,sha256=C-_TIVkF_cxZBgG-Run2Oyn0TAhJgA8IWXZ-OPY3uek,1136
10
- ingestr/src/http_client.py,sha256=UwPiv95EfHPdT4525xeLFJ1AYlf-cyaHKRU-2QnZt2o,435
10
+ ingestr/src/http_client.py,sha256=bxqsk6nJNXCo-79gW04B53DQO-yr25vaSsqP0AKtjx4,732
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
@@ -109,13 +109,13 @@ ingestr/src/slack/__init__.py,sha256=pyDukxcilqTAe_bBzfWJ8Vxi83S-XEdEFBH2pEgILrM
109
109
  ingestr/src/slack/helpers.py,sha256=08TLK7vhFvH_uekdLVOLF3bTDe1zgH0QxHObXHzk1a8,6545
110
110
  ingestr/src/slack/settings.py,sha256=NhKn4y1zokEa5EmIZ05wtj_-I0GOASXZ5V81M1zXCtY,457
111
111
  ingestr/src/smartsheets/__init__.py,sha256=pdzSV7rA0XYD5Xa1u4zb6vziy5iFXIQNROkpJ9oYas0,1623
112
- ingestr/src/solidgate/__init__.py,sha256=vpoXu0Ox3zE_WPSzdsA6iUG1_XBa9OaA5F7eFBbZYuQ,2819
113
- ingestr/src/solidgate/helpers.py,sha256=_PuHmKZ-jpNEsPxRgXCzu39PsaygVDCpnoMTZAYSpHE,2432
112
+ ingestr/src/solidgate/__init__.py,sha256=DZYQ4M3Cc7AIbdQcNQm_6yX2whnFhE-iM10-ACJ3W3A,3626
113
+ ingestr/src/solidgate/helpers.py,sha256=oePEc9nnvmN3IaKrfJCvyKL79xdGM0-gRTN3-8tY4Fc,4952
114
114
  ingestr/src/sql_database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
115
115
  ingestr/src/sql_database/callbacks.py,sha256=sEFFmXxAURY3yeBjnawigDtq9LBCvi8HFqG4kLd7tMU,2002
116
116
  ingestr/src/stripe_analytics/__init__.py,sha256=FBkZu5op5Z-FceEi4zG7qcAgZfUYJRPMVPPrPMjvmXw,4502
117
117
  ingestr/src/stripe_analytics/helpers.py,sha256=iqZOyiGIOhOAhVXXU16DP0hkkTKcTrDu69vAJoTxgEo,1976
118
- ingestr/src/stripe_analytics/settings.py,sha256=fA2j_6FquEmyRyB799P4SncwLwK1S1u9WFNjbzu91kY,786
118
+ ingestr/src/stripe_analytics/settings.py,sha256=ZahhZg3Sq2KnvnDcfSaXO494Csy3tElBDEHnvA1AVmA,2461
119
119
  ingestr/src/telemetry/event.py,sha256=W7bs4uVfPakQ5otmiqgqu1l5SqjYx1p87wudnWXckBc,949
120
120
  ingestr/src/testdata/fakebqcredentials.json,sha256=scc6TUc963KAbKTLZCfcmqVzbtzDCW1_8JNRnyAXyy8,628
121
121
  ingestr/src/tiktok_ads/__init__.py,sha256=aEqCl3dTH6_d43s1jgAeG1UasEls_SlorORulYMwIL8,4590
@@ -135,8 +135,8 @@ ingestr/testdata/merge_expected.csv,sha256=DReHqWGnQMsf2PBv_Q2pfjsgvikYFnf1zYcQZ
135
135
  ingestr/testdata/merge_part1.csv,sha256=Pw8Z9IDKcNU0qQHx1z6BUf4rF_-SxKGFOvymCt4OY9I,185
136
136
  ingestr/testdata/merge_part2.csv,sha256=T_GiWxA81SN63_tMOIuemcvboEFeAmbKc7xRXvL9esw,287
137
137
  ingestr/tests/unit/test_smartsheets.py,sha256=eiC2CCO4iNJcuN36ONvqmEDryCA1bA1REpayHpu42lk,5058
138
- ingestr-0.13.50.dist-info/METADATA,sha256=cYNN5jAD-1Fistktk9x6ggyzbHIbmKEoOkwUgkLLosI,13983
139
- ingestr-0.13.50.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
140
- ingestr-0.13.50.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
141
- ingestr-0.13.50.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
142
- ingestr-0.13.50.dist-info/RECORD,,
138
+ ingestr-0.13.51.dist-info/METADATA,sha256=danPRyBhqlvyOwtw8kBmBh5FEY_xH6upsy5GrsFaMg4,13983
139
+ ingestr-0.13.51.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
140
+ ingestr-0.13.51.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
141
+ ingestr-0.13.51.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
142
+ ingestr-0.13.51.dist-info/RECORD,,