ingestr 0.8.4__py3-none-any.whl → 0.9.0__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.

@@ -0,0 +1,118 @@
1
+ from enum import Enum
2
+ from typing import Any, Dict, Iterator, Optional, Tuple
3
+
4
+ from dlt.common.typing import DictStrStr, TDataItems, TSecretValue
5
+ from dlt.sources.helpers.requests import client
6
+
7
+ from .. import settings
8
+ from .credentials import (
9
+ TZendeskCredentials,
10
+ ZendeskCredentialsEmailPass,
11
+ ZendeskCredentialsOAuth,
12
+ ZendeskCredentialsToken,
13
+ )
14
+
15
+
16
+ class PaginationType(Enum):
17
+ OFFSET = 0
18
+ CURSOR = 1
19
+ STREAM = 2
20
+ START_TIME = 3
21
+
22
+
23
+ class ZendeskAPIClient:
24
+ """
25
+ API client used to make requests to Zendesk talk, support and chat API
26
+ """
27
+
28
+ subdomain: str = ""
29
+ url: str = ""
30
+ headers: Optional[DictStrStr]
31
+ auth: Optional[Tuple[str, TSecretValue]]
32
+
33
+ def __init__(
34
+ self, credentials: TZendeskCredentials, url_prefix: Optional[str] = None
35
+ ) -> None:
36
+ """
37
+ Initializer for the API client which is then used to make API calls to the ZendeskAPI
38
+
39
+ Args:
40
+ credentials: ZendeskCredentials object which contains the necessary credentials to authenticate to ZendeskAPI
41
+ """
42
+ # oauth token is the preferred way to authenticate, followed by api token and then email + password combo
43
+ # fill headers and auth for every possibility of credentials given, raise error if credentials are of incorrect type
44
+ if isinstance(credentials, ZendeskCredentialsOAuth):
45
+ self.headers = {"Authorization": f"Bearer {credentials.oauth_token}"}
46
+ self.auth = None
47
+ elif isinstance(credentials, ZendeskCredentialsToken):
48
+ self.headers = None
49
+ self.auth = (f"{credentials.email}/token", credentials.token)
50
+ elif isinstance(credentials, ZendeskCredentialsEmailPass):
51
+ self.auth = (credentials.email, credentials.password)
52
+ self.headers = None
53
+ else:
54
+ raise TypeError(
55
+ "Wrong credentials type provided to ZendeskAPIClient. The credentials need to be of type: ZendeskCredentialsOAuth, ZendeskCredentialsToken or ZendeskCredentialsEmailPass"
56
+ )
57
+
58
+ # If url_prefix is set it overrides the default API URL (e.g. chat api uses zopim.com domain)
59
+ if url_prefix:
60
+ self.url = url_prefix
61
+ else:
62
+ self.subdomain = credentials.subdomain
63
+ self.url = f"https://{self.subdomain}.zendesk.com"
64
+
65
+ def get_pages(
66
+ self,
67
+ endpoint: str,
68
+ data_point_name: str,
69
+ pagination: PaginationType,
70
+ params: Optional[Dict[str, Any]] = None,
71
+ ) -> Iterator[TDataItems]:
72
+ """
73
+ Makes a request to a paginated endpoint and returns a generator of data items per page.
74
+
75
+ Args:
76
+ endpoint: The url to the endpoint, e.g. /api/v2/calls
77
+ data_point_name: The key which data items are nested under in the response object (e.g. calls)
78
+ params: Optional dict of query params to include in the request
79
+ pagination: Type of pagination type used by endpoint
80
+
81
+ Returns:
82
+ Generator of pages, each page is a list of dict data items
83
+ """
84
+ # update the page size to enable cursor pagination
85
+ params = params or {}
86
+ if pagination == PaginationType.CURSOR:
87
+ params["page[size]"] = settings.PAGE_SIZE
88
+ elif pagination == PaginationType.STREAM:
89
+ params["per_page"] = settings.INCREMENTAL_PAGE_SIZE
90
+ elif pagination == PaginationType.START_TIME:
91
+ params["limit"] = settings.INCREMENTAL_PAGE_SIZE
92
+
93
+ # make request and keep looping until there is no next page
94
+ get_url = f"{self.url}{endpoint}"
95
+ while get_url:
96
+ response = client.get(
97
+ get_url, headers=self.headers, auth=self.auth, params=params
98
+ )
99
+ response.raise_for_status()
100
+ response_json = response.json()
101
+ result = response_json[data_point_name]
102
+ yield result
103
+
104
+ get_url = None
105
+ if pagination == PaginationType.CURSOR:
106
+ if response_json["meta"]["has_more"]:
107
+ get_url = response_json["links"]["next"]
108
+ elif pagination == PaginationType.OFFSET:
109
+ get_url = response_json.get("next_page", None)
110
+ elif pagination == PaginationType.STREAM:
111
+ # See https://developer.zendesk.com/api-reference/ticketing/ticket-management/incremental_exports/#json-format
112
+ if not response_json["end_of_stream"]:
113
+ get_url = response_json["next_page"]
114
+ elif pagination == PaginationType.START_TIME:
115
+ if response_json["count"] > 0:
116
+ get_url = response_json["next_page"]
117
+
118
+ params = {}
@@ -0,0 +1,57 @@
1
+ """Zendesk source settings and constants"""
2
+
3
+ from dlt.common import pendulum
4
+
5
+ DEFAULT_START_DATE = pendulum.datetime(year=2024, month=10, day=3)
6
+
7
+ INCREMENTAL_PAGE_SIZE = 1000
8
+ PAGE_SIZE = 100
9
+
10
+
11
+ CUSTOM_FIELDS_STATE_KEY = "ticket_custom_fields_v2"
12
+
13
+ # Tuples of (Resource name, endpoint URL, data_key, supports pagination)
14
+ # data_key is the key which data list is nested under in responses
15
+ # if the data key is None it is assumed to be the same as the resource name
16
+ # The last element of the tuple says if endpoint supports cursor pagination
17
+ SUPPORT_ENDPOINTS = [
18
+ ("users", "/api/v2/users.json", "users", True),
19
+ ("sla_policies", "/api/v2/slas/policies.json", None, False),
20
+ ("groups", "/api/v2/groups.json", None, True),
21
+ ("organizations", "/api/v2/organizations.json", None, True),
22
+ ("brands", "/api/v2/brands.json", None, True),
23
+ ]
24
+
25
+ SUPPORT_EXTRA_ENDPOINTS = [
26
+ ("activities", "/api/v2/activities.json", None, True),
27
+ ("automations", "/api/v2/automations.json", None, True),
28
+ ("macros", "/api/v2/macros.json", None, True),
29
+ ("recipient_addresses", "/api/v2/recipient_addresses.json", None, True),
30
+ ("requests", "/api/v2/requests.json", None, True),
31
+ ("targets", "/api/v2/targets.json", None, False),
32
+ ("ticket_forms", "/api/v2/ticket_forms.json", None, False),
33
+ ("ticket_metrics", "/api/v2/ticket_metrics.json", None, True),
34
+ ("triggers", "/api/v2/triggers.json", None, True),
35
+ ("user_fields", "/api/v2/user_fields.json", None, True),
36
+ ]
37
+
38
+ TALK_ENDPOINTS = [
39
+ ("calls", "/api/v2/channels/voice/calls", None, False),
40
+ ("addresses", "/api/v2/channels/voice/addresses", None, False),
41
+ ("greetings", "/api/v2/channels/voice/greetings", None, False),
42
+ ("phone_numbers", "/api/v2/channels/voice/phone_numbers", None, False),
43
+ ("settings", "/api/v2/channels/voice/settings", None, False),
44
+ ("lines", "/api/v2/channels/voice/lines", None, False),
45
+ ("agents_activity", "/api/v2/channels/voice/stats/agents_activity", None, False),
46
+ (
47
+ "current_queue_activity",
48
+ "/api/v2/channels/voice/stats/current_queue_activity",
49
+ None,
50
+ False,
51
+ ),
52
+ ]
53
+
54
+ INCREMENTAL_TALK_ENDPOINTS = {
55
+ "calls": "/api/v2/channels/voice/stats/incremental/calls.json",
56
+ "legs": "/api/v2/channels/voice/stats/incremental/legs.json",
57
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ingestr
3
- Version: 0.8.4
3
+ Version: 0.9.0
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
@@ -35,6 +35,7 @@ Requires-Dist: pyrate-limiter==3.6.1
35
35
  Requires-Dist: redshift-connector==2.1.0
36
36
  Requires-Dist: rich==13.7.1
37
37
  Requires-Dist: rudder-sdk-python==2.1.0
38
+ Requires-Dist: s3fs==2024.9.0
38
39
  Requires-Dist: snowflake-sqlalchemy==1.5.3
39
40
  Requires-Dist: sqlalchemy-bigquery==1.11.0
40
41
  Requires-Dist: sqlalchemy-hana==2.0.0
@@ -226,6 +227,11 @@ Join our Slack community [here](https://join.slack.com/t/bruindatacommunity/shar
226
227
  <td>Notion</td>
227
228
  <td>✅</td>
228
229
  <td>-</td>
230
+ </tr>
231
+ <tr>
232
+ <td>S3</td>
233
+ <td>✅</td>
234
+ <td>-</td>
229
235
  </tr>
230
236
  <tr>
231
237
  <td>Shopify</td>
@@ -242,6 +248,11 @@ Join our Slack community [here](https://join.slack.com/t/bruindatacommunity/shar
242
248
  <td>✅</td>
243
249
  <td>-</td>
244
250
  </tr>
251
+ <tr>
252
+ <td>Zendesk</td>
253
+ <td>✅</td>
254
+ <td>-</td>
255
+ </tr>
245
256
  </table>
246
257
 
247
258
  More to come soon!
@@ -1,10 +1,10 @@
1
1
  ingestr/main.py,sha256=U66TM57ePv-RdoAftQ0pFZx8woZUQnLepKa50C-bA5I,17655
2
2
  ingestr/src/.gitignore,sha256=8cX1AZTSI0TcdZFGTmS_oyBjpfCzhOEt0DdAo2dFIY8,203
3
3
  ingestr/src/destinations.py,sha256=2SfPMjtTelPmzQmc3zNs8xGcKIPuGn_hoZFIBUuhjXI,6338
4
- ingestr/src/factory.py,sha256=-_KwBpbNAegv_oXIB57klyjUb3K6e0lw_xdi5bwmarI,4645
5
- ingestr/src/sources.py,sha256=0IGguMm85E3Rahu6zVLawoe2d4lqRY31uHuxlqCsiQc,25324
4
+ ingestr/src/factory.py,sha256=NbSLbF2ClHRcptpmL0n9p7bGVz8Uj6xyG0Y93OZTfKM,4830
5
+ ingestr/src/sources.py,sha256=Ny7qYulZUmwrCsG8UZ_SeR6FIqVR3E1FWdvj_yQD41M,30786
6
6
  ingestr/src/table_definition.py,sha256=REbAbqdlmUMUuRh8nEQRreWjPVOQ5ZcfqGkScKdCrmk,390
7
- ingestr/src/version.py,sha256=jhHEJFZWhkQDemoZMomBYq-RNrKXknYzUaeIU9A6XsI,22
7
+ ingestr/src/version.py,sha256=H9NWRZb7NbeRRPLP_V1fARmLNXranorVM-OOY-8_2ug,22
8
8
  ingestr/src/adjust/_init_.py,sha256=_jJE3Ywvv-YyJ7ywICdht_X2Gnd1cKm6F1wAfnpXuWM,890
9
9
  ingestr/src/adjust/helpers.py,sha256=kkYC3MqMHLNucuQ50klZWrvd3o8VfUysNtZTQSsKZ_c,2588
10
10
  ingestr/src/airtable/__init__.py,sha256=GHWYrjI2qhs_JihdNJysB0Ni3bzqT_MLXn_S9_Q5zRA,2775
@@ -17,6 +17,9 @@ ingestr/src/facebook_ads/__init__.py,sha256=ZZyogV48gmhDcC3CYQEsC4qT3Q6JI9IOnMff
17
17
  ingestr/src/facebook_ads/exceptions.py,sha256=4Nlbc0Mv3i5g-9AoyT-n1PIa8IDi3VCTfEAzholx4Wc,115
18
18
  ingestr/src/facebook_ads/helpers.py,sha256=ZLbNHiKer5lPb4g3_435XeBJr57Wv0o1KTyBA1mQ100,9068
19
19
  ingestr/src/facebook_ads/settings.py,sha256=1IxZeP_4rN3IBvAncNHOoqpzAirx0Hz-MUK_tl6UTFk,4881
20
+ ingestr/src/filesystem/__init__.py,sha256=wHHaKFuAjsR_ZRjl6g_Flf6FhVs9qhwREthTr03_7cc,4162
21
+ ingestr/src/filesystem/helpers.py,sha256=bg0muSHZr3hMa8H4jN2-LGWzI-SUoKlQNiWJ74-YYms,3211
22
+ ingestr/src/filesystem/readers.py,sha256=a0fKkaRpnAOGsXI3EBNYZa7x6tlmAOsgRzb883StY30,3987
20
23
  ingestr/src/google_sheets/README.md,sha256=wFQhvmGpRA38Ba2N_WIax6duyD4c7c_pwvvprRfQDnw,5470
21
24
  ingestr/src/google_sheets/__init__.py,sha256=5qlX-6ilx5MW7klC7B_0jGSxloQSLkSESTh4nlY3Aos,6643
22
25
  ingestr/src/google_sheets/helpers/__init__.py,sha256=5hXZrZK8cMO3UOuL-s4OKOpdACdihQD0hYYlSEu-iQ8,35
@@ -39,7 +42,7 @@ ingestr/src/notion/settings.py,sha256=MwQVZViJtnvOegfjXYc_pJ50oUYgSRPgwqu7TvpeMO
39
42
  ingestr/src/notion/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
43
  ingestr/src/notion/helpers/client.py,sha256=QXuudkf5Zzff98HRsCqA1g1EZWIrnfn1falPrnKg_y4,5500
41
44
  ingestr/src/notion/helpers/database.py,sha256=gigPibTeVefP3lA-8w4aOwX67pj7RlciPk5koDs1ry8,2737
42
- ingestr/src/shopify/__init__.py,sha256=UyPquBb9FYww8KMotdJexy8OP69ArnZpbDFoVh-yZeU,62691
45
+ ingestr/src/shopify/__init__.py,sha256=LWO5hfPfwPrq-gZwSRHThDfLHbGo2wkm6CrN1BCm3CQ,62815
43
46
  ingestr/src/shopify/exceptions.py,sha256=BhV3lIVWeBt8Eh4CWGW_REFJpGCzvW6-62yZrBWa3nQ,50
44
47
  ingestr/src/shopify/helpers.py,sha256=NfHD6lWXe88ybR0ri-FCQuh2Vf8l5WG0a0FVjmdoSC4,6296
45
48
  ingestr/src/shopify/settings.py,sha256=StY0EPr7wFJ7KzRRDN4TKxV0_gkIS1wPj2eR4AYSsDk,141
@@ -56,6 +59,12 @@ ingestr/src/stripe_analytics/helpers.py,sha256=iqZOyiGIOhOAhVXXU16DP0hkkTKcTrDu6
56
59
  ingestr/src/stripe_analytics/settings.py,sha256=rl9L5XumxO0pjkZf7MGesXHp4QLRgnz3RWLuDWDBKXo,380
57
60
  ingestr/src/telemetry/event.py,sha256=MpWc5tt0lSJ1pWKe9HQ11BHrcPBxSH40l4wjZi9u0tI,924
58
61
  ingestr/src/testdata/fakebqcredentials.json,sha256=scc6TUc963KAbKTLZCfcmqVzbtzDCW1_8JNRnyAXyy8,628
62
+ ingestr/src/zendesk/__init__.py,sha256=FjsDhzKaK7RgFs9zIGjMHHl8TlfYRN3TKocbGiJklD4,17570
63
+ ingestr/src/zendesk/settings.py,sha256=Vdj706nTJFQ-3KH4nO97iYCQuba3dV3E9gfnmLK6xwU,2294
64
+ ingestr/src/zendesk/helpers/__init__.py,sha256=YTJejCiUjfIcsj9FrkY0l-JGYDI7RRte1Ydq5FDH_0c,888
65
+ ingestr/src/zendesk/helpers/api_helpers.py,sha256=dMkNn4ZQXgJTDOXAAXdmRt41phNFoRhYyPaLJih0pZY,4184
66
+ ingestr/src/zendesk/helpers/credentials.py,sha256=EWyi0ZlxWFgd1huD86KNF4dApLHgmabqWksFpEg1cf0,1332
67
+ ingestr/src/zendesk/helpers/talk_api.py,sha256=TSVSOErsBZvxcX91LMhAgvy6yLSYvpuVfOyKViOHtvA,4718
59
68
  ingestr/testdata/.gitignore,sha256=DFzYYOpqdTiT7S1HjCT-jffZSmEvFZge295_upAB0FY,13
60
69
  ingestr/testdata/create_replace.csv,sha256=TQDbOSkRKq9ZZv1d68Qjwh94aIyUQ-oEwxpJIrd3YK8,1060
61
70
  ingestr/testdata/delete_insert_expected.csv,sha256=wbj7uboVWwm3sNMh1n7f4-OKFEQJv1s96snjEHp9nkg,336
@@ -64,8 +73,8 @@ ingestr/testdata/delete_insert_part2.csv,sha256=B_KUzpzbNdDY_n7wWop1mT2cz36TmayS
64
73
  ingestr/testdata/merge_expected.csv,sha256=DReHqWGnQMsf2PBv_Q2pfjsgvikYFnf1zYcQZ7ZqYN0,276
65
74
  ingestr/testdata/merge_part1.csv,sha256=Pw8Z9IDKcNU0qQHx1z6BUf4rF_-SxKGFOvymCt4OY9I,185
66
75
  ingestr/testdata/merge_part2.csv,sha256=T_GiWxA81SN63_tMOIuemcvboEFeAmbKc7xRXvL9esw,287
67
- ingestr-0.8.4.dist-info/METADATA,sha256=3A0SR09KmNhx0b7jDkrqTM3PwBqtm77dG3XqhCwemHM,6755
68
- ingestr-0.8.4.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
69
- ingestr-0.8.4.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
70
- ingestr-0.8.4.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
71
- ingestr-0.8.4.dist-info/RECORD,,
76
+ ingestr-0.9.0.dist-info/METADATA,sha256=yJZ6Tdum4TirrssuFG40UZ_zm4pzhzSSweJRIignFck,6949
77
+ ingestr-0.9.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
78
+ ingestr-0.9.0.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
79
+ ingestr-0.9.0.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
80
+ ingestr-0.9.0.dist-info/RECORD,,