ingestr 0.13.64__tar.gz → 0.13.66__tar.gz

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 (313) hide show
  1. {ingestr-0.13.64 → ingestr-0.13.66}/PKG-INFO +1 -1
  2. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/attio/__init__.py +10 -7
  3. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/attio/helpers.py +19 -8
  4. ingestr-0.13.66/ingestr/src/buildinfo.py +1 -0
  5. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/destinations.py +73 -63
  6. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/linear/__init__.py +46 -34
  7. ingestr-0.13.66/ingestr/src/linear/helpers.py +60 -0
  8. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/sources.py +4 -3
  9. ingestr-0.13.64/ingestr/src/buildinfo.py +0 -1
  10. {ingestr-0.13.64 → ingestr-0.13.66}/.dockerignore +0 -0
  11. {ingestr-0.13.64 → ingestr-0.13.66}/.githooks/pre-commit-hook.sh +0 -0
  12. {ingestr-0.13.64 → ingestr-0.13.66}/.github/workflows/deploy-docs.yml +0 -0
  13. {ingestr-0.13.64 → ingestr-0.13.66}/.github/workflows/release.yml +0 -0
  14. {ingestr-0.13.64 → ingestr-0.13.66}/.github/workflows/secrets-scan.yml +0 -0
  15. {ingestr-0.13.64 → ingestr-0.13.66}/.github/workflows/tests.yml +0 -0
  16. {ingestr-0.13.64 → ingestr-0.13.66}/.gitignore +0 -0
  17. {ingestr-0.13.64 → ingestr-0.13.66}/.gitleaksignore +0 -0
  18. {ingestr-0.13.64 → ingestr-0.13.66}/.python-version +0 -0
  19. {ingestr-0.13.64 → ingestr-0.13.66}/.vale.ini +0 -0
  20. {ingestr-0.13.64 → ingestr-0.13.66}/Dockerfile +0 -0
  21. {ingestr-0.13.64 → ingestr-0.13.66}/LICENSE.md +0 -0
  22. {ingestr-0.13.64 → ingestr-0.13.66}/Makefile +0 -0
  23. {ingestr-0.13.64 → ingestr-0.13.66}/README.md +0 -0
  24. {ingestr-0.13.64 → ingestr-0.13.66}/docs/.vitepress/config.mjs +0 -0
  25. {ingestr-0.13.64 → ingestr-0.13.66}/docs/.vitepress/theme/custom.css +0 -0
  26. {ingestr-0.13.64 → ingestr-0.13.66}/docs/.vitepress/theme/index.js +0 -0
  27. {ingestr-0.13.64 → ingestr-0.13.66}/docs/commands/example-uris.md +0 -0
  28. {ingestr-0.13.64 → ingestr-0.13.66}/docs/commands/ingest.md +0 -0
  29. {ingestr-0.13.64 → ingestr-0.13.66}/docs/getting-started/core-concepts.md +0 -0
  30. {ingestr-0.13.64 → ingestr-0.13.66}/docs/getting-started/incremental-loading.md +0 -0
  31. {ingestr-0.13.64 → ingestr-0.13.66}/docs/getting-started/quickstart.md +0 -0
  32. {ingestr-0.13.64 → ingestr-0.13.66}/docs/getting-started/telemetry.md +0 -0
  33. {ingestr-0.13.64 → ingestr-0.13.66}/docs/index.md +0 -0
  34. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/applovin_max.png +0 -0
  35. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/athena.png +0 -0
  36. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/clickhouse_img.png +0 -0
  37. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/cratedb-destination.png +0 -0
  38. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/cratedb-source.png +0 -0
  39. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/freshdesk_ingestion.png +0 -0
  40. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/gcp_spanner_ingestion.png +0 -0
  41. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/github.png +0 -0
  42. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/google_analytics_realtime_report.png +0 -0
  43. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/googleanalytics.png +0 -0
  44. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/ingestion_elasticsearch_img.png +0 -0
  45. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/kinesis.bigquery.png +0 -0
  46. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/linear.png +0 -0
  47. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/linkedin_ads.png +0 -0
  48. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/mixpanel_ingestion.png +0 -0
  49. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/personio.png +0 -0
  50. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/personio_duckdb.png +0 -0
  51. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/phantombuster.png +0 -0
  52. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/pipedrive.png +0 -0
  53. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/quickbook_ingestion.png +0 -0
  54. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/sftp.png +0 -0
  55. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/stripe_postgres.png +0 -0
  56. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/tiktok.png +0 -0
  57. {ingestr-0.13.64 → ingestr-0.13.66}/docs/media/zoom_ingestion.png +0 -0
  58. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/adjust.md +0 -0
  59. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/airtable.md +0 -0
  60. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/applovin.md +0 -0
  61. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/applovin_max.md +0 -0
  62. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/appsflyer.md +0 -0
  63. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/appstore.md +0 -0
  64. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/asana.md +0 -0
  65. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/athena.md +0 -0
  66. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/attio.md +0 -0
  67. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/bigquery.md +0 -0
  68. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/chess.md +0 -0
  69. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/clickhouse.md +0 -0
  70. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/cratedb.md +0 -0
  71. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/csv.md +0 -0
  72. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/custom_queries.md +0 -0
  73. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/databricks.md +0 -0
  74. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/db2.md +0 -0
  75. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/duckdb.md +0 -0
  76. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/dynamodb.md +0 -0
  77. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/elasticsearch.md +0 -0
  78. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/facebook-ads.md +0 -0
  79. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/frankfurter.md +0 -0
  80. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/freshdesk.md +0 -0
  81. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/gcs.md +0 -0
  82. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/github.md +0 -0
  83. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/google-ads.md +0 -0
  84. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/google_analytics.md +0 -0
  85. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/gorgias.md +0 -0
  86. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/gsheets.md +0 -0
  87. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/hubspot.md +0 -0
  88. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/isoc-pulse.md +0 -0
  89. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/kafka.md +0 -0
  90. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/kinesis.md +0 -0
  91. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/klaviyo.md +0 -0
  92. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/linear.md +0 -0
  93. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/linkedin_ads.md +0 -0
  94. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/mixpanel.md +0 -0
  95. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/mongodb.md +0 -0
  96. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/mssql.md +0 -0
  97. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/mysql.md +0 -0
  98. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/notion.md +0 -0
  99. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/oracle.md +0 -0
  100. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/personio.md +0 -0
  101. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/phantombuster.md +0 -0
  102. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/pinterest.md +0 -0
  103. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/pipedrive.md +0 -0
  104. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/postgres.md +0 -0
  105. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/quickbooks.md +0 -0
  106. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/redshift.md +0 -0
  107. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/s3.md +0 -0
  108. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/salesforce.md +0 -0
  109. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/sap-hana.md +0 -0
  110. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/sftp.md +0 -0
  111. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/shopify.md +0 -0
  112. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/slack.md +0 -0
  113. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/smartsheets.md +0 -0
  114. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/snowflake.md +0 -0
  115. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/solidgate.md +0 -0
  116. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/spanner.md +0 -0
  117. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/sqlite.md +0 -0
  118. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/stripe.md +0 -0
  119. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/tiktok-ads.md +0 -0
  120. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/trustpilot.md +0 -0
  121. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/zendesk.md +0 -0
  122. {ingestr-0.13.64 → ingestr-0.13.66}/docs/supported-sources/zoom.md +0 -0
  123. {ingestr-0.13.64 → ingestr-0.13.66}/docs/tutorials/load-kinesis-bigquery.md +0 -0
  124. {ingestr-0.13.64 → ingestr-0.13.66}/docs/tutorials/load-personio-duckdb.md +0 -0
  125. {ingestr-0.13.64 → ingestr-0.13.66}/docs/tutorials/load-stripe-postgres.md +0 -0
  126. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/conftest.py +0 -0
  127. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/main.py +0 -0
  128. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/.gitignore +0 -0
  129. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/adjust/__init__.py +0 -0
  130. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/adjust/adjust_helpers.py +0 -0
  131. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/airtable/__init__.py +0 -0
  132. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/applovin/__init__.py +0 -0
  133. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/applovin_max/__init__.py +0 -0
  134. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/appsflyer/__init__.py +0 -0
  135. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/appsflyer/client.py +0 -0
  136. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/appstore/__init__.py +0 -0
  137. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/appstore/client.py +0 -0
  138. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/appstore/errors.py +0 -0
  139. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/appstore/models.py +0 -0
  140. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/appstore/resources.py +0 -0
  141. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/arrow/__init__.py +0 -0
  142. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/asana_source/__init__.py +0 -0
  143. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/asana_source/helpers.py +0 -0
  144. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/asana_source/settings.py +0 -0
  145. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/blob.py +0 -0
  146. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/chess/__init__.py +0 -0
  147. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/chess/helpers.py +0 -0
  148. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/chess/settings.py +0 -0
  149. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/collector/spinner.py +0 -0
  150. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/dynamodb/__init__.py +0 -0
  151. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/elasticsearch/__init__.py +0 -0
  152. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/errors.py +0 -0
  153. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/facebook_ads/__init__.py +0 -0
  154. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/facebook_ads/exceptions.py +0 -0
  155. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/facebook_ads/helpers.py +0 -0
  156. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/facebook_ads/settings.py +0 -0
  157. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/facebook_ads/utils.py +0 -0
  158. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/factory.py +0 -0
  159. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/filesystem/__init__.py +0 -0
  160. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/filesystem/helpers.py +0 -0
  161. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/filesystem/readers.py +0 -0
  162. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/filters.py +0 -0
  163. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/frankfurter/__init__.py +0 -0
  164. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/frankfurter/helpers.py +0 -0
  165. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/freshdesk/__init__.py +0 -0
  166. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/freshdesk/freshdesk_client.py +0 -0
  167. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/freshdesk/settings.py +0 -0
  168. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/github/__init__.py +0 -0
  169. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/github/helpers.py +0 -0
  170. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/github/queries.py +0 -0
  171. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/github/settings.py +0 -0
  172. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/google_ads/__init__.py +0 -0
  173. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/google_ads/field.py +0 -0
  174. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/google_ads/metrics.py +0 -0
  175. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/google_ads/predicates.py +0 -0
  176. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/google_ads/reports.py +0 -0
  177. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/google_analytics/__init__.py +0 -0
  178. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/google_analytics/helpers.py +0 -0
  179. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/google_sheets/README.md +0 -0
  180. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/google_sheets/__init__.py +0 -0
  181. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/google_sheets/helpers/__init__.py +0 -0
  182. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/google_sheets/helpers/api_calls.py +0 -0
  183. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/google_sheets/helpers/data_processing.py +0 -0
  184. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/gorgias/__init__.py +0 -0
  185. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/gorgias/helpers.py +0 -0
  186. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/http_client.py +0 -0
  187. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/hubspot/__init__.py +0 -0
  188. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/hubspot/helpers.py +0 -0
  189. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/hubspot/settings.py +0 -0
  190. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/isoc_pulse/__init__.py +0 -0
  191. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/kafka/__init__.py +0 -0
  192. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/kafka/helpers.py +0 -0
  193. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/kinesis/__init__.py +0 -0
  194. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/kinesis/helpers.py +0 -0
  195. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/klaviyo/__init__.py +0 -0
  196. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/klaviyo/client.py +0 -0
  197. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/klaviyo/helpers.py +0 -0
  198. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/linkedin_ads/__init__.py +0 -0
  199. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/linkedin_ads/dimension_time_enum.py +0 -0
  200. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/linkedin_ads/helpers.py +0 -0
  201. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/loader.py +0 -0
  202. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/mixpanel/__init__.py +0 -0
  203. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/mixpanel/client.py +0 -0
  204. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/mongodb/__init__.py +0 -0
  205. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/mongodb/helpers.py +0 -0
  206. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/notion/__init__.py +0 -0
  207. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/notion/helpers/__init__.py +0 -0
  208. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/notion/helpers/client.py +0 -0
  209. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/notion/helpers/database.py +0 -0
  210. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/notion/settings.py +0 -0
  211. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/partition.py +0 -0
  212. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/personio/__init__.py +0 -0
  213. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/personio/helpers.py +0 -0
  214. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/phantombuster/__init__.py +0 -0
  215. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/phantombuster/client.py +0 -0
  216. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/pinterest/__init__.py +0 -0
  217. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/pipedrive/__init__.py +0 -0
  218. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/pipedrive/helpers/__init__.py +0 -0
  219. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/pipedrive/helpers/custom_fields_munger.py +0 -0
  220. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/pipedrive/helpers/pages.py +0 -0
  221. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/pipedrive/settings.py +0 -0
  222. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/pipedrive/typing.py +0 -0
  223. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/quickbooks/__init__.py +0 -0
  224. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/resource.py +0 -0
  225. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/salesforce/__init__.py +0 -0
  226. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/salesforce/helpers.py +0 -0
  227. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/shopify/__init__.py +0 -0
  228. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/shopify/exceptions.py +0 -0
  229. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/shopify/helpers.py +0 -0
  230. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/shopify/settings.py +0 -0
  231. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/slack/__init__.py +0 -0
  232. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/slack/helpers.py +0 -0
  233. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/slack/settings.py +0 -0
  234. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/smartsheets/__init__.py +0 -0
  235. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/solidgate/__init__.py +0 -0
  236. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/solidgate/helpers.py +0 -0
  237. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/sql_database/__init__.py +0 -0
  238. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/sql_database/callbacks.py +0 -0
  239. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/stripe_analytics/__init__.py +0 -0
  240. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/stripe_analytics/helpers.py +0 -0
  241. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/stripe_analytics/settings.py +0 -0
  242. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/table_definition.py +0 -0
  243. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/telemetry/event.py +0 -0
  244. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/testdata/fakebqcredentials.json +0 -0
  245. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/tiktok_ads/__init__.py +0 -0
  246. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/tiktok_ads/tiktok_helpers.py +0 -0
  247. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/time.py +0 -0
  248. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/trustpilot/__init__.py +0 -0
  249. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/trustpilot/client.py +0 -0
  250. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/version.py +0 -0
  251. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/zendesk/__init__.py +0 -0
  252. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/zendesk/helpers/__init__.py +0 -0
  253. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/zendesk/helpers/api_helpers.py +0 -0
  254. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/zendesk/helpers/credentials.py +0 -0
  255. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/zendesk/helpers/talk_api.py +0 -0
  256. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/zendesk/settings.py +0 -0
  257. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/zoom/__init__.py +0 -0
  258. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/src/zoom/helpers.py +0 -0
  259. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/testdata/.gitignore +0 -0
  260. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/testdata/create_replace.csv +0 -0
  261. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/testdata/delete_insert_expected.csv +0 -0
  262. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/testdata/delete_insert_part1.csv +0 -0
  263. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/testdata/delete_insert_part2.csv +0 -0
  264. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/testdata/merge_expected.csv +0 -0
  265. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/testdata/merge_part1.csv +0 -0
  266. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/testdata/merge_part2.csv +0 -0
  267. {ingestr-0.13.64 → ingestr-0.13.66}/ingestr/tests/unit/test_smartsheets.py +0 -0
  268. {ingestr-0.13.64 → ingestr-0.13.66}/package-lock.json +0 -0
  269. {ingestr-0.13.64 → ingestr-0.13.66}/package.json +0 -0
  270. {ingestr-0.13.64 → ingestr-0.13.66}/pyproject.toml +0 -0
  271. {ingestr-0.13.64 → ingestr-0.13.66}/requirements-dev.txt +0 -0
  272. {ingestr-0.13.64 → ingestr-0.13.66}/requirements.in +0 -0
  273. {ingestr-0.13.64 → ingestr-0.13.66}/requirements.txt +0 -0
  274. {ingestr-0.13.64 → ingestr-0.13.66}/requirements_arm64.txt +0 -0
  275. {ingestr-0.13.64 → ingestr-0.13.66}/resources/demo.gif +0 -0
  276. {ingestr-0.13.64 → ingestr-0.13.66}/resources/demo.tape +0 -0
  277. {ingestr-0.13.64 → ingestr-0.13.66}/resources/ingestr.svg +0 -0
  278. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/AMPM.yml +0 -0
  279. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Acronyms.yml +0 -0
  280. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Colons.yml +0 -0
  281. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Contractions.yml +0 -0
  282. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/DateFormat.yml +0 -0
  283. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Ellipses.yml +0 -0
  284. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/EmDash.yml +0 -0
  285. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Exclamation.yml +0 -0
  286. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/FirstPerson.yml +0 -0
  287. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Gender.yml +0 -0
  288. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/GenderBias.yml +0 -0
  289. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/HeadingPunctuation.yml +0 -0
  290. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Headings.yml +0 -0
  291. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Latin.yml +0 -0
  292. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/LyHyphens.yml +0 -0
  293. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/OptionalPlurals.yml +0 -0
  294. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Ordinal.yml +0 -0
  295. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/OxfordComma.yml +0 -0
  296. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Parens.yml +0 -0
  297. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Passive.yml +0 -0
  298. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Periods.yml +0 -0
  299. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Quotes.yml +0 -0
  300. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Ranges.yml +0 -0
  301. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Semicolons.yml +0 -0
  302. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Slang.yml +0 -0
  303. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Spacing.yml +0 -0
  304. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Spelling.yml +0 -0
  305. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Units.yml +0 -0
  306. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/We.yml +0 -0
  307. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/Will.yml +0 -0
  308. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/WordList.yml +0 -0
  309. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/meta.json +0 -0
  310. {ingestr-0.13.64 → ingestr-0.13.66}/styles/Google/vocab.txt +0 -0
  311. {ingestr-0.13.64 → ingestr-0.13.66}/styles/bruin/Ingestr.yml +0 -0
  312. {ingestr-0.13.64 → ingestr-0.13.66}/styles/config/vocabularies/bruin/accept.txt +0 -0
  313. {ingestr-0.13.64 → ingestr-0.13.66}/test.env.template +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ingestr
3
- Version: 0.13.64
3
+ Version: 0.13.66
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
@@ -20,13 +20,15 @@ def attio_source(
20
20
  "created_at": {"data_type": "timestamp", "partition": True},
21
21
  },
22
22
  )
23
+ # https://docs.attio.com/rest-api/endpoint-reference/objects/list-objects - does not support pagination
23
24
  def fetch_objects() -> Iterator[dict]:
24
25
  if len(params) != 0:
25
26
  raise ValueError("Objects table must be in the format `objects`")
26
27
 
27
28
  path = "objects"
28
- yield attio_client.fetch_data(path, "get")
29
+ yield attio_client.fetch_all(path, "get")
29
30
 
31
+ # https://docs.attio.com/rest-api/endpoint-reference/records/list-records
30
32
  @dlt.resource(
31
33
  name="records",
32
34
  write_disposition="replace",
@@ -39,12 +41,12 @@ def attio_source(
39
41
  raise ValueError(
40
42
  "Records table must be in the format `records:{object_api_slug}`"
41
43
  )
42
-
43
44
  object_id = params[0]
44
45
  path = f"objects/{object_id}/records/query"
45
46
 
46
- yield attio_client.fetch_data(path, "post")
47
+ yield attio_client.fetch_paginated(path, "post")
47
48
 
49
+ # https://docs.attio.com/rest-api/endpoint-reference/lists/list-all-lists -- does not support pagination
48
50
  @dlt.resource(
49
51
  name="lists",
50
52
  write_disposition="replace",
@@ -54,8 +56,9 @@ def attio_source(
54
56
  )
55
57
  def fetch_lists() -> Iterator[dict]:
56
58
  path = "lists"
57
- yield attio_client.fetch_data(path, "get")
59
+ yield attio_client.fetch_all(path, "get")
58
60
 
61
+ # https://docs.attio.com/rest-api/endpoint-reference/entries/list-entries
59
62
  @dlt.resource(
60
63
  name="list_entries",
61
64
  write_disposition="replace",
@@ -70,7 +73,7 @@ def attio_source(
70
73
  )
71
74
  path = f"lists/{params[0]}/entries/query"
72
75
 
73
- yield attio_client.fetch_data(path, "post")
76
+ yield attio_client.fetch_paginated(path, "post")
74
77
 
75
78
  @dlt.resource(
76
79
  name="all_list_entries",
@@ -85,10 +88,10 @@ def attio_source(
85
88
  "All list entries table must be in the format `all_list_entries:{object_api_slug}`"
86
89
  )
87
90
  path = "lists"
88
- for lst in attio_client.fetch_data(path, "get"):
91
+ for lst in attio_client.fetch_all(path, "get"):
89
92
  if params[0] in lst["parent_object"]:
90
93
  path = f"lists/{lst['id']['list_id']}/entries/query"
91
- yield from attio_client.fetch_data(path, "post")
94
+ yield from attio_client.fetch_paginated(path, "post")
92
95
 
93
96
  return (
94
97
  fetch_objects,
@@ -10,42 +10,53 @@ class AttioClient:
10
10
  }
11
11
  self.client = create_client()
12
12
 
13
- def fetch_data(self, path: str, method: str, limit: int = 1000, params=None):
13
+ def fetch_paginated(self, path: str, method: str, limit: int = 1000, params=None):
14
14
  url = f"{self.base_url}/{path}"
15
15
  if params is None:
16
16
  params = {}
17
17
  offset = 0
18
18
  while True:
19
- query_params = {**params, "limit": limit, "offset": offset}
19
+ query_params = {"limit": limit, "offset": offset, **params}
20
20
  if method == "get":
21
21
  response = self.client.get(
22
22
  url, headers=self.headers, params=query_params
23
23
  )
24
24
  else:
25
- response = self.client.post(
26
- url, headers=self.headers, params=query_params
27
- )
25
+ json_body = {**params, "limit": limit, "offset": offset}
26
+ response = self.client.post(url, headers=self.headers, json=json_body)
28
27
 
29
28
  if response.status_code != 200:
30
29
  raise Exception(f"HTTP {response.status_code} error: {response.text}")
31
30
 
32
31
  response_data = response.json()
33
32
  if "data" not in response_data:
34
- print(f"API Response: {response_data}")
35
33
  raise Exception(
36
34
  "Attio API returned a response without the expected data"
37
35
  )
38
36
 
39
37
  data = response_data["data"]
40
-
41
38
  for item in data:
42
39
  flat_item = flatten_item(item)
43
40
  yield flat_item
44
-
45
41
  if len(data) < limit:
46
42
  break
43
+
47
44
  offset += limit
48
45
 
46
+ def fetch_all(self, path: str, method: str = "get", params=None):
47
+ url = f"{self.base_url}/{path}"
48
+ params = params or {}
49
+
50
+ if method == "get":
51
+ response = self.client.get(url, headers=self.headers, params=params)
52
+ else:
53
+ response = self.client.post(url, headers=self.headers, json=params)
54
+
55
+ response.raise_for_status()
56
+ data = response.json().get("data", [])
57
+ for item in data:
58
+ yield flatten_item(item)
59
+
49
60
 
50
61
  def flatten_item(item: dict) -> dict:
51
62
  if "id" in item:
@@ -0,0 +1 @@
1
+ version = "v0.13.66"
@@ -18,14 +18,6 @@ from dlt.common.storages.configuration import FileSystemCredentials
18
18
  from dlt.destinations.impl.clickhouse.configuration import (
19
19
  ClickHouseCredentials,
20
20
  )
21
- from dlt.destinations.impl.mssql.configuration import MsSqlClientConfiguration
22
- from dlt.destinations.impl.mssql.mssql import (
23
- HINT_TO_MSSQL_ATTR,
24
- MsSqlJobClient,
25
- )
26
- from dlt.destinations.impl.mssql.sql_client import (
27
- PyOdbcMsSqlClient,
28
- )
29
21
 
30
22
  from ingestr.src.errors import MissingValueError
31
23
  from ingestr.src.loader import load_dlt_file
@@ -172,72 +164,90 @@ def handle_datetimeoffset(dto_value: bytes) -> datetime.datetime:
172
164
  )
173
165
 
174
166
 
175
- class OdbcMsSqlClient(PyOdbcMsSqlClient):
176
- SQL_COPT_SS_ACCESS_TOKEN = 1256
177
- SKIP_CREDENTIALS = {"PWD", "AUTHENTICATION", "UID"}
167
+ # MSSQL_COPT_SS_ACCESS_TOKEN is a connection attribute used to pass
168
+ # an Azure Active Directory access token to the SQL Server ODBC driver.
169
+ MSSQL_COPT_SS_ACCESS_TOKEN = 1256
178
170
 
179
- def open_connection(self):
180
- cfg = self.credentials._get_odbc_dsn_dict()
181
- if (
182
- cfg.get("AUTHENTICATION", "").strip().lower()
183
- != "activedirectoryaccesstoken"
184
- ):
185
- return super().open_connection()
186
171
 
187
- import pyodbc # type: ignore
172
+ def serialize_azure_token(token):
173
+ # https://github.com/mkleehammer/pyodbc/issues/228#issuecomment-494773723
174
+ encoded = token.encode("utf_16_le")
175
+ return struct.pack("<i", len(encoded)) + encoded
188
176
 
189
- dsn = ";".join(
190
- [f"{k}={v}" for k, v in cfg.items() if k not in self.SKIP_CREDENTIALS]
191
- )
192
177
 
193
- self._conn = pyodbc.connect(
194
- dsn,
195
- timeout=self.credentials.connect_timeout,
196
- attrs_before={
197
- self.SQL_COPT_SS_ACCESS_TOKEN: self.serialize_token(cfg["PWD"]),
198
- },
199
- )
178
+ def build_mssql_dest():
179
+ # https://github.com/bruin-data/ingestr/issues/293
200
180
 
201
- # https://github.com/mkleehammer/pyodbc/wiki/Using-an-Output-Converter-function
202
- self._conn.add_output_converter(-155, handle_datetimeoffset)
203
- self._conn.autocommit = True
204
- return self._conn
205
-
206
- def serialize_token(self, token):
207
- # https://github.com/mkleehammer/pyodbc/issues/228#issuecomment-494773723
208
- encoded = token.encode("utf_16_le")
209
- return struct.pack("<i", len(encoded)) + encoded
210
-
211
-
212
- class MsSqlClient(MsSqlJobClient):
213
- def __init__(
214
- self,
215
- schema: Schema,
216
- config: MsSqlClientConfiguration,
217
- capabilities: DestinationCapabilitiesContext,
218
- ) -> None:
219
- sql_client = OdbcMsSqlClient(
220
- config.normalize_dataset_name(schema),
221
- config.normalize_staging_dataset_name(schema),
222
- config.credentials,
223
- capabilities,
224
- )
225
- super(MsSqlJobClient, self).__init__(schema, config, sql_client)
226
- self.config: MsSqlClientConfiguration = config
227
- self.sql_client = sql_client
228
- self.active_hints = HINT_TO_MSSQL_ATTR if self.config.create_indexes else {}
229
- self.type_mapper = capabilities.get_type_mapper()
181
+ from dlt.destinations.impl.mssql.configuration import MsSqlClientConfiguration
182
+ from dlt.destinations.impl.mssql.mssql import (
183
+ HINT_TO_MSSQL_ATTR,
184
+ MsSqlJobClient,
185
+ )
186
+ from dlt.destinations.impl.mssql.sql_client import (
187
+ PyOdbcMsSqlClient,
188
+ )
230
189
 
190
+ class OdbcMsSqlClient(PyOdbcMsSqlClient):
191
+ SKIP_CREDENTIALS = {"PWD", "AUTHENTICATION", "UID"}
231
192
 
232
- class MsSqlDestImpl(dlt.destinations.mssql):
233
- @property
234
- def client_class(self):
235
- return MsSqlClient
193
+ def open_connection(self):
194
+ cfg = self.credentials._get_odbc_dsn_dict()
195
+ if (
196
+ cfg.get("AUTHENTICATION", "").strip().lower()
197
+ != "activedirectoryaccesstoken"
198
+ ):
199
+ return super().open_connection()
200
+
201
+ import pyodbc # type: ignore
202
+
203
+ dsn = ";".join(
204
+ [f"{k}={v}" for k, v in cfg.items() if k not in self.SKIP_CREDENTIALS]
205
+ )
206
+
207
+ self._conn = pyodbc.connect(
208
+ dsn,
209
+ timeout=self.credentials.connect_timeout,
210
+ attrs_before={
211
+ MSSQL_COPT_SS_ACCESS_TOKEN: serialize_azure_token(cfg["PWD"]),
212
+ },
213
+ )
214
+
215
+ # https://github.com/mkleehammer/pyodbc/wiki/Using-an-Output-Converter-function
216
+ self._conn.add_output_converter(-155, handle_datetimeoffset)
217
+ self._conn.autocommit = True
218
+ return self._conn
219
+
220
+ class MsSqlClient(MsSqlJobClient):
221
+ def __init__(
222
+ self,
223
+ schema: Schema,
224
+ config: MsSqlClientConfiguration,
225
+ capabilities: DestinationCapabilitiesContext,
226
+ ) -> None:
227
+ sql_client = OdbcMsSqlClient(
228
+ config.normalize_dataset_name(schema),
229
+ config.normalize_staging_dataset_name(schema),
230
+ config.credentials,
231
+ capabilities,
232
+ )
233
+ super(MsSqlJobClient, self).__init__(schema, config, sql_client)
234
+ self.config: MsSqlClientConfiguration = config
235
+ self.sql_client = sql_client
236
+ self.active_hints = HINT_TO_MSSQL_ATTR if self.config.create_indexes else {}
237
+ self.type_mapper = capabilities.get_type_mapper()
238
+
239
+ class MsSqlDestImpl(dlt.destinations.mssql):
240
+ @property
241
+ def client_class(self):
242
+ return MsSqlClient
243
+
244
+ return MsSqlDestImpl
236
245
 
237
246
 
238
247
  class MsSQLDestination(GenericSqlDestination):
239
248
  def dlt_dest(self, uri: str, **kwargs):
240
- return MsSqlDestImpl(credentials=uri, **kwargs)
249
+ cls = build_mssql_dest()
250
+ return cls(credentials=uri, **kwargs)
241
251
 
242
252
 
243
253
  class DatabricksDestination(GenericSqlDestination):
@@ -1,38 +1,9 @@
1
- from typing import Any, Dict, Iterable, Iterator, Optional
1
+ from typing import Any, Dict, Iterable, Iterator
2
2
 
3
3
  import dlt
4
4
  import pendulum
5
- import requests
6
-
7
- LINEAR_GRAPHQL_ENDPOINT = "https://api.linear.app/graphql"
8
-
9
-
10
- def _graphql(
11
- api_key: str, query: str, variables: Optional[Dict[str, Any]] = None
12
- ) -> Dict[str, Any]:
13
- headers = {"Authorization": api_key, "Content-Type": "application/json"}
14
- response = requests.post(
15
- LINEAR_GRAPHQL_ENDPOINT,
16
- json={"query": query, "variables": variables or {}},
17
- headers=headers,
18
- )
19
- response.raise_for_status()
20
- payload = response.json()
21
- if "errors" in payload:
22
- raise ValueError(str(payload["errors"]))
23
- return payload["data"]
24
-
25
-
26
- def _paginate(api_key: str, query: str, root: str) -> Iterator[Dict[str, Any]]:
27
- cursor: Optional[str] = None
28
- while True:
29
- data = _graphql(api_key, query, {"cursor": cursor})[root]
30
- for item in data["nodes"]:
31
- yield item
32
- if not data["pageInfo"]["hasNextPage"]:
33
- break
34
- cursor = data["pageInfo"]["endCursor"]
35
5
 
6
+ from .helpers import _paginate, _normalize_issue, _normalize_team
36
7
 
37
8
  ISSUES_QUERY = """
38
9
  query Issues($cursor: String) {
@@ -43,6 +14,17 @@ query Issues($cursor: String) {
43
14
  description
44
15
  createdAt
45
16
  updatedAt
17
+ creator { id }
18
+ assignee { id}
19
+ state { id}
20
+ labels { nodes { id } }
21
+ cycle { id}
22
+ project { id }
23
+ subtasks: children { nodes { id title } }
24
+ comments(first: 250) { nodes { id body } }
25
+ priority
26
+ attachments { nodes { id } }
27
+ subscribers { nodes { id } }
46
28
  }
47
29
  pageInfo { hasNextPage endCursor }
48
30
  }
@@ -58,6 +40,10 @@ query Projects($cursor: String) {
58
40
  description
59
41
  createdAt
60
42
  updatedAt
43
+ health
44
+ priority
45
+ targetDate
46
+ lead { id }
61
47
  }
62
48
  pageInfo { hasNextPage endCursor }
63
49
  }
@@ -72,6 +58,11 @@ query Teams($cursor: String) {
72
58
  name
73
59
  key
74
60
  description
61
+ updatedAt
62
+ createdAt
63
+ memberships { nodes { id } }
64
+ members { nodes { id } }
65
+ projects { nodes { id } }
75
66
  }
76
67
  pageInfo { hasNextPage endCursor }
77
68
  }
@@ -124,7 +115,7 @@ def linear_source(
124
115
  for item in _paginate(api_key, ISSUES_QUERY, "issues"):
125
116
  if pendulum.parse(item["updatedAt"]) >= current_start_date:
126
117
  if pendulum.parse(item["updatedAt"]) <= current_end_date:
127
- yield item
118
+ yield _normalize_issue(item)
128
119
 
129
120
  @dlt.resource(name="projects", primary_key="id", write_disposition="merge")
130
121
  def projects(
@@ -152,8 +143,29 @@ def linear_source(
152
143
  yield item
153
144
 
154
145
  @dlt.resource(name="teams", primary_key="id", write_disposition="merge")
155
- def teams() -> Iterator[Dict[str, Any]]:
156
- yield from _paginate(api_key, TEAMS_QUERY, "teams")
146
+ def teams( updated_at: dlt.sources.incremental[str] = dlt.sources.incremental(
147
+ "updatedAt",
148
+ initial_value=start_date.isoformat(),
149
+ end_value=end_date.isoformat() if end_date else None,
150
+ range_start="closed",
151
+ range_end="closed",
152
+ ),) -> Iterator[Dict[str, Any]]:
153
+ print(start_date)
154
+ if updated_at.last_value:
155
+ current_start_date = pendulum.parse(updated_at.last_value)
156
+ else:
157
+ current_start_date = pendulum.parse(start_date)
158
+ print(current_start_date)
159
+
160
+ if updated_at.end_value:
161
+ current_end_date = pendulum.parse(updated_at.end_value)
162
+ else:
163
+ current_end_date = pendulum.now(tz="UTC")
164
+
165
+ for item in _paginate(api_key, TEAMS_QUERY, "teams"):
166
+ if pendulum.parse(item["updatedAt"]) >= current_start_date:
167
+ if pendulum.parse(item["updatedAt"]) <= current_end_date:
168
+ yield _normalize_team(item)
157
169
 
158
170
  @dlt.resource(name="users", primary_key="id", write_disposition="merge")
159
171
  def users(
@@ -0,0 +1,60 @@
1
+ import json
2
+ from typing import Any, Dict, Iterator, Optional
3
+
4
+ import requests
5
+
6
+ LINEAR_GRAPHQL_ENDPOINT = "https://api.linear.app/graphql"
7
+
8
+ def _graphql(
9
+ api_key: str, query: str, variables: Optional[Dict[str, Any]] = None
10
+ ) -> Dict[str, Any]:
11
+ headers = {"Authorization": api_key, "Content-Type": "application/json"}
12
+ response = requests.post(
13
+ LINEAR_GRAPHQL_ENDPOINT,
14
+ json={"query": query, "variables": variables or {}},
15
+ headers=headers,
16
+ )
17
+ response.raise_for_status()
18
+ payload = response.json()
19
+ if "errors" in payload:
20
+ raise ValueError(str(payload["errors"]))
21
+ return payload["data"]
22
+
23
+ def _paginate(api_key: str, query: str, root: str) -> Iterator[Dict[str, Any]]:
24
+ cursor: Optional[str] = None
25
+ while True:
26
+ data = _graphql(api_key, query, {"cursor": cursor})[root]
27
+ for item in data["nodes"]:
28
+ yield item
29
+ if not data["pageInfo"]["hasNextPage"]:
30
+ break
31
+ cursor = data["pageInfo"]["endCursor"]
32
+
33
+ def _normalize_issue(item: Dict[str, Any]) -> Dict[str, Any]:
34
+ field_mapping = {
35
+ "assignee": "assignee_id",
36
+ "creator": "creator_id",
37
+ "state": "state_id",
38
+ "cycle": "cycle_id",
39
+ "project": "project_id",
40
+ }
41
+ for key, value in field_mapping.items():
42
+ if item.get(key):
43
+ item[value] = item[key]["id"]
44
+ del item[key]
45
+ else:
46
+ item[value] = None
47
+ del item[key]
48
+ json_fields = ["comments", "subscribers", "attachments", "labels", "subtasks","projects", "memberships", "members"]
49
+ for field in json_fields:
50
+ if item.get(field):
51
+ item[f"{field}"] = item[field].get("nodes", [])
52
+
53
+ return item
54
+
55
+ def _normalize_team(item: Dict[str, Any]) -> Dict[str, Any]:
56
+ json_fields = ["memberships", "members", "projects"]
57
+ for field in json_fields:
58
+ if item.get(field):
59
+ item[f"{field}"] = item[field].get("nodes", [])
60
+ return item
@@ -268,8 +268,9 @@ class SqlSource:
268
268
  from sqlalchemy import create_engine
269
269
 
270
270
  from ingestr.src.destinations import (
271
- OdbcMsSqlClient,
271
+ MSSQL_COPT_SS_ACCESS_TOKEN,
272
272
  handle_datetimeoffset,
273
+ serialize_azure_token,
273
274
  )
274
275
 
275
276
  cfg = {
@@ -283,7 +284,7 @@ class SqlSource:
283
284
  if k.lower() not in ["driver", "authentication", "connect_timeout"]:
284
285
  cfg[k.upper()] = v[0]
285
286
 
286
- token = OdbcMsSqlClient.serialize_token(None, parsed_uri.password) # type: ignore[arg-type]
287
+ token = serialize_azure_token(parsed_uri.password)
287
288
  dsn = ";".join([f"{k}={v}" for k, v in cfg.items()])
288
289
 
289
290
  def creator():
@@ -292,7 +293,7 @@ class SqlSource:
292
293
  autocommit=True,
293
294
  timeout=kwargs.get("connect_timeout", 30),
294
295
  attrs_before={
295
- OdbcMsSqlClient.SQL_COPT_SS_ACCESS_TOKEN: token,
296
+ MSSQL_COPT_SS_ACCESS_TOKEN: token,
296
297
  },
297
298
  )
298
299
  connection.add_output_converter(-155, handle_datetimeoffset)
@@ -1 +0,0 @@
1
- version = "v0.13.64"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes