ingestr 0.13.75__py3-none-any.whl → 0.14.98__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 +22 -3
- ingestr/src/adjust/__init__.py +4 -4
- ingestr/src/allium/__init__.py +128 -0
- ingestr/src/anthropic/__init__.py +277 -0
- ingestr/src/anthropic/helpers.py +525 -0
- ingestr/src/appstore/__init__.py +1 -0
- ingestr/src/asana_source/__init__.py +1 -1
- ingestr/src/buildinfo.py +1 -1
- ingestr/src/chess/__init__.py +1 -1
- ingestr/src/couchbase_source/__init__.py +118 -0
- ingestr/src/couchbase_source/helpers.py +135 -0
- ingestr/src/cursor/__init__.py +83 -0
- ingestr/src/cursor/helpers.py +188 -0
- ingestr/src/destinations.py +169 -1
- ingestr/src/docebo/__init__.py +589 -0
- ingestr/src/docebo/client.py +435 -0
- ingestr/src/docebo/helpers.py +97 -0
- ingestr/src/elasticsearch/helpers.py +138 -0
- ingestr/src/errors.py +8 -0
- ingestr/src/facebook_ads/__init__.py +26 -23
- ingestr/src/facebook_ads/helpers.py +47 -1
- ingestr/src/factory.py +48 -0
- ingestr/src/filesystem/__init__.py +8 -3
- ingestr/src/filters.py +9 -0
- ingestr/src/fluxx/__init__.py +9906 -0
- ingestr/src/fluxx/helpers.py +209 -0
- ingestr/src/frankfurter/__init__.py +157 -163
- ingestr/src/frankfurter/helpers.py +3 -3
- ingestr/src/freshdesk/__init__.py +25 -8
- ingestr/src/freshdesk/freshdesk_client.py +40 -5
- ingestr/src/fundraiseup/__init__.py +49 -0
- ingestr/src/fundraiseup/client.py +81 -0
- ingestr/src/github/__init__.py +6 -4
- ingestr/src/google_analytics/__init__.py +1 -1
- ingestr/src/hostaway/__init__.py +302 -0
- ingestr/src/hostaway/client.py +288 -0
- ingestr/src/http/__init__.py +35 -0
- ingestr/src/http/readers.py +114 -0
- ingestr/src/hubspot/__init__.py +6 -12
- ingestr/src/influxdb/__init__.py +1 -0
- ingestr/src/intercom/__init__.py +142 -0
- ingestr/src/intercom/helpers.py +674 -0
- ingestr/src/intercom/settings.py +279 -0
- ingestr/src/jira_source/__init__.py +340 -0
- ingestr/src/jira_source/helpers.py +439 -0
- ingestr/src/jira_source/settings.py +170 -0
- ingestr/src/klaviyo/__init__.py +5 -5
- ingestr/src/linear/__init__.py +553 -116
- ingestr/src/linear/helpers.py +77 -38
- ingestr/src/mailchimp/__init__.py +126 -0
- ingestr/src/mailchimp/helpers.py +226 -0
- ingestr/src/mailchimp/settings.py +164 -0
- ingestr/src/masking.py +344 -0
- ingestr/src/monday/__init__.py +246 -0
- ingestr/src/monday/helpers.py +392 -0
- ingestr/src/monday/settings.py +328 -0
- ingestr/src/mongodb/__init__.py +5 -2
- ingestr/src/mongodb/helpers.py +384 -10
- ingestr/src/plusvibeai/__init__.py +335 -0
- ingestr/src/plusvibeai/helpers.py +544 -0
- ingestr/src/plusvibeai/settings.py +252 -0
- ingestr/src/revenuecat/__init__.py +83 -0
- ingestr/src/revenuecat/helpers.py +237 -0
- ingestr/src/salesforce/__init__.py +15 -8
- ingestr/src/shopify/__init__.py +1 -1
- ingestr/src/smartsheets/__init__.py +33 -5
- ingestr/src/socrata_source/__init__.py +83 -0
- ingestr/src/socrata_source/helpers.py +85 -0
- ingestr/src/socrata_source/settings.py +8 -0
- ingestr/src/sources.py +1418 -54
- ingestr/src/stripe_analytics/__init__.py +2 -19
- ingestr/src/wise/__init__.py +68 -0
- ingestr/src/wise/client.py +63 -0
- ingestr/tests/unit/test_smartsheets.py +6 -9
- {ingestr-0.13.75.dist-info → ingestr-0.14.98.dist-info}/METADATA +24 -12
- {ingestr-0.13.75.dist-info → ingestr-0.14.98.dist-info}/RECORD +79 -37
- {ingestr-0.13.75.dist-info → ingestr-0.14.98.dist-info}/WHEEL +0 -0
- {ingestr-0.13.75.dist-info → ingestr-0.14.98.dist-info}/entry_points.txt +0 -0
- {ingestr-0.13.75.dist-info → ingestr-0.14.98.dist-info}/licenses/LICENSE.md +0 -0
ingestr/src/masking.py
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import hmac
|
|
3
|
+
import random
|
|
4
|
+
import re
|
|
5
|
+
import string
|
|
6
|
+
import uuid
|
|
7
|
+
from datetime import date, datetime, timedelta
|
|
8
|
+
from typing import Any, Callable, Dict, Optional, Tuple, Union
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MaskingEngine:
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self.token_cache: Dict[str, Union[str, int]] = {}
|
|
14
|
+
self.sequential_counter = 0
|
|
15
|
+
|
|
16
|
+
def parse_mask_config(self, config: str) -> Tuple[str, str, Optional[str]]:
|
|
17
|
+
parts = config.split(":")
|
|
18
|
+
if len(parts) == 2:
|
|
19
|
+
return parts[0], parts[1], None
|
|
20
|
+
elif len(parts) == 3:
|
|
21
|
+
return parts[0], parts[1], parts[2]
|
|
22
|
+
else:
|
|
23
|
+
raise ValueError(
|
|
24
|
+
f"Invalid mask configuration: {config}. Expected format: 'column:algorithm[:param]'"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def get_masking_function(
|
|
28
|
+
self, algorithm: str, param: Optional[str] = None
|
|
29
|
+
) -> Callable:
|
|
30
|
+
algorithm = algorithm.lower()
|
|
31
|
+
|
|
32
|
+
# Hash-based masking
|
|
33
|
+
if algorithm == "hash" or algorithm == "sha256":
|
|
34
|
+
return self._hash_sha256
|
|
35
|
+
elif algorithm == "md5":
|
|
36
|
+
return self._hash_md5
|
|
37
|
+
elif algorithm == "hmac":
|
|
38
|
+
return lambda x: self._hash_hmac(x, param or "default-key")
|
|
39
|
+
|
|
40
|
+
# Format-preserving masking
|
|
41
|
+
elif algorithm == "email":
|
|
42
|
+
return self._mask_email
|
|
43
|
+
elif algorithm == "phone":
|
|
44
|
+
return self._mask_phone
|
|
45
|
+
elif algorithm == "credit_card":
|
|
46
|
+
return self._mask_credit_card
|
|
47
|
+
elif algorithm == "ssn":
|
|
48
|
+
return self._mask_ssn
|
|
49
|
+
|
|
50
|
+
# Redaction strategies
|
|
51
|
+
elif algorithm == "redact":
|
|
52
|
+
return lambda x: "REDACTED"
|
|
53
|
+
elif algorithm == "stars":
|
|
54
|
+
return lambda x: "*" * len(str(x)) if x else ""
|
|
55
|
+
elif algorithm == "fixed":
|
|
56
|
+
return lambda x: param or "MASKED"
|
|
57
|
+
elif algorithm == "random":
|
|
58
|
+
return self._random_replace
|
|
59
|
+
|
|
60
|
+
# Partial masking
|
|
61
|
+
elif algorithm == "partial":
|
|
62
|
+
chars = int(param) if param else 2
|
|
63
|
+
return lambda x: self._partial_mask(x, chars)
|
|
64
|
+
elif algorithm == "first_letter":
|
|
65
|
+
return self._first_letter_mask
|
|
66
|
+
|
|
67
|
+
# Tokenization
|
|
68
|
+
elif algorithm == "uuid":
|
|
69
|
+
return self._tokenize_uuid
|
|
70
|
+
elif algorithm == "sequential":
|
|
71
|
+
return self._tokenize_sequential
|
|
72
|
+
|
|
73
|
+
# Numeric masking
|
|
74
|
+
elif algorithm == "round":
|
|
75
|
+
precision = int(param) if param else 10
|
|
76
|
+
return lambda x: self._round_number(x, precision)
|
|
77
|
+
elif algorithm == "range":
|
|
78
|
+
bucket_size = int(param) if param else 100
|
|
79
|
+
return lambda x: self._range_mask(x, bucket_size)
|
|
80
|
+
elif algorithm == "noise":
|
|
81
|
+
noise_level = float(param) if param else 0.1
|
|
82
|
+
return lambda x: self._add_noise(x, noise_level)
|
|
83
|
+
|
|
84
|
+
# Date masking
|
|
85
|
+
elif algorithm == "date_shift":
|
|
86
|
+
max_days = int(param) if param else 30
|
|
87
|
+
return lambda x: self._date_shift(x, max_days)
|
|
88
|
+
elif algorithm == "year_only":
|
|
89
|
+
return self._year_only
|
|
90
|
+
elif algorithm == "month_year":
|
|
91
|
+
return self._month_year
|
|
92
|
+
|
|
93
|
+
else:
|
|
94
|
+
raise ValueError(f"Unknown masking algorithm: {algorithm}")
|
|
95
|
+
|
|
96
|
+
# Hash functions
|
|
97
|
+
def _hash_sha256(self, value: Any) -> Optional[str]:
|
|
98
|
+
if value is None:
|
|
99
|
+
return None
|
|
100
|
+
return hashlib.sha256(str(value).encode()).hexdigest()
|
|
101
|
+
|
|
102
|
+
def _hash_md5(self, value: Any) -> Optional[str]:
|
|
103
|
+
if value is None:
|
|
104
|
+
return None
|
|
105
|
+
return hashlib.md5(str(value).encode()).hexdigest()
|
|
106
|
+
|
|
107
|
+
def _hash_hmac(self, value: Any, key: str) -> Optional[str]:
|
|
108
|
+
if value is None:
|
|
109
|
+
return None
|
|
110
|
+
return hmac.new(key.encode(), str(value).encode(), hashlib.sha256).hexdigest()
|
|
111
|
+
|
|
112
|
+
# Format-preserving masks
|
|
113
|
+
def _mask_email(self, value: Any) -> Any:
|
|
114
|
+
if value is None or not value:
|
|
115
|
+
return value
|
|
116
|
+
email_str = str(value)
|
|
117
|
+
if "@" not in email_str:
|
|
118
|
+
return self._partial_mask(email_str, 2)
|
|
119
|
+
|
|
120
|
+
local, domain = email_str.split("@", 1)
|
|
121
|
+
if len(local) <= 2:
|
|
122
|
+
masked_local = "*" * len(local)
|
|
123
|
+
else:
|
|
124
|
+
masked_local = local[0] + "*" * (len(local) - 2) + local[-1]
|
|
125
|
+
return f"{masked_local}@{domain}"
|
|
126
|
+
|
|
127
|
+
def _mask_phone(self, value: Any) -> Any:
|
|
128
|
+
if value is None or not value:
|
|
129
|
+
return value
|
|
130
|
+
phone_str = re.sub(r"\D", "", str(value))
|
|
131
|
+
if len(phone_str) < 10:
|
|
132
|
+
return "*" * len(phone_str)
|
|
133
|
+
|
|
134
|
+
# Keep country code and area code, mask the rest
|
|
135
|
+
if len(phone_str) >= 10:
|
|
136
|
+
return phone_str[:3] + "-***-****"
|
|
137
|
+
return phone_str
|
|
138
|
+
|
|
139
|
+
def _mask_credit_card(self, value: Any) -> Any:
|
|
140
|
+
if value is None or not value:
|
|
141
|
+
return value
|
|
142
|
+
cc_str = re.sub(r"\D", "", str(value))
|
|
143
|
+
if len(cc_str) < 12:
|
|
144
|
+
return "*" * len(cc_str)
|
|
145
|
+
return "*" * (len(cc_str) - 4) + cc_str[-4:]
|
|
146
|
+
|
|
147
|
+
def _mask_ssn(self, value: Any) -> Any:
|
|
148
|
+
if value is None or not value:
|
|
149
|
+
return value
|
|
150
|
+
ssn_str = re.sub(r"\D", "", str(value))
|
|
151
|
+
if len(ssn_str) != 9:
|
|
152
|
+
return "*" * len(ssn_str)
|
|
153
|
+
return "***-**-" + ssn_str[-4:]
|
|
154
|
+
|
|
155
|
+
# Partial masking
|
|
156
|
+
def _partial_mask(self, value: Any, chars_to_show: int) -> Any:
|
|
157
|
+
if value is None or not value:
|
|
158
|
+
return value
|
|
159
|
+
val_str = str(value)
|
|
160
|
+
if len(val_str) <= chars_to_show * 2:
|
|
161
|
+
return "*" * len(val_str)
|
|
162
|
+
return (
|
|
163
|
+
val_str[:chars_to_show]
|
|
164
|
+
+ "*" * (len(val_str) - chars_to_show * 2)
|
|
165
|
+
+ val_str[-chars_to_show:]
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
def _first_letter_mask(self, value: Any) -> Any:
|
|
169
|
+
if value is None or not value:
|
|
170
|
+
return value
|
|
171
|
+
val_str = str(value)
|
|
172
|
+
if len(val_str) <= 1:
|
|
173
|
+
return val_str
|
|
174
|
+
return val_str[0] + "*" * (len(val_str) - 1)
|
|
175
|
+
|
|
176
|
+
# Random replacement
|
|
177
|
+
def _random_replace(self, value: Any) -> Any:
|
|
178
|
+
if value is None:
|
|
179
|
+
return value
|
|
180
|
+
|
|
181
|
+
if isinstance(value, (int, float)):
|
|
182
|
+
# Generate random number in similar range
|
|
183
|
+
if isinstance(value, int):
|
|
184
|
+
magnitude = len(str(abs(value)))
|
|
185
|
+
return random.randint(10 ** (magnitude - 1), 10**magnitude - 1)
|
|
186
|
+
else:
|
|
187
|
+
return random.uniform(0, abs(value) * 2)
|
|
188
|
+
elif isinstance(value, str):
|
|
189
|
+
# Generate random string of same length
|
|
190
|
+
return "".join(
|
|
191
|
+
random.choices(string.ascii_letters + string.digits, k=len(value))
|
|
192
|
+
)
|
|
193
|
+
else:
|
|
194
|
+
return str(value)
|
|
195
|
+
|
|
196
|
+
# Tokenization
|
|
197
|
+
def _tokenize_uuid(self, value: Any) -> Optional[str]:
|
|
198
|
+
if value is None:
|
|
199
|
+
return None
|
|
200
|
+
val_str = str(value)
|
|
201
|
+
if val_str not in self.token_cache:
|
|
202
|
+
self.token_cache[val_str] = str(uuid.uuid4())
|
|
203
|
+
return str(self.token_cache[val_str])
|
|
204
|
+
|
|
205
|
+
def _tokenize_sequential(self, value: Any) -> Optional[int]:
|
|
206
|
+
if value is None:
|
|
207
|
+
return None
|
|
208
|
+
val_str = str(value)
|
|
209
|
+
if val_str not in self.token_cache:
|
|
210
|
+
self.sequential_counter += 1
|
|
211
|
+
self.token_cache[val_str] = self.sequential_counter
|
|
212
|
+
return int(self.token_cache[val_str])
|
|
213
|
+
|
|
214
|
+
# Numeric masking
|
|
215
|
+
def _round_number(self, value: Any, precision: int) -> Any:
|
|
216
|
+
if value is None:
|
|
217
|
+
return value
|
|
218
|
+
try:
|
|
219
|
+
num = float(value)
|
|
220
|
+
return round(num / precision) * precision
|
|
221
|
+
except (ValueError, TypeError):
|
|
222
|
+
return value
|
|
223
|
+
|
|
224
|
+
def _range_mask(self, value: Any, bucket_size: int) -> Any:
|
|
225
|
+
if value is None:
|
|
226
|
+
return value
|
|
227
|
+
try:
|
|
228
|
+
num = float(value)
|
|
229
|
+
lower = int(num // bucket_size) * bucket_size
|
|
230
|
+
upper = lower + bucket_size
|
|
231
|
+
return f"{lower}-{upper}"
|
|
232
|
+
except (ValueError, TypeError):
|
|
233
|
+
return value
|
|
234
|
+
|
|
235
|
+
def _add_noise(self, value: Any, noise_level: float) -> Any:
|
|
236
|
+
if value is None:
|
|
237
|
+
return value
|
|
238
|
+
try:
|
|
239
|
+
num = float(value)
|
|
240
|
+
noise = random.uniform(-noise_level, noise_level) * abs(num)
|
|
241
|
+
result = num + noise
|
|
242
|
+
if isinstance(value, int):
|
|
243
|
+
return int(result)
|
|
244
|
+
return result
|
|
245
|
+
except (ValueError, TypeError):
|
|
246
|
+
return value
|
|
247
|
+
|
|
248
|
+
# Date masking
|
|
249
|
+
def _date_shift(self, value: Any, max_days: int) -> Any:
|
|
250
|
+
if value is None:
|
|
251
|
+
return value
|
|
252
|
+
|
|
253
|
+
if isinstance(value, (date, datetime)):
|
|
254
|
+
shift_days = random.randint(-max_days, max_days)
|
|
255
|
+
return value + timedelta(days=shift_days)
|
|
256
|
+
|
|
257
|
+
# Try to parse string dates
|
|
258
|
+
try:
|
|
259
|
+
from dateutil import parser # type: ignore
|
|
260
|
+
|
|
261
|
+
dt = parser.parse(str(value))
|
|
262
|
+
shift_days = random.randint(-max_days, max_days)
|
|
263
|
+
result = dt + timedelta(days=shift_days)
|
|
264
|
+
if isinstance(value, str):
|
|
265
|
+
return result.strftime("%Y-%m-%d")
|
|
266
|
+
return result
|
|
267
|
+
except Exception:
|
|
268
|
+
return value
|
|
269
|
+
|
|
270
|
+
def _year_only(self, value: Any) -> Any:
|
|
271
|
+
if value is None:
|
|
272
|
+
return value
|
|
273
|
+
|
|
274
|
+
if isinstance(value, (date, datetime)):
|
|
275
|
+
return value.year
|
|
276
|
+
|
|
277
|
+
# Try to parse string dates
|
|
278
|
+
try:
|
|
279
|
+
from dateutil import parser
|
|
280
|
+
|
|
281
|
+
dt = parser.parse(str(value))
|
|
282
|
+
return dt.year
|
|
283
|
+
except Exception:
|
|
284
|
+
return value
|
|
285
|
+
|
|
286
|
+
def _month_year(self, value: Any) -> Any:
|
|
287
|
+
if value is None:
|
|
288
|
+
return value
|
|
289
|
+
|
|
290
|
+
if isinstance(value, (date, datetime)):
|
|
291
|
+
return f"{value.year}-{value.month:02d}"
|
|
292
|
+
|
|
293
|
+
# Try to parse string dates
|
|
294
|
+
try:
|
|
295
|
+
from dateutil import parser
|
|
296
|
+
|
|
297
|
+
dt = parser.parse(str(value))
|
|
298
|
+
return f"{dt.year}-{dt.month:02d}"
|
|
299
|
+
except Exception:
|
|
300
|
+
return value
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def create_masking_mapper(mask_configs: list[str]) -> Callable:
|
|
304
|
+
engine = MaskingEngine()
|
|
305
|
+
|
|
306
|
+
# Parse all configurations
|
|
307
|
+
masks = {}
|
|
308
|
+
for config in mask_configs:
|
|
309
|
+
column, algorithm, param = engine.parse_mask_config(config)
|
|
310
|
+
masks[column] = engine.get_masking_function(algorithm, param)
|
|
311
|
+
|
|
312
|
+
def apply_masks(data: Any) -> Any:
|
|
313
|
+
# Handle PyArrow tables
|
|
314
|
+
try:
|
|
315
|
+
import pyarrow as pa # type: ignore
|
|
316
|
+
|
|
317
|
+
if isinstance(data, pa.Table):
|
|
318
|
+
# Convert to pandas for easier manipulation
|
|
319
|
+
df = data.to_pandas()
|
|
320
|
+
|
|
321
|
+
# Apply masks to each column
|
|
322
|
+
for column, mask_func in masks.items():
|
|
323
|
+
if column in df.columns:
|
|
324
|
+
df[column] = df[column].apply(mask_func)
|
|
325
|
+
|
|
326
|
+
# Convert back to PyArrow table
|
|
327
|
+
return pa.Table.from_pandas(df)
|
|
328
|
+
except ImportError:
|
|
329
|
+
pass
|
|
330
|
+
|
|
331
|
+
# Handle dictionaries (original behavior)
|
|
332
|
+
if isinstance(data, dict):
|
|
333
|
+
for column, mask_func in masks.items():
|
|
334
|
+
if column in data:
|
|
335
|
+
try:
|
|
336
|
+
data[column] = mask_func(data[column])
|
|
337
|
+
except Exception as e:
|
|
338
|
+
print(f"Warning: Failed to mask column {column}: {e}")
|
|
339
|
+
return data
|
|
340
|
+
|
|
341
|
+
# Return as-is if not a supported type
|
|
342
|
+
return data
|
|
343
|
+
|
|
344
|
+
return apply_masks
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Monday.com source for data extraction via GraphQL API.
|
|
3
|
+
|
|
4
|
+
This source provides access to Monday.com app installation data.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Iterable, Iterator, Optional
|
|
8
|
+
|
|
9
|
+
import dlt
|
|
10
|
+
from dlt.sources import DltResource
|
|
11
|
+
|
|
12
|
+
from .helpers import MondayClient, normalize_dict
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dlt.source(max_table_nesting=0, name="monday_source")
|
|
16
|
+
def monday_source(
|
|
17
|
+
api_token: str,
|
|
18
|
+
params: list[str],
|
|
19
|
+
start_date: Optional[str] = None,
|
|
20
|
+
end_date: Optional[str] = None,
|
|
21
|
+
) -> Iterable[DltResource]:
|
|
22
|
+
"""
|
|
23
|
+
Monday.com data source.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
api_token: Monday.com API token for authentication
|
|
27
|
+
params: Table-specific parameters in format [table_type, ...params]
|
|
28
|
+
start_date: Optional start date for date-filtered queries (YYYY-MM-DD)
|
|
29
|
+
end_date: Optional end date for date-filtered queries (YYYY-MM-DD)
|
|
30
|
+
|
|
31
|
+
Yields:
|
|
32
|
+
DltResource: Data resource for the requested table
|
|
33
|
+
"""
|
|
34
|
+
monday_client = MondayClient(api_token)
|
|
35
|
+
|
|
36
|
+
@dlt.resource(
|
|
37
|
+
name="account",
|
|
38
|
+
write_disposition="replace",
|
|
39
|
+
)
|
|
40
|
+
def fetch_account() -> Iterator[dict[str, Any]]:
|
|
41
|
+
"""
|
|
42
|
+
Fetch account information from Monday.com.
|
|
43
|
+
|
|
44
|
+
Table format: account (no parameters needed)
|
|
45
|
+
"""
|
|
46
|
+
if len(params) != 0:
|
|
47
|
+
raise ValueError("Account table must be in the format `account`")
|
|
48
|
+
|
|
49
|
+
yield normalize_dict(monday_client.get_account())
|
|
50
|
+
|
|
51
|
+
@dlt.resource(
|
|
52
|
+
name="account_roles",
|
|
53
|
+
write_disposition="replace",
|
|
54
|
+
)
|
|
55
|
+
def fetch_account_roles() -> Iterator[dict[str, Any]]:
|
|
56
|
+
"""
|
|
57
|
+
Fetch account roles from Monday.com.
|
|
58
|
+
|
|
59
|
+
Table format: account_roles (no parameters needed)
|
|
60
|
+
"""
|
|
61
|
+
if len(params) != 0:
|
|
62
|
+
raise ValueError(
|
|
63
|
+
"Account roles table must be in the format `account_roles`"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
yield from monday_client.get_account_roles()
|
|
67
|
+
|
|
68
|
+
@dlt.resource(
|
|
69
|
+
name="users",
|
|
70
|
+
write_disposition="replace",
|
|
71
|
+
)
|
|
72
|
+
def fetch_users() -> Iterator[dict[str, Any]]:
|
|
73
|
+
"""
|
|
74
|
+
Fetch users from Monday.com.
|
|
75
|
+
|
|
76
|
+
Table format: users (no parameters needed)
|
|
77
|
+
"""
|
|
78
|
+
if len(params) != 0:
|
|
79
|
+
raise ValueError("Users table must be in the format `users`")
|
|
80
|
+
|
|
81
|
+
yield from monday_client.get_users()
|
|
82
|
+
|
|
83
|
+
@dlt.resource(
|
|
84
|
+
name="boards",
|
|
85
|
+
write_disposition="merge",
|
|
86
|
+
primary_key="id",
|
|
87
|
+
)
|
|
88
|
+
def fetch_boards(
|
|
89
|
+
updated_at: dlt.sources.incremental[str] = dlt.sources.incremental(
|
|
90
|
+
"updated_at", initial_value=start_date
|
|
91
|
+
),
|
|
92
|
+
) -> Iterator[dict[str, Any]]:
|
|
93
|
+
"""
|
|
94
|
+
Fetch boards from Monday.com.
|
|
95
|
+
|
|
96
|
+
Table format: boards (no parameters needed)
|
|
97
|
+
"""
|
|
98
|
+
if len(params) != 0:
|
|
99
|
+
raise ValueError("Boards table must be in the format `boards`")
|
|
100
|
+
|
|
101
|
+
yield from monday_client.get_boards()
|
|
102
|
+
|
|
103
|
+
@dlt.resource(
|
|
104
|
+
name="workspaces",
|
|
105
|
+
write_disposition="replace",
|
|
106
|
+
)
|
|
107
|
+
def fetch_workspaces() -> Iterator[dict[str, Any]]:
|
|
108
|
+
"""
|
|
109
|
+
Fetch workspaces from Monday.com.
|
|
110
|
+
|
|
111
|
+
Table format: workspaces (no parameters needed)
|
|
112
|
+
"""
|
|
113
|
+
if len(params) != 0:
|
|
114
|
+
raise ValueError("Workspaces table must be in the format `workspaces`")
|
|
115
|
+
|
|
116
|
+
yield from monday_client.get_workspaces()
|
|
117
|
+
|
|
118
|
+
@dlt.resource(
|
|
119
|
+
name="webhooks",
|
|
120
|
+
write_disposition="replace",
|
|
121
|
+
)
|
|
122
|
+
def fetch_webhooks() -> Iterator[dict[str, Any]]:
|
|
123
|
+
"""
|
|
124
|
+
Fetch webhooks from Monday.com.
|
|
125
|
+
|
|
126
|
+
Table format: webhooks (no parameters needed)
|
|
127
|
+
"""
|
|
128
|
+
if len(params) != 0:
|
|
129
|
+
raise ValueError("Webhooks table must be in the format `webhooks`")
|
|
130
|
+
|
|
131
|
+
yield from monday_client.get_webhooks()
|
|
132
|
+
|
|
133
|
+
@dlt.resource(
|
|
134
|
+
name="updates",
|
|
135
|
+
write_disposition="merge",
|
|
136
|
+
primary_key="id",
|
|
137
|
+
)
|
|
138
|
+
def fetch_updates(
|
|
139
|
+
updated_at: dlt.sources.incremental[str] = dlt.sources.incremental(
|
|
140
|
+
"updated_at", initial_value=start_date
|
|
141
|
+
),
|
|
142
|
+
) -> Iterator[dict[str, Any]]:
|
|
143
|
+
"""
|
|
144
|
+
Fetch updates from Monday.com.
|
|
145
|
+
|
|
146
|
+
Table format: updates (no parameters needed)
|
|
147
|
+
Requires start_date and end_date parameters
|
|
148
|
+
"""
|
|
149
|
+
if len(params) != 0:
|
|
150
|
+
raise ValueError("Updates table must be in the format `updates`")
|
|
151
|
+
|
|
152
|
+
yield from monday_client.get_updates(start_date=start_date, end_date=end_date)
|
|
153
|
+
|
|
154
|
+
@dlt.resource(
|
|
155
|
+
name="teams",
|
|
156
|
+
write_disposition="replace",
|
|
157
|
+
)
|
|
158
|
+
def fetch_teams() -> Iterator[dict[str, Any]]:
|
|
159
|
+
"""
|
|
160
|
+
Fetch teams from Monday.com.
|
|
161
|
+
|
|
162
|
+
Table format: teams (no parameters needed)
|
|
163
|
+
"""
|
|
164
|
+
if len(params) != 0:
|
|
165
|
+
raise ValueError("Teams table must be in the format `teams`")
|
|
166
|
+
|
|
167
|
+
yield from monday_client.get_teams()
|
|
168
|
+
|
|
169
|
+
@dlt.resource(
|
|
170
|
+
name="tags",
|
|
171
|
+
write_disposition="replace",
|
|
172
|
+
)
|
|
173
|
+
def fetch_tags() -> Iterator[dict[str, Any]]:
|
|
174
|
+
"""
|
|
175
|
+
Fetch tags from Monday.com.
|
|
176
|
+
|
|
177
|
+
Table format: tags (no parameters needed)
|
|
178
|
+
"""
|
|
179
|
+
if len(params) != 0:
|
|
180
|
+
raise ValueError("Tags table must be in the format `tags`")
|
|
181
|
+
|
|
182
|
+
yield from monday_client.get_tags()
|
|
183
|
+
|
|
184
|
+
@dlt.resource(
|
|
185
|
+
name="custom_activities",
|
|
186
|
+
write_disposition="replace",
|
|
187
|
+
)
|
|
188
|
+
def fetch_custom_activities() -> Iterator[dict[str, Any]]:
|
|
189
|
+
"""
|
|
190
|
+
Fetch custom activities from Monday.com.
|
|
191
|
+
|
|
192
|
+
Table format: custom_activities (no parameters needed)
|
|
193
|
+
"""
|
|
194
|
+
if len(params) != 0:
|
|
195
|
+
raise ValueError(
|
|
196
|
+
"Custom activities table must be in the format `custom_activities`"
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
yield from monday_client.get_custom_activities()
|
|
200
|
+
|
|
201
|
+
@dlt.resource(
|
|
202
|
+
name="board_columns",
|
|
203
|
+
write_disposition="replace",
|
|
204
|
+
)
|
|
205
|
+
def fetch_board_columns() -> Iterator[dict[str, Any]]:
|
|
206
|
+
"""
|
|
207
|
+
Fetch board columns from Monday.com.
|
|
208
|
+
|
|
209
|
+
Table format: board_columns (no parameters needed)
|
|
210
|
+
"""
|
|
211
|
+
if len(params) != 0:
|
|
212
|
+
raise ValueError(
|
|
213
|
+
"Board columns table must be in the format `board_columns`"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
yield from monday_client.get_board_columns()
|
|
217
|
+
|
|
218
|
+
@dlt.resource(
|
|
219
|
+
name="board_views",
|
|
220
|
+
write_disposition="replace",
|
|
221
|
+
)
|
|
222
|
+
def fetch_board_views() -> Iterator[dict[str, Any]]:
|
|
223
|
+
"""
|
|
224
|
+
Fetch board views from Monday.com.
|
|
225
|
+
|
|
226
|
+
Table format: board_views (no parameters needed)
|
|
227
|
+
"""
|
|
228
|
+
if len(params) != 0:
|
|
229
|
+
raise ValueError("Board views table must be in the format `board_views`")
|
|
230
|
+
|
|
231
|
+
yield from monday_client.get_board_views()
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
fetch_account,
|
|
235
|
+
fetch_account_roles,
|
|
236
|
+
fetch_users,
|
|
237
|
+
fetch_boards,
|
|
238
|
+
fetch_workspaces,
|
|
239
|
+
fetch_webhooks,
|
|
240
|
+
fetch_updates,
|
|
241
|
+
fetch_teams,
|
|
242
|
+
fetch_tags,
|
|
243
|
+
fetch_custom_activities,
|
|
244
|
+
fetch_board_columns,
|
|
245
|
+
fetch_board_views,
|
|
246
|
+
)
|