omniload 0.0.0.dev0__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.
Files changed (218) hide show
  1. omniload/conftest.py +72 -0
  2. omniload/main.py +810 -0
  3. omniload/src/.gitignore +10 -0
  4. omniload/src/adjust/__init__.py +108 -0
  5. omniload/src/adjust/adjust_helpers.py +122 -0
  6. omniload/src/airtable/__init__.py +84 -0
  7. omniload/src/allium/__init__.py +128 -0
  8. omniload/src/anthropic/__init__.py +277 -0
  9. omniload/src/anthropic/helpers.py +525 -0
  10. omniload/src/applovin/__init__.py +316 -0
  11. omniload/src/applovin_max/__init__.py +117 -0
  12. omniload/src/appsflyer/__init__.py +325 -0
  13. omniload/src/appsflyer/client.py +110 -0
  14. omniload/src/appstore/__init__.py +142 -0
  15. omniload/src/appstore/client.py +126 -0
  16. omniload/src/appstore/errors.py +15 -0
  17. omniload/src/appstore/models.py +117 -0
  18. omniload/src/appstore/resources.py +179 -0
  19. omniload/src/arrow/__init__.py +81 -0
  20. omniload/src/asana_source/__init__.py +281 -0
  21. omniload/src/asana_source/helpers.py +30 -0
  22. omniload/src/asana_source/settings.py +158 -0
  23. omniload/src/attio/__init__.py +102 -0
  24. omniload/src/attio/helpers.py +65 -0
  25. omniload/src/blob.py +95 -0
  26. omniload/src/bruin/__init__.py +76 -0
  27. omniload/src/chess/__init__.py +180 -0
  28. omniload/src/chess/helpers.py +35 -0
  29. omniload/src/chess/settings.py +18 -0
  30. omniload/src/clickup/__init__.py +85 -0
  31. omniload/src/clickup/helpers.py +47 -0
  32. omniload/src/collector/spinner.py +43 -0
  33. omniload/src/couchbase_source/__init__.py +118 -0
  34. omniload/src/couchbase_source/helpers.py +135 -0
  35. omniload/src/cursor/__init__.py +83 -0
  36. omniload/src/cursor/helpers.py +188 -0
  37. omniload/src/customer_io/__init__.py +486 -0
  38. omniload/src/customer_io/helpers.py +530 -0
  39. omniload/src/destinations.py +982 -0
  40. omniload/src/docebo/__init__.py +589 -0
  41. omniload/src/docebo/client.py +435 -0
  42. omniload/src/docebo/helpers.py +97 -0
  43. omniload/src/dune/__init__.py +104 -0
  44. omniload/src/dune/helpers.py +108 -0
  45. omniload/src/dynamodb/__init__.py +86 -0
  46. omniload/src/elasticsearch/__init__.py +80 -0
  47. omniload/src/elasticsearch/helpers.py +141 -0
  48. omniload/src/errors.py +26 -0
  49. omniload/src/facebook_ads/__init__.py +403 -0
  50. omniload/src/facebook_ads/exceptions.py +19 -0
  51. omniload/src/facebook_ads/helpers.py +296 -0
  52. omniload/src/facebook_ads/settings.py +224 -0
  53. omniload/src/facebook_ads/utils.py +53 -0
  54. omniload/src/factory.py +305 -0
  55. omniload/src/filesystem/__init__.py +133 -0
  56. omniload/src/filesystem/helpers.py +114 -0
  57. omniload/src/filesystem/readers.py +187 -0
  58. omniload/src/filters.py +62 -0
  59. omniload/src/fireflies/__init__.py +151 -0
  60. omniload/src/fireflies/helpers.py +753 -0
  61. omniload/src/fluxx/__init__.py +10013 -0
  62. omniload/src/fluxx/helpers.py +233 -0
  63. omniload/src/frankfurter/__init__.py +157 -0
  64. omniload/src/frankfurter/helpers.py +48 -0
  65. omniload/src/freshdesk/__init__.py +103 -0
  66. omniload/src/freshdesk/freshdesk_client.py +151 -0
  67. omniload/src/freshdesk/settings.py +23 -0
  68. omniload/src/fundraiseup/__init__.py +95 -0
  69. omniload/src/fundraiseup/client.py +81 -0
  70. omniload/src/github/__init__.py +202 -0
  71. omniload/src/github/helpers.py +207 -0
  72. omniload/src/github/queries.py +129 -0
  73. omniload/src/github/settings.py +24 -0
  74. omniload/src/google_ads/__init__.py +198 -0
  75. omniload/src/google_ads/field.py +17 -0
  76. omniload/src/google_ads/metrics.py +254 -0
  77. omniload/src/google_ads/predicates.py +37 -0
  78. omniload/src/google_ads/reports.py +411 -0
  79. omniload/src/google_ads/test_google_ads.py +184 -0
  80. omniload/src/google_analytics/__init__.py +144 -0
  81. omniload/src/google_analytics/helpers.py +312 -0
  82. omniload/src/google_sheets/README.md +95 -0
  83. omniload/src/google_sheets/__init__.py +166 -0
  84. omniload/src/google_sheets/helpers/__init__.py +15 -0
  85. omniload/src/google_sheets/helpers/api_calls.py +160 -0
  86. omniload/src/google_sheets/helpers/data_processing.py +316 -0
  87. omniload/src/gorgias/__init__.py +595 -0
  88. omniload/src/gorgias/helpers.py +166 -0
  89. omniload/src/hostaway/__init__.py +302 -0
  90. omniload/src/hostaway/client.py +288 -0
  91. omniload/src/http/__init__.py +38 -0
  92. omniload/src/http/readers.py +146 -0
  93. omniload/src/http_client.py +24 -0
  94. omniload/src/hubspot/__init__.py +800 -0
  95. omniload/src/hubspot/helpers.py +417 -0
  96. omniload/src/hubspot/settings.py +329 -0
  97. omniload/src/indeed/__init__.py +153 -0
  98. omniload/src/indeed/helpers.py +228 -0
  99. omniload/src/influxdb/__init__.py +46 -0
  100. omniload/src/influxdb/client.py +34 -0
  101. omniload/src/intercom/__init__.py +142 -0
  102. omniload/src/intercom/helpers.py +674 -0
  103. omniload/src/intercom/settings.py +279 -0
  104. omniload/src/isoc_pulse/__init__.py +159 -0
  105. omniload/src/jira_source/__init__.py +377 -0
  106. omniload/src/jira_source/helpers.py +510 -0
  107. omniload/src/jira_source/settings.py +184 -0
  108. omniload/src/kafka/__init__.py +120 -0
  109. omniload/src/kafka/helpers.py +241 -0
  110. omniload/src/kinesis/__init__.py +153 -0
  111. omniload/src/kinesis/helpers.py +96 -0
  112. omniload/src/klaviyo/__init__.py +237 -0
  113. omniload/src/klaviyo/client.py +212 -0
  114. omniload/src/klaviyo/helpers.py +19 -0
  115. omniload/src/linear/__init__.py +634 -0
  116. omniload/src/linear/helpers.py +111 -0
  117. omniload/src/linkedin_ads/__init__.py +266 -0
  118. omniload/src/linkedin_ads/dimension_time_enum.py +17 -0
  119. omniload/src/linkedin_ads/helpers.py +246 -0
  120. omniload/src/loader.py +69 -0
  121. omniload/src/mailchimp/__init__.py +126 -0
  122. omniload/src/mailchimp/helpers.py +226 -0
  123. omniload/src/mailchimp/settings.py +164 -0
  124. omniload/src/masking.py +344 -0
  125. omniload/src/mixpanel/__init__.py +62 -0
  126. omniload/src/mixpanel/client.py +104 -0
  127. omniload/src/monday/__init__.py +246 -0
  128. omniload/src/monday/helpers.py +392 -0
  129. omniload/src/monday/settings.py +325 -0
  130. omniload/src/mongodb/__init__.py +281 -0
  131. omniload/src/mongodb/helpers.py +975 -0
  132. omniload/src/notion/__init__.py +69 -0
  133. omniload/src/notion/helpers/__init__.py +14 -0
  134. omniload/src/notion/helpers/client.py +178 -0
  135. omniload/src/notion/helpers/database.py +92 -0
  136. omniload/src/notion/settings.py +17 -0
  137. omniload/src/partition.py +32 -0
  138. omniload/src/personio/__init__.py +345 -0
  139. omniload/src/personio/helpers.py +100 -0
  140. omniload/src/phantombuster/__init__.py +65 -0
  141. omniload/src/phantombuster/client.py +87 -0
  142. omniload/src/pinterest/__init__.py +82 -0
  143. omniload/src/pipedrive/__init__.py +212 -0
  144. omniload/src/pipedrive/helpers/__init__.py +37 -0
  145. omniload/src/pipedrive/helpers/custom_fields_munger.py +116 -0
  146. omniload/src/pipedrive/helpers/pages.py +129 -0
  147. omniload/src/pipedrive/settings.py +41 -0
  148. omniload/src/pipedrive/typing.py +17 -0
  149. omniload/src/plusvibeai/__init__.py +335 -0
  150. omniload/src/plusvibeai/helpers.py +544 -0
  151. omniload/src/plusvibeai/settings.py +252 -0
  152. omniload/src/primer/__init__.py +45 -0
  153. omniload/src/primer/helpers.py +79 -0
  154. omniload/src/quickbooks/__init__.py +117 -0
  155. omniload/src/reddit_ads/__init__.py +183 -0
  156. omniload/src/reddit_ads/helpers.py +232 -0
  157. omniload/src/resource.py +40 -0
  158. omniload/src/revenuecat/__init__.py +83 -0
  159. omniload/src/revenuecat/helpers.py +237 -0
  160. omniload/src/salesforce/__init__.py +170 -0
  161. omniload/src/salesforce/helpers.py +78 -0
  162. omniload/src/shopify/__init__.py +1953 -0
  163. omniload/src/shopify/exceptions.py +17 -0
  164. omniload/src/shopify/helpers.py +202 -0
  165. omniload/src/shopify/settings.py +19 -0
  166. omniload/src/slack/__init__.py +290 -0
  167. omniload/src/slack/helpers.py +218 -0
  168. omniload/src/slack/settings.py +36 -0
  169. omniload/src/smartsheets/__init__.py +82 -0
  170. omniload/src/snapchat_ads/__init__.py +455 -0
  171. omniload/src/snapchat_ads/client.py +72 -0
  172. omniload/src/snapchat_ads/helpers.py +630 -0
  173. omniload/src/snapchat_ads/settings.py +130 -0
  174. omniload/src/socrata_source/__init__.py +83 -0
  175. omniload/src/socrata_source/helpers.py +85 -0
  176. omniload/src/socrata_source/settings.py +8 -0
  177. omniload/src/solidgate/__init__.py +219 -0
  178. omniload/src/solidgate/helpers.py +154 -0
  179. omniload/src/sources.py +5408 -0
  180. omniload/src/sql_database/__init__.py +0 -0
  181. omniload/src/sql_database/callbacks.py +66 -0
  182. omniload/src/stripe_analytics/__init__.py +183 -0
  183. omniload/src/stripe_analytics/helpers.py +386 -0
  184. omniload/src/stripe_analytics/settings.py +80 -0
  185. omniload/src/table_definition.py +15 -0
  186. omniload/src/testdata/fakebqcredentials.json +14 -0
  187. omniload/src/tiktok_ads/__init__.py +150 -0
  188. omniload/src/tiktok_ads/tiktok_helpers.py +130 -0
  189. omniload/src/time.py +11 -0
  190. omniload/src/trustpilot/__init__.py +48 -0
  191. omniload/src/trustpilot/client.py +48 -0
  192. omniload/src/version.py +6 -0
  193. omniload/src/wise/__init__.py +68 -0
  194. omniload/src/wise/client.py +63 -0
  195. omniload/src/zendesk/__init__.py +480 -0
  196. omniload/src/zendesk/helpers/__init__.py +39 -0
  197. omniload/src/zendesk/helpers/api_helpers.py +119 -0
  198. omniload/src/zendesk/helpers/credentials.py +68 -0
  199. omniload/src/zendesk/helpers/talk_api.py +132 -0
  200. omniload/src/zendesk/settings.py +71 -0
  201. omniload/src/zoom/__init__.py +99 -0
  202. omniload/src/zoom/helpers.py +102 -0
  203. omniload/testdata/.gitignore +2 -0
  204. omniload/testdata/create_replace.csv +21 -0
  205. omniload/testdata/delete_insert_expected.csv +6 -0
  206. omniload/testdata/delete_insert_part1.csv +5 -0
  207. omniload/testdata/delete_insert_part2.csv +6 -0
  208. omniload/testdata/merge_expected.csv +5 -0
  209. omniload/testdata/merge_part1.csv +4 -0
  210. omniload/testdata/merge_part2.csv +5 -0
  211. omniload/tests/unit/test_smartsheets.py +133 -0
  212. omniload-0.0.0.dev0.dist-info/METADATA +439 -0
  213. omniload-0.0.0.dev0.dist-info/RECORD +218 -0
  214. omniload-0.0.0.dev0.dist-info/WHEEL +4 -0
  215. omniload-0.0.0.dev0.dist-info/entry_points.txt +2 -0
  216. omniload-0.0.0.dev0.dist-info/licenses/LICENSE.Apache-2.0 +201 -0
  217. omniload-0.0.0.dev0.dist-info/licenses/LICENSE.md +21 -0
  218. omniload-0.0.0.dev0.dist-info/licenses/NOTICE +35 -0
@@ -0,0 +1,377 @@
1
+ # Copyright 2022-2025 ScaleVector
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ This source provides data extraction from Jira Cloud via the REST API v3.
17
+
18
+ It defines several functions to fetch data from different parts of Jira including
19
+ projects, issues, users, boards, sprints, and various configuration objects like
20
+ issue types, statuses, and priorities.
21
+ """
22
+
23
+ from typing import Any, Iterable, Optional
24
+
25
+ import dlt
26
+ from dlt.common.typing import TDataItem
27
+
28
+ from .helpers import get_client
29
+ from .settings import (
30
+ DEFAULT_PAGE_SIZE,
31
+ DEFAULT_START_DATE,
32
+ ISSUE_FIELDS,
33
+ )
34
+
35
+
36
+ @dlt.source
37
+ def jira_source() -> Any:
38
+ """
39
+ The main function that runs all the other functions to fetch data from Jira.
40
+
41
+ Returns:
42
+ Sequence[DltResource]: A sequence of DltResource objects containing the fetched data.
43
+ """
44
+ return [
45
+ projects,
46
+ issues,
47
+ users,
48
+ issue_types,
49
+ statuses,
50
+ priorities,
51
+ resolutions,
52
+ project_versions,
53
+ project_components,
54
+ events,
55
+ issue_changelogs,
56
+ ]
57
+
58
+
59
+ @dlt.resource(write_disposition="replace")
60
+ def projects(
61
+ base_url: str = dlt.secrets.value,
62
+ email: str = dlt.secrets.value,
63
+ api_token: str = dlt.secrets.value,
64
+ expand: Optional[str] = None,
65
+ recent: Optional[int] = None,
66
+ ) -> Iterable[TDataItem]:
67
+ """
68
+ Fetches and returns a list of projects from Jira.
69
+
70
+ Args:
71
+ base_url (str): Jira instance URL (e.g., https://your-domain.atlassian.net)
72
+ email (str): User email for authentication
73
+ api_token (str): API token for authentication
74
+ expand (str): Comma-separated list of fields to expand
75
+ recent (int): Number of recent projects to return
76
+
77
+ Yields:
78
+ dict: The project data.
79
+ """
80
+ client = get_client(base_url, email, api_token)
81
+ yield from client.get_projects(expand=expand, recent=recent)
82
+
83
+
84
+ @dlt.resource(
85
+ write_disposition="merge",
86
+ primary_key="id",
87
+ max_table_nesting=2,
88
+ )
89
+ def issues(
90
+ base_url: str = dlt.secrets.value,
91
+ email: str = dlt.secrets.value,
92
+ api_token: str = dlt.secrets.value,
93
+ jql: str = "order by updated DESC",
94
+ fields: Optional[str] = None,
95
+ expand: Optional[str] = None,
96
+ max_results: Optional[int] = None,
97
+ updated: dlt.sources.incremental[str] = dlt.sources.incremental(
98
+ "fields.updated",
99
+ initial_value=DEFAULT_START_DATE,
100
+ range_end="closed",
101
+ range_start="closed",
102
+ ),
103
+ ) -> Iterable[TDataItem]:
104
+ """
105
+ Fetches issues from Jira using JQL search.
106
+
107
+ Args:
108
+ base_url (str): Jira instance URL
109
+ email (str): User email for authentication
110
+ api_token (str): API token for authentication
111
+ jql (str): JQL query string
112
+ fields (str): Comma-separated list of fields to return
113
+ expand (str): Comma-separated list of fields to expand
114
+ max_results (int): Maximum number of results to return
115
+ updated (str): The date from which to fetch updated issues
116
+
117
+ Yields:
118
+ dict: The issue data.
119
+ """
120
+ client = get_client(base_url, email, api_token)
121
+
122
+ # Build JQL with incremental filter
123
+ incremental_jql = jql
124
+ if updated.start_value:
125
+ date_filter = f"updated >= '{updated.start_value}'"
126
+
127
+ # Check if JQL has ORDER BY clause and handle it properly
128
+ jql_upper = jql.upper()
129
+ if "ORDER BY" in jql_upper:
130
+ # Split at ORDER BY and add filter before it
131
+ order_by_index = jql_upper.find("ORDER BY")
132
+ main_query = jql[:order_by_index].strip()
133
+ order_clause = jql[order_by_index:].strip()
134
+
135
+ if main_query and (
136
+ "WHERE" in main_query.upper()
137
+ or "AND" in main_query.upper()
138
+ or "OR" in main_query.upper()
139
+ ):
140
+ incremental_jql = f"({main_query}) AND {date_filter} {order_clause}"
141
+ else:
142
+ if main_query:
143
+ incremental_jql = f"{main_query} AND {date_filter} {order_clause}"
144
+ else:
145
+ incremental_jql = f"{date_filter} {order_clause}"
146
+ else:
147
+ # No ORDER BY clause, use original logic
148
+ if "WHERE" in jql_upper or "AND" in jql_upper or "OR" in jql_upper:
149
+ incremental_jql = f"({jql}) AND {date_filter}"
150
+ else:
151
+ incremental_jql = f"{jql} AND {date_filter}"
152
+
153
+ # Use default fields if not specified
154
+ if fields is None:
155
+ fields = ",".join(ISSUE_FIELDS)
156
+
157
+ yield from client.search_issues(
158
+ jql=incremental_jql, fields=fields, expand=expand, max_results=max_results
159
+ )
160
+
161
+
162
+ @dlt.resource(write_disposition="replace")
163
+ def users(
164
+ base_url: str = dlt.secrets.value,
165
+ email: str = dlt.secrets.value,
166
+ api_token: str = dlt.secrets.value,
167
+ username: Optional[str] = None,
168
+ account_id: Optional[str] = None,
169
+ max_results: int = DEFAULT_PAGE_SIZE,
170
+ ) -> Iterable[TDataItem]:
171
+ """
172
+ Fetches users from Jira.
173
+
174
+ Args:
175
+ base_url (str): Jira instance URL
176
+ email (str): User email for authentication
177
+ api_token (str): API token for authentication
178
+ username (str): Username to search for
179
+ account_id (str): Account ID to search for
180
+ max_results (int): Maximum results per page
181
+
182
+ Yields:
183
+ dict: The user data.
184
+ """
185
+ client = get_client(base_url, email, api_token)
186
+ yield from client.get_users(
187
+ username=username, account_id=account_id, max_results=max_results
188
+ )
189
+
190
+
191
+ @dlt.resource(write_disposition="replace")
192
+ def issue_types(
193
+ base_url: str = dlt.secrets.value,
194
+ email: str = dlt.secrets.value,
195
+ api_token: str = dlt.secrets.value,
196
+ ) -> Iterable[TDataItem]:
197
+ """
198
+ Fetches all issue types from Jira.
199
+
200
+ Args:
201
+ base_url (str): Jira instance URL
202
+ email (str): User email for authentication
203
+ api_token (str): API token for authentication
204
+
205
+ Yields:
206
+ dict: The issue type data.
207
+ """
208
+ client = get_client(base_url, email, api_token)
209
+ yield from client.get_issue_types()
210
+
211
+
212
+ @dlt.resource(write_disposition="replace")
213
+ def statuses(
214
+ base_url: str = dlt.secrets.value,
215
+ email: str = dlt.secrets.value,
216
+ api_token: str = dlt.secrets.value,
217
+ ) -> Iterable[TDataItem]:
218
+ """
219
+ Fetches all statuses from Jira.
220
+
221
+ Args:
222
+ base_url (str): Jira instance URL
223
+ email (str): User email for authentication
224
+ api_token (str): API token for authentication
225
+
226
+ Yields:
227
+ dict: The status data.
228
+ """
229
+ client = get_client(base_url, email, api_token)
230
+ yield from client.get_statuses()
231
+
232
+
233
+ @dlt.resource(write_disposition="replace")
234
+ def priorities(
235
+ base_url: str = dlt.secrets.value,
236
+ email: str = dlt.secrets.value,
237
+ api_token: str = dlt.secrets.value,
238
+ ) -> Iterable[TDataItem]:
239
+ """
240
+ Fetches all priorities from Jira.
241
+
242
+ Args:
243
+ base_url (str): Jira instance URL
244
+ email (str): User email for authentication
245
+ api_token (str): API token for authentication
246
+
247
+ Yields:
248
+ dict: The priority data.
249
+ """
250
+ client = get_client(base_url, email, api_token)
251
+ yield from client.get_priorities()
252
+
253
+
254
+ @dlt.resource(write_disposition="replace")
255
+ def resolutions(
256
+ base_url: str = dlt.secrets.value,
257
+ email: str = dlt.secrets.value,
258
+ api_token: str = dlt.secrets.value,
259
+ ) -> Iterable[TDataItem]:
260
+ """
261
+ Fetches all resolutions from Jira.
262
+
263
+ Args:
264
+ base_url (str): Jira instance URL
265
+ email (str): User email for authentication
266
+ api_token (str): API token for authentication
267
+
268
+ Yields:
269
+ dict: The resolution data.
270
+ """
271
+ client = get_client(base_url, email, api_token)
272
+ yield from client.get_resolutions()
273
+
274
+
275
+ @dlt.transformer(
276
+ data_from=projects,
277
+ write_disposition="replace",
278
+ )
279
+ @dlt.defer
280
+ def project_versions(
281
+ project: TDataItem,
282
+ base_url: str = dlt.secrets.value,
283
+ email: str = dlt.secrets.value,
284
+ api_token: str = dlt.secrets.value,
285
+ ) -> Iterable[TDataItem]:
286
+ """
287
+ Fetches versions for each project from Jira.
288
+
289
+ Args:
290
+ project (dict): The project data.
291
+ base_url (str): Jira instance URL
292
+ email (str): User email for authentication
293
+ api_token (str): API token for authentication
294
+
295
+ Returns:
296
+ list[dict]: The version data for the given project.
297
+ """
298
+ client = get_client(base_url, email, api_token)
299
+ project_key = project.get("key")
300
+ if not project_key:
301
+ return []
302
+
303
+ return list(client.get_project_versions(project_key))
304
+
305
+
306
+ @dlt.transformer(
307
+ data_from=projects,
308
+ write_disposition="replace",
309
+ )
310
+ @dlt.defer
311
+ def project_components(
312
+ project: TDataItem,
313
+ base_url: str = dlt.secrets.value,
314
+ email: str = dlt.secrets.value,
315
+ api_token: str = dlt.secrets.value,
316
+ ) -> Iterable[TDataItem]:
317
+ """
318
+ Fetches components for each project from Jira.
319
+
320
+ Args:
321
+ project (dict): The project data.
322
+ base_url (str): Jira instance URL
323
+ email (str): User email for authentication
324
+ api_token (str): API token for authentication
325
+
326
+ Returns:
327
+ list[dict]: The component data for the given project.
328
+ """
329
+ client = get_client(base_url, email, api_token)
330
+ project_key = project.get("key")
331
+ if not project_key:
332
+ return []
333
+
334
+ return list(client.get_project_components(project_key))
335
+
336
+
337
+ @dlt.resource(write_disposition="replace")
338
+ def events(
339
+ base_url: str = dlt.secrets.value,
340
+ email: str = dlt.secrets.value,
341
+ api_token: str = dlt.secrets.value,
342
+ ) -> Iterable[TDataItem]:
343
+ """
344
+ Fetches all event types from Jira (e.g., Issue Created, Issue Updated, etc.).
345
+
346
+ Args:
347
+ base_url (str): Jira instance URL
348
+ email (str): User email for authentication
349
+ api_token (str): API token for authentication
350
+
351
+ Yields:
352
+ dict: The event data.
353
+ """
354
+ client = get_client(base_url, email, api_token)
355
+ yield from client.get_events()
356
+
357
+
358
+ @dlt.resource(write_disposition="replace", max_table_nesting=0)
359
+ def issue_changelogs(
360
+ base_url: str = dlt.secrets.value,
361
+ email: str = dlt.secrets.value,
362
+ api_token: str = dlt.secrets.value,
363
+ ) -> Iterable[TDataItem]:
364
+ client = get_client(base_url, email, api_token)
365
+
366
+ issue_keys = []
367
+ for project in client.get_projects():
368
+ project_key = project.get("key")
369
+ if project_key:
370
+ jql = f"project = {project_key} order by updated DESC"
371
+ for issue in client.search_issues(jql=jql, fields="key"):
372
+ issue_key = issue.get("key")
373
+ if issue_key:
374
+ issue_keys.append(issue_key)
375
+
376
+ if issue_keys:
377
+ yield from client.get_changelogs_bulk(issue_keys)