ingestr 0.13.2__py3-none-any.whl → 0.14.104__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 (146) hide show
  1. ingestr/conftest.py +72 -0
  2. ingestr/main.py +134 -87
  3. ingestr/src/adjust/__init__.py +4 -4
  4. ingestr/src/adjust/adjust_helpers.py +7 -3
  5. ingestr/src/airtable/__init__.py +3 -2
  6. ingestr/src/allium/__init__.py +128 -0
  7. ingestr/src/anthropic/__init__.py +277 -0
  8. ingestr/src/anthropic/helpers.py +525 -0
  9. ingestr/src/applovin/__init__.py +262 -0
  10. ingestr/src/applovin_max/__init__.py +117 -0
  11. ingestr/src/appsflyer/__init__.py +325 -0
  12. ingestr/src/appsflyer/client.py +49 -45
  13. ingestr/src/appstore/__init__.py +1 -0
  14. ingestr/src/arrow/__init__.py +9 -1
  15. ingestr/src/asana_source/__init__.py +1 -1
  16. ingestr/src/attio/__init__.py +102 -0
  17. ingestr/src/attio/helpers.py +65 -0
  18. ingestr/src/blob.py +38 -11
  19. ingestr/src/buildinfo.py +1 -0
  20. ingestr/src/chess/__init__.py +1 -1
  21. ingestr/src/clickup/__init__.py +85 -0
  22. ingestr/src/clickup/helpers.py +47 -0
  23. ingestr/src/collector/spinner.py +43 -0
  24. ingestr/src/couchbase_source/__init__.py +118 -0
  25. ingestr/src/couchbase_source/helpers.py +135 -0
  26. ingestr/src/cursor/__init__.py +83 -0
  27. ingestr/src/cursor/helpers.py +188 -0
  28. ingestr/src/destinations.py +520 -33
  29. ingestr/src/docebo/__init__.py +589 -0
  30. ingestr/src/docebo/client.py +435 -0
  31. ingestr/src/docebo/helpers.py +97 -0
  32. ingestr/src/elasticsearch/__init__.py +80 -0
  33. ingestr/src/elasticsearch/helpers.py +138 -0
  34. ingestr/src/errors.py +8 -0
  35. ingestr/src/facebook_ads/__init__.py +47 -28
  36. ingestr/src/facebook_ads/helpers.py +59 -37
  37. ingestr/src/facebook_ads/settings.py +2 -0
  38. ingestr/src/facebook_ads/utils.py +39 -0
  39. ingestr/src/factory.py +116 -2
  40. ingestr/src/filesystem/__init__.py +8 -3
  41. ingestr/src/filters.py +46 -3
  42. ingestr/src/fluxx/__init__.py +9906 -0
  43. ingestr/src/fluxx/helpers.py +209 -0
  44. ingestr/src/frankfurter/__init__.py +157 -0
  45. ingestr/src/frankfurter/helpers.py +48 -0
  46. ingestr/src/freshdesk/__init__.py +89 -0
  47. ingestr/src/freshdesk/freshdesk_client.py +137 -0
  48. ingestr/src/freshdesk/settings.py +9 -0
  49. ingestr/src/fundraiseup/__init__.py +95 -0
  50. ingestr/src/fundraiseup/client.py +81 -0
  51. ingestr/src/github/__init__.py +41 -6
  52. ingestr/src/github/helpers.py +5 -5
  53. ingestr/src/google_analytics/__init__.py +22 -4
  54. ingestr/src/google_analytics/helpers.py +124 -6
  55. ingestr/src/google_sheets/__init__.py +4 -4
  56. ingestr/src/google_sheets/helpers/data_processing.py +2 -2
  57. ingestr/src/hostaway/__init__.py +302 -0
  58. ingestr/src/hostaway/client.py +288 -0
  59. ingestr/src/http/__init__.py +35 -0
  60. ingestr/src/http/readers.py +114 -0
  61. ingestr/src/http_client.py +24 -0
  62. ingestr/src/hubspot/__init__.py +66 -23
  63. ingestr/src/hubspot/helpers.py +52 -22
  64. ingestr/src/hubspot/settings.py +14 -7
  65. ingestr/src/influxdb/__init__.py +46 -0
  66. ingestr/src/influxdb/client.py +34 -0
  67. ingestr/src/intercom/__init__.py +142 -0
  68. ingestr/src/intercom/helpers.py +674 -0
  69. ingestr/src/intercom/settings.py +279 -0
  70. ingestr/src/isoc_pulse/__init__.py +159 -0
  71. ingestr/src/jira_source/__init__.py +340 -0
  72. ingestr/src/jira_source/helpers.py +439 -0
  73. ingestr/src/jira_source/settings.py +170 -0
  74. ingestr/src/kafka/__init__.py +4 -1
  75. ingestr/src/kinesis/__init__.py +139 -0
  76. ingestr/src/kinesis/helpers.py +82 -0
  77. ingestr/src/klaviyo/{_init_.py → __init__.py} +5 -6
  78. ingestr/src/linear/__init__.py +634 -0
  79. ingestr/src/linear/helpers.py +111 -0
  80. ingestr/src/linkedin_ads/helpers.py +0 -1
  81. ingestr/src/loader.py +69 -0
  82. ingestr/src/mailchimp/__init__.py +126 -0
  83. ingestr/src/mailchimp/helpers.py +226 -0
  84. ingestr/src/mailchimp/settings.py +164 -0
  85. ingestr/src/masking.py +344 -0
  86. ingestr/src/mixpanel/__init__.py +62 -0
  87. ingestr/src/mixpanel/client.py +99 -0
  88. ingestr/src/monday/__init__.py +246 -0
  89. ingestr/src/monday/helpers.py +392 -0
  90. ingestr/src/monday/settings.py +328 -0
  91. ingestr/src/mongodb/__init__.py +72 -8
  92. ingestr/src/mongodb/helpers.py +915 -38
  93. ingestr/src/partition.py +32 -0
  94. ingestr/src/personio/__init__.py +331 -0
  95. ingestr/src/personio/helpers.py +86 -0
  96. ingestr/src/phantombuster/__init__.py +65 -0
  97. ingestr/src/phantombuster/client.py +87 -0
  98. ingestr/src/pinterest/__init__.py +82 -0
  99. ingestr/src/pipedrive/__init__.py +198 -0
  100. ingestr/src/pipedrive/helpers/__init__.py +23 -0
  101. ingestr/src/pipedrive/helpers/custom_fields_munger.py +102 -0
  102. ingestr/src/pipedrive/helpers/pages.py +115 -0
  103. ingestr/src/pipedrive/settings.py +27 -0
  104. ingestr/src/pipedrive/typing.py +3 -0
  105. ingestr/src/plusvibeai/__init__.py +335 -0
  106. ingestr/src/plusvibeai/helpers.py +544 -0
  107. ingestr/src/plusvibeai/settings.py +252 -0
  108. ingestr/src/quickbooks/__init__.py +117 -0
  109. ingestr/src/resource.py +40 -0
  110. ingestr/src/revenuecat/__init__.py +83 -0
  111. ingestr/src/revenuecat/helpers.py +237 -0
  112. ingestr/src/salesforce/__init__.py +156 -0
  113. ingestr/src/salesforce/helpers.py +64 -0
  114. ingestr/src/shopify/__init__.py +1 -17
  115. ingestr/src/smartsheets/__init__.py +82 -0
  116. ingestr/src/snapchat_ads/__init__.py +489 -0
  117. ingestr/src/snapchat_ads/client.py +72 -0
  118. ingestr/src/snapchat_ads/helpers.py +535 -0
  119. ingestr/src/socrata_source/__init__.py +83 -0
  120. ingestr/src/socrata_source/helpers.py +85 -0
  121. ingestr/src/socrata_source/settings.py +8 -0
  122. ingestr/src/solidgate/__init__.py +219 -0
  123. ingestr/src/solidgate/helpers.py +154 -0
  124. ingestr/src/sources.py +3132 -212
  125. ingestr/src/stripe_analytics/__init__.py +49 -21
  126. ingestr/src/stripe_analytics/helpers.py +286 -1
  127. ingestr/src/stripe_analytics/settings.py +62 -10
  128. ingestr/src/telemetry/event.py +10 -9
  129. ingestr/src/tiktok_ads/__init__.py +12 -6
  130. ingestr/src/tiktok_ads/tiktok_helpers.py +0 -1
  131. ingestr/src/trustpilot/__init__.py +48 -0
  132. ingestr/src/trustpilot/client.py +48 -0
  133. ingestr/src/version.py +6 -1
  134. ingestr/src/wise/__init__.py +68 -0
  135. ingestr/src/wise/client.py +63 -0
  136. ingestr/src/zoom/__init__.py +99 -0
  137. ingestr/src/zoom/helpers.py +102 -0
  138. ingestr/tests/unit/test_smartsheets.py +133 -0
  139. ingestr-0.14.104.dist-info/METADATA +563 -0
  140. ingestr-0.14.104.dist-info/RECORD +203 -0
  141. ingestr/src/appsflyer/_init_.py +0 -24
  142. ingestr-0.13.2.dist-info/METADATA +0 -302
  143. ingestr-0.13.2.dist-info/RECORD +0 -107
  144. {ingestr-0.13.2.dist-info → ingestr-0.14.104.dist-info}/WHEEL +0 -0
  145. {ingestr-0.13.2.dist-info → ingestr-0.14.104.dist-info}/entry_points.txt +0 -0
  146. {ingestr-0.13.2.dist-info → ingestr-0.14.104.dist-info}/licenses/LICENSE.md +0 -0
@@ -0,0 +1,634 @@
1
+ from typing import Any, Dict, Iterable, Iterator
2
+
3
+ import dlt
4
+ import pendulum
5
+
6
+ from .helpers import (
7
+ _create_paginated_resource,
8
+ _get_date_range,
9
+ _graphql,
10
+ normalize_dictionaries,
11
+ )
12
+
13
+ ISSUES_QUERY = """
14
+ query Issues($cursor: String) {
15
+ issues(first: 50, after: $cursor) {
16
+ nodes {
17
+ id
18
+ title
19
+ description
20
+ createdAt
21
+ updatedAt
22
+ archivedAt
23
+ addedToCycleAt
24
+ autoArchivedAt
25
+ autoClosedAt
26
+ boardOrder
27
+ branchName
28
+ canceledAt
29
+ completedAt
30
+ customerTicketCount
31
+ descriptionState
32
+ dueDate
33
+ estimate
34
+ identifier
35
+ integrationSourceType
36
+ labelIds
37
+ number
38
+ previousIdentifiers
39
+ priority
40
+ priorityLabel
41
+ prioritySortOrder
42
+ reactionData
43
+ slaBreachesAt
44
+ slaHighRiskAt
45
+ slaMediumRiskAt
46
+ slaStartedAt
47
+ slaType
48
+ snoozedUntilAt
49
+ sortOrder
50
+ startedAt
51
+ startedTriageAt
52
+ subIssueSortOrder
53
+ triagedAt
54
+ url
55
+
56
+ creator { id }
57
+ assignee { id }
58
+ botActor { id name type }
59
+ cycle { id }
60
+ delegate { id }
61
+ externalUserCreator { id }
62
+ favorite { id }
63
+ lastAppliedTemplate { id }
64
+ parent { id }
65
+ projectMilestone { id }
66
+ recurringIssueTemplate { id }
67
+ snoozedBy { id }
68
+ sourceComment { id }
69
+ state { id }
70
+
71
+ labels(first: 250) {
72
+ nodes {
73
+ id
74
+ }
75
+ }
76
+ }
77
+ pageInfo { hasNextPage endCursor }
78
+ }
79
+ }
80
+ """
81
+
82
+ ATTACHMENTS_QUERY = """
83
+ query Attachments($cursor: String) {
84
+ attachments(first: 50, after: $cursor) {
85
+ nodes {
86
+ id
87
+ archivedAt
88
+ bodyData
89
+ createdAt
90
+ groupBySource
91
+ metadata
92
+ sourceType
93
+ subtitle
94
+ title
95
+ updatedAt
96
+ url
97
+
98
+ creator { id }
99
+ externalUserCreator { id }
100
+ issue { id }
101
+ originalIssue { id }
102
+ }
103
+ pageInfo { hasNextPage endCursor }
104
+ }
105
+ }
106
+ """
107
+
108
+ COMMENTS_QUERY = """
109
+ query Comments($cursor: String) {
110
+ comments(first: 50, after: $cursor) {
111
+ nodes {
112
+ id
113
+ archivedAt
114
+ body
115
+ bodyData
116
+ createdAt
117
+ editedAt
118
+ quotedText
119
+ reactionData
120
+ resolvedAt
121
+ threadSummary
122
+ updatedAt
123
+ url
124
+
125
+ botActor { id }
126
+ documentContent { id }
127
+ externalThread { id }
128
+ externalUser { id }
129
+ initiativeUpdate { id }
130
+ issue { id }
131
+ parent { id }
132
+ post { id }
133
+ projectUpdate { id }
134
+ resolvingComment { id }
135
+ resolvingUser { id }
136
+ user { id }
137
+ }
138
+ pageInfo { hasNextPage endCursor }
139
+ }
140
+ }
141
+ """
142
+
143
+ CYCLES_QUERY = """
144
+ query Cycles($cursor: String) {
145
+ cycles(first: 50, after: $cursor) {
146
+ nodes {
147
+ id
148
+ archivedAt
149
+ autoArchivedAt
150
+ completedAt
151
+ completedIssueCountHistory
152
+ completedScopeHistory
153
+ createdAt
154
+ description
155
+ endsAt
156
+ inProgressScopeHistory
157
+ issueCountHistory
158
+ name
159
+ number
160
+ progress
161
+ scopeHistory
162
+ startsAt
163
+ updatedAt
164
+ }
165
+ pageInfo { hasNextPage endCursor }
166
+ }
167
+ }
168
+ """
169
+
170
+ DOCUMENTS_QUERY = """
171
+ query Documents($cursor: String) {
172
+ documents(first: 50, after: $cursor) {
173
+ nodes {
174
+ id
175
+ archivedAt
176
+ color
177
+ createdAt
178
+ icon
179
+ slugId
180
+ title
181
+ updatedAt
182
+
183
+ creator { id }
184
+ updatedBy { id }
185
+ }
186
+ pageInfo { hasNextPage endCursor }
187
+ }
188
+ }
189
+ """
190
+
191
+ EXTERNAL_USERS_QUERY = """
192
+ query ExternalUsers($cursor: String) {
193
+ externalUsers(first: 50, after: $cursor) {
194
+ nodes {
195
+ id
196
+ archivedAt
197
+ avatarUrl
198
+ createdAt
199
+ displayName
200
+ email
201
+ lastSeen
202
+ name
203
+ updatedAt
204
+
205
+ organization { id }
206
+ }
207
+ pageInfo { hasNextPage endCursor }
208
+ }
209
+ }
210
+ """
211
+
212
+ INITIATIVES_QUERY = """
213
+ query Initiatives($cursor: String) {
214
+ initiatives(first: 50, after: $cursor) {
215
+ nodes {
216
+ id
217
+ archivedAt
218
+ color
219
+ completedAt
220
+ content
221
+ createdAt
222
+ description
223
+ frequencyResolution
224
+ health
225
+ healthUpdatedAt
226
+ icon
227
+ name
228
+ slugId
229
+ sortOrder
230
+ startedAt
231
+ status
232
+ targetDate
233
+ targetDateResolution
234
+ trashed
235
+ updateReminderFrequency
236
+ updateReminderFrequencyInWeeks
237
+ updateRemindersDay
238
+ updateRemindersHour
239
+ updatedAt
240
+
241
+ creator { id }
242
+ documentContent { id }
243
+ integrationsSettings { id }
244
+ lastUpdate { id }
245
+ organization { id }
246
+ owner { id }
247
+ parentInitiative { id }
248
+ }
249
+ pageInfo { hasNextPage endCursor }
250
+ }
251
+ }
252
+ """
253
+
254
+
255
+ INITIATIVE_TO_PROJECTS_QUERY = """
256
+ query InitiativeToProjects($cursor: String) {
257
+ initiativeToProjects(first: 50, after: $cursor) {
258
+ nodes {
259
+ id
260
+ archivedAt
261
+ createdAt
262
+ sortOrder
263
+ updatedAt
264
+
265
+ initiative { id }
266
+ }
267
+ pageInfo { hasNextPage endCursor }
268
+ }
269
+ }
270
+ """
271
+
272
+ PROJECT_MILESTONES_QUERY = """
273
+ query ProjectMilestones($cursor: String) {
274
+ projectMilestones(first: 50, after: $cursor) {
275
+ nodes {
276
+ id
277
+ archivedAt
278
+ createdAt
279
+ currentProgress
280
+ description
281
+ descriptionState
282
+ name
283
+ progress
284
+ progressHistory
285
+ sortOrder
286
+ status
287
+ targetDate
288
+ updatedAt
289
+
290
+ documentContent { id }
291
+ }
292
+ pageInfo { hasNextPage endCursor }
293
+ }
294
+ }
295
+ """
296
+
297
+ PROJECT_STATUSES_QUERY = """
298
+ query ProjectStatuses($cursor: String) {
299
+ projectStatuses(first: 50, after: $cursor) {
300
+ nodes {
301
+ id
302
+ archivedAt
303
+ color
304
+ createdAt
305
+ description
306
+ indefinite
307
+ name
308
+ position
309
+ type
310
+ updatedAt
311
+ }
312
+ pageInfo { hasNextPage endCursor }
313
+ }
314
+ }
315
+ """
316
+
317
+ INTEGRATIONS_QUERY = """
318
+ query Integrations($cursor: String) {
319
+ integrations(first: 50, after: $cursor) {
320
+ nodes {
321
+ id
322
+ archivedAt
323
+ createdAt
324
+ service
325
+ updatedAt
326
+
327
+ creator { id }
328
+ organization { id }
329
+ }
330
+ pageInfo { hasNextPage endCursor }
331
+ }
332
+ }
333
+ """
334
+
335
+
336
+ LABELS_QUERY = """
337
+ query IssueLabels($cursor: String) {
338
+ issueLabels(first: 50, after: $cursor) {
339
+ nodes {
340
+ id
341
+ archivedAt
342
+ color
343
+ createdAt
344
+ description
345
+ name
346
+ updatedAt
347
+
348
+ creator { id }
349
+ organization { id }
350
+ parent { id }
351
+ }
352
+ pageInfo { hasNextPage endCursor }
353
+ }
354
+ }
355
+ """
356
+
357
+
358
+ ORGANIZATION_QUERY = """
359
+ query Organization {
360
+ viewer {
361
+ organization {
362
+ id
363
+ name
364
+ createdAt
365
+ updatedAt
366
+ archivedAt
367
+ logoUrl
368
+ allowMembersToInvite
369
+ allowedAuthServices
370
+ createdIssueCount
371
+ customerCount
372
+ customersEnabled
373
+ deletionRequestedAt
374
+ gitBranchFormat
375
+ gitLinkbackMessagesEnabled
376
+ gitPublicLinkbackMessagesEnabled
377
+ logoUrl
378
+ periodUploadVolume
379
+ previousUrlKeys
380
+ roadmapEnabled
381
+ samlEnabled
382
+ scimEnabled
383
+ }
384
+ }
385
+ }
386
+ """
387
+
388
+
389
+ PROJECT_UPDATES_QUERY = """
390
+ query ProjectUpdates($cursor: String) {
391
+ projectUpdates(first: 50, after: $cursor) {
392
+ nodes {
393
+ id
394
+ archivedAt
395
+ body
396
+ bodyData
397
+ createdAt
398
+ diffMarkdown
399
+ health
400
+ updatedAt
401
+ url
402
+
403
+ user { id }
404
+ }
405
+ pageInfo { hasNextPage endCursor }
406
+ }
407
+ }
408
+ """
409
+
410
+
411
+ TEAM_MEMBERSHIPS_QUERY = """
412
+ query TeamMemberships($cursor: String) {
413
+ teamMemberships(first: 50, after: $cursor) {
414
+ nodes {
415
+ id
416
+ archivedAt
417
+ createdAt
418
+ owner
419
+ sortOrder
420
+ updatedAt
421
+
422
+ user { id }
423
+ }
424
+ pageInfo { hasNextPage endCursor }
425
+ }
426
+ }
427
+ """
428
+
429
+ USERS_QUERY = """
430
+ query Users($cursor: String) {
431
+ users(first: 50, after: $cursor) {
432
+ nodes {
433
+ id
434
+ active
435
+ admin
436
+ archivedAt
437
+ avatarUrl
438
+ calendarHash
439
+ createdAt
440
+ createdIssueCount
441
+ description
442
+ disableReason
443
+ displayName
444
+ email
445
+ guest
446
+ inviteHash
447
+ lastSeen
448
+ name
449
+ statusEmoji
450
+ statusLabel
451
+ statusUntilAt
452
+ timezone
453
+ updatedAt
454
+ url
455
+
456
+ organization { id }
457
+ }
458
+ pageInfo { hasNextPage endCursor }
459
+ }
460
+ }
461
+ """
462
+ WORKFLOW_STATES_QUERY = """
463
+ query WorkflowStates($cursor: String) {
464
+ workflowStates(first: 50, after: $cursor) {
465
+ nodes {
466
+ id
467
+ archivedAt
468
+ color
469
+ createdAt
470
+ description
471
+ name
472
+ position
473
+ type
474
+ updatedAt
475
+ }
476
+ pageInfo { hasNextPage endCursor }
477
+ }
478
+ }
479
+ """
480
+ PROJECTS_QUERY = """
481
+ query Projects($cursor: String) {
482
+ projects(first: 50, after: $cursor) {
483
+ nodes {
484
+ id
485
+ name
486
+ description
487
+ createdAt
488
+ updatedAt
489
+ archivedAt
490
+ completedAt
491
+ canceledAt
492
+ startedAt
493
+
494
+ color
495
+ icon
496
+ slugId
497
+ url
498
+
499
+ health
500
+ priority
501
+ priorityLabel
502
+ state
503
+
504
+ targetDate
505
+ startDate
506
+
507
+ progress
508
+ currentProgress
509
+ scope
510
+
511
+ sortOrder
512
+ trashed
513
+
514
+ creator { id }
515
+ lead { id }
516
+ }
517
+ pageInfo { hasNextPage endCursor }
518
+ }
519
+ }
520
+ """
521
+
522
+ TEAMS_QUERY = """
523
+ query Teams($cursor: String) {
524
+ teams(first: 50, after: $cursor) {
525
+ nodes {
526
+ id
527
+ name
528
+ key
529
+ description
530
+ color
531
+ icon
532
+ private
533
+ archivedAt
534
+ createdAt
535
+ updatedAt
536
+
537
+ organization { id }
538
+ parent { id }
539
+
540
+ cyclesEnabled
541
+ cycleDuration
542
+ cycleStartDay
543
+ cycleCooldownTime
544
+
545
+ issueCount
546
+ issueEstimationType
547
+ issueEstimationAllowZero
548
+ issueEstimationExtended
549
+ issueOrderingNoPriorityFirst
550
+
551
+ autoArchivePeriod
552
+ autoClosePeriod
553
+ autoCloseChildIssues
554
+ autoCloseParentIssues
555
+
556
+ groupIssueHistory
557
+ timezone
558
+ inviteHash
559
+ joinByDefault
560
+
561
+ slackNewIssue
562
+ slackIssueComments
563
+ slackIssueStatuses
564
+
565
+ triageEnabled
566
+ requirePriorityToLeaveTriage
567
+ upcomingCycleCount
568
+ }
569
+ pageInfo { hasNextPage endCursor }
570
+ }
571
+ }
572
+ """
573
+
574
+
575
+ # Paginated resources configuration
576
+ PAGINATED_RESOURCES = [
577
+ ("issues", ISSUES_QUERY, "issues"),
578
+ ("users", USERS_QUERY, "users"),
579
+ ("workflow_states", WORKFLOW_STATES_QUERY, "workflowStates"),
580
+ ("cycles", CYCLES_QUERY, "cycles"),
581
+ ("attachments", ATTACHMENTS_QUERY, "attachments"),
582
+ ("comments", COMMENTS_QUERY, "comments"),
583
+ ("documents", DOCUMENTS_QUERY, "documents"),
584
+ ("external_users", EXTERNAL_USERS_QUERY, "externalUsers"),
585
+ ("initiative", INITIATIVES_QUERY, "initiatives"),
586
+ ("integrations", INTEGRATIONS_QUERY, "integrations"),
587
+ ("labels", LABELS_QUERY, "issueLabels"),
588
+ ("project_updates", PROJECT_UPDATES_QUERY, "projectUpdates"),
589
+ ("team_memberships", TEAM_MEMBERSHIPS_QUERY, "teamMemberships"),
590
+ ("initiative_to_project", INITIATIVE_TO_PROJECTS_QUERY, "initiativeToProjects"),
591
+ ("project_milestone", PROJECT_MILESTONES_QUERY, "projectMilestones"),
592
+ ("project_status", PROJECT_STATUSES_QUERY, "projectStatuses"),
593
+ ("projects", PROJECTS_QUERY, "projects"),
594
+ ("teams", TEAMS_QUERY, "teams"),
595
+ ]
596
+
597
+
598
+ @dlt.source(name="linear", max_table_nesting=0)
599
+ def linear_source(
600
+ api_key: str,
601
+ start_date: pendulum.DateTime,
602
+ end_date: pendulum.DateTime | None = None,
603
+ ) -> Iterable[dlt.sources.DltResource]:
604
+ @dlt.resource(name="organization", primary_key="id", write_disposition="merge")
605
+ def organization(
606
+ updated_at: dlt.sources.incremental[str] = dlt.sources.incremental(
607
+ "updatedAt",
608
+ initial_value=start_date.isoformat(),
609
+ end_value=end_date.isoformat() if end_date else None,
610
+ range_start="closed",
611
+ range_end="closed",
612
+ ),
613
+ ) -> Iterator[Dict[str, Any]]:
614
+ current_start_date, current_end_date = _get_date_range(updated_at, start_date)
615
+
616
+ data = _graphql(api_key, ORGANIZATION_QUERY)
617
+ if "viewer" in data and "organization" in data["viewer"]:
618
+ item = data["viewer"]["organization"]
619
+ if item and pendulum.parse(item["updatedAt"]) >= current_start_date:
620
+ if pendulum.parse(item["updatedAt"]) <= current_end_date:
621
+ yield normalize_dictionaries(item)
622
+
623
+ # Create paginated resources dynamically
624
+ paginated_resources = [
625
+ _create_paginated_resource(
626
+ resource_name, query, query_field, api_key, start_date, end_date
627
+ )
628
+ for resource_name, query, query_field in PAGINATED_RESOURCES
629
+ ]
630
+
631
+ return [
632
+ *paginated_resources,
633
+ organization,
634
+ ]