ingestr 0.14.93__tar.gz → 0.14.94__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.

Potentially problematic release.


This version of ingestr might be problematic. Click here for more details.

Files changed (375) hide show
  1. {ingestr-0.14.93 → ingestr-0.14.94}/PKG-INFO +2 -1
  2. {ingestr-0.14.93 → ingestr-0.14.94}/docs/.vitepress/config.mjs +1 -0
  3. ingestr-0.14.94/docs/supported-sources/couchbase.md +136 -0
  4. ingestr-0.14.94/ingestr/src/buildinfo.py +1 -0
  5. ingestr-0.14.94/ingestr/src/couchbase_source/__init__.py +118 -0
  6. ingestr-0.14.94/ingestr/src/couchbase_source/helpers.py +135 -0
  7. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/factory.py +2 -0
  8. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/revenuecat/__init__.py +16 -41
  9. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/revenuecat/helpers.py +19 -73
  10. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/sources.py +167 -0
  11. {ingestr-0.14.93 → ingestr-0.14.94}/requirements.in +1 -0
  12. {ingestr-0.14.93 → ingestr-0.14.94}/requirements.txt +2 -0
  13. {ingestr-0.14.93 → ingestr-0.14.94}/requirements_arm64.txt +2 -0
  14. ingestr-0.14.93/ingestr/src/buildinfo.py +0 -1
  15. {ingestr-0.14.93 → ingestr-0.14.94}/.dlt/config.toml +0 -0
  16. {ingestr-0.14.93 → ingestr-0.14.94}/.dockerignore +0 -0
  17. {ingestr-0.14.93 → ingestr-0.14.94}/.githooks/pre-commit-hook.sh +0 -0
  18. {ingestr-0.14.93 → ingestr-0.14.94}/.github/workflows/deploy-docs.yml +0 -0
  19. {ingestr-0.14.93 → ingestr-0.14.94}/.github/workflows/release.yml +0 -0
  20. {ingestr-0.14.93 → ingestr-0.14.94}/.github/workflows/secrets-scan.yml +0 -0
  21. {ingestr-0.14.93 → ingestr-0.14.94}/.github/workflows/tests.yml +0 -0
  22. {ingestr-0.14.93 → ingestr-0.14.94}/.gitignore +0 -0
  23. {ingestr-0.14.93 → ingestr-0.14.94}/.gitleaksignore +0 -0
  24. {ingestr-0.14.93 → ingestr-0.14.94}/.python-version +0 -0
  25. {ingestr-0.14.93 → ingestr-0.14.94}/.vale.ini +0 -0
  26. {ingestr-0.14.93 → ingestr-0.14.94}/Dockerfile +0 -0
  27. {ingestr-0.14.93 → ingestr-0.14.94}/LICENSE.md +0 -0
  28. {ingestr-0.14.93 → ingestr-0.14.94}/Makefile +0 -0
  29. {ingestr-0.14.93 → ingestr-0.14.94}/README.md +0 -0
  30. {ingestr-0.14.93 → ingestr-0.14.94}/docs/.vitepress/theme/custom.css +0 -0
  31. {ingestr-0.14.93 → ingestr-0.14.94}/docs/.vitepress/theme/index.js +0 -0
  32. {ingestr-0.14.93 → ingestr-0.14.94}/docs/commands/example-uris.md +0 -0
  33. {ingestr-0.14.93 → ingestr-0.14.94}/docs/commands/ingest.md +0 -0
  34. {ingestr-0.14.93 → ingestr-0.14.94}/docs/getting-started/core-concepts.md +0 -0
  35. {ingestr-0.14.93 → ingestr-0.14.94}/docs/getting-started/data-masking.md +0 -0
  36. {ingestr-0.14.93 → ingestr-0.14.94}/docs/getting-started/incremental-loading.md +0 -0
  37. {ingestr-0.14.93 → ingestr-0.14.94}/docs/getting-started/quickstart.md +0 -0
  38. {ingestr-0.14.93 → ingestr-0.14.94}/docs/getting-started/telemetry.md +0 -0
  39. {ingestr-0.14.93 → ingestr-0.14.94}/docs/index.md +0 -0
  40. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/applovin_max.png +0 -0
  41. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/athena.png +0 -0
  42. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/clickhouse_img.png +0 -0
  43. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/clickup_ingestion.png +0 -0
  44. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/cratedb-destination.png +0 -0
  45. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/cratedb-source.png +0 -0
  46. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/freshdesk_ingestion.png +0 -0
  47. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/gcp_spanner_ingestion.png +0 -0
  48. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/github.png +0 -0
  49. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/google_analytics_realtime_report.png +0 -0
  50. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/googleanalytics.png +0 -0
  51. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/ingestion_elasticsearch_img.png +0 -0
  52. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/kinesis.bigquery.png +0 -0
  53. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/linear.png +0 -0
  54. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/linkedin_ads.png +0 -0
  55. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/mixpanel_ingestion.png +0 -0
  56. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/personio.png +0 -0
  57. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/personio_duckdb.png +0 -0
  58. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/phantombuster.png +0 -0
  59. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/pipedrive.png +0 -0
  60. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/quickbook_ingestion.png +0 -0
  61. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/sftp.png +0 -0
  62. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/stripe_postgres.png +0 -0
  63. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/tiktok.png +0 -0
  64. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/wise_ingestion.png +0 -0
  65. {ingestr-0.14.93 → ingestr-0.14.94}/docs/media/zoom_ingestion.png +0 -0
  66. {ingestr-0.14.93 → ingestr-0.14.94}/docs/public/demo.gif +0 -0
  67. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/adjust.md +0 -0
  68. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/airtable.md +0 -0
  69. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/allium.md +0 -0
  70. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/anthropic.md +0 -0
  71. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/applovin.md +0 -0
  72. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/applovin_max.md +0 -0
  73. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/appsflyer.md +0 -0
  74. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/appstore.md +0 -0
  75. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/asana.md +0 -0
  76. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/athena.md +0 -0
  77. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/attio.md +0 -0
  78. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/bigquery.md +0 -0
  79. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/chess.md +0 -0
  80. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/clickhouse.md +0 -0
  81. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/clickup.md +0 -0
  82. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/cratedb.md +0 -0
  83. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/csv.md +0 -0
  84. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/custom_queries.md +0 -0
  85. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/databricks.md +0 -0
  86. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/db2.md +0 -0
  87. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/docebo.md +0 -0
  88. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/duckdb.md +0 -0
  89. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/dynamodb.md +0 -0
  90. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/elasticsearch.md +0 -0
  91. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/facebook-ads.md +0 -0
  92. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/fluxx.md +0 -0
  93. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/frankfurter.md +0 -0
  94. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/freshdesk.md +0 -0
  95. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/fundraiseup.md +0 -0
  96. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/gcs.md +0 -0
  97. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/github.md +0 -0
  98. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/google-ads.md +0 -0
  99. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/google_analytics.md +0 -0
  100. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/gorgias.md +0 -0
  101. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/gsheets.md +0 -0
  102. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/http.md +0 -0
  103. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/hubspot.md +0 -0
  104. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/influxdb.md +0 -0
  105. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/intercom.md +0 -0
  106. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/isoc-pulse.md +0 -0
  107. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/jira.md +0 -0
  108. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/kafka.md +0 -0
  109. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/kinesis.md +0 -0
  110. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/klaviyo.md +0 -0
  111. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/linear.md +0 -0
  112. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/linkedin_ads.md +0 -0
  113. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/mailchimp.md +0 -0
  114. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/mixpanel.md +0 -0
  115. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/monday.md +0 -0
  116. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/mongodb.md +0 -0
  117. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/motherduck.md +0 -0
  118. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/mssql.md +0 -0
  119. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/mysql.md +0 -0
  120. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/notion.md +0 -0
  121. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/oracle.md +0 -0
  122. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/personio.md +0 -0
  123. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/phantombuster.md +0 -0
  124. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/pinterest.md +0 -0
  125. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/pipedrive.md +0 -0
  126. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/plusvibeai.md +0 -0
  127. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/postgres.md +0 -0
  128. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/quickbooks.md +0 -0
  129. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/redshift.md +0 -0
  130. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/revenuecat.md +0 -0
  131. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/s3.md +0 -0
  132. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/salesforce.md +0 -0
  133. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/sap-hana.md +0 -0
  134. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/sftp.md +0 -0
  135. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/shopify.md +0 -0
  136. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/slack.md +0 -0
  137. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/smartsheets.md +0 -0
  138. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/snowflake.md +0 -0
  139. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/solidgate.md +0 -0
  140. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/spanner.md +0 -0
  141. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/sqlite.md +0 -0
  142. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/stripe.md +0 -0
  143. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/tiktok-ads.md +0 -0
  144. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/trino.md +0 -0
  145. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/trustpilot.md +0 -0
  146. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/wise.md +0 -0
  147. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/zendesk.md +0 -0
  148. {ingestr-0.14.93 → ingestr-0.14.94}/docs/supported-sources/zoom.md +0 -0
  149. {ingestr-0.14.93 → ingestr-0.14.94}/docs/tutorials/load-kinesis-bigquery.md +0 -0
  150. {ingestr-0.14.93 → ingestr-0.14.94}/docs/tutorials/load-personio-duckdb.md +0 -0
  151. {ingestr-0.14.93 → ingestr-0.14.94}/docs/tutorials/load-stripe-postgres.md +0 -0
  152. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/conftest.py +0 -0
  153. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/main.py +0 -0
  154. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/.gitignore +0 -0
  155. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/adjust/__init__.py +0 -0
  156. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/adjust/adjust_helpers.py +0 -0
  157. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/airtable/__init__.py +0 -0
  158. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/allium/__init__.py +0 -0
  159. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/anthropic/__init__.py +0 -0
  160. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/anthropic/helpers.py +0 -0
  161. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/applovin/__init__.py +0 -0
  162. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/applovin_max/__init__.py +0 -0
  163. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/appsflyer/__init__.py +0 -0
  164. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/appsflyer/client.py +0 -0
  165. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/appstore/__init__.py +0 -0
  166. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/appstore/client.py +0 -0
  167. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/appstore/errors.py +0 -0
  168. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/appstore/models.py +0 -0
  169. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/appstore/resources.py +0 -0
  170. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/arrow/__init__.py +0 -0
  171. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/asana_source/__init__.py +0 -0
  172. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/asana_source/helpers.py +0 -0
  173. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/asana_source/settings.py +0 -0
  174. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/attio/__init__.py +0 -0
  175. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/attio/helpers.py +0 -0
  176. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/blob.py +0 -0
  177. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/chess/__init__.py +0 -0
  178. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/chess/helpers.py +0 -0
  179. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/chess/settings.py +0 -0
  180. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/clickup/__init__.py +0 -0
  181. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/clickup/helpers.py +0 -0
  182. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/collector/spinner.py +0 -0
  183. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/destinations.py +0 -0
  184. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/docebo/__init__.py +0 -0
  185. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/docebo/client.py +0 -0
  186. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/docebo/helpers.py +0 -0
  187. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/dynamodb/__init__.py +0 -0
  188. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/elasticsearch/__init__.py +0 -0
  189. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/elasticsearch/helpers.py +0 -0
  190. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/errors.py +0 -0
  191. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/facebook_ads/__init__.py +0 -0
  192. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/facebook_ads/exceptions.py +0 -0
  193. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/facebook_ads/helpers.py +0 -0
  194. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/facebook_ads/settings.py +0 -0
  195. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/facebook_ads/utils.py +0 -0
  196. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/filesystem/__init__.py +0 -0
  197. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/filesystem/helpers.py +0 -0
  198. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/filesystem/readers.py +0 -0
  199. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/filters.py +0 -0
  200. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/fluxx/__init__.py +0 -0
  201. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/fluxx/helpers.py +0 -0
  202. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/frankfurter/__init__.py +0 -0
  203. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/frankfurter/helpers.py +0 -0
  204. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/freshdesk/__init__.py +0 -0
  205. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/freshdesk/freshdesk_client.py +0 -0
  206. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/freshdesk/settings.py +0 -0
  207. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/fundraiseup/__init__.py +0 -0
  208. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/fundraiseup/client.py +0 -0
  209. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/github/__init__.py +0 -0
  210. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/github/helpers.py +0 -0
  211. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/github/queries.py +0 -0
  212. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/github/settings.py +0 -0
  213. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/google_ads/__init__.py +0 -0
  214. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/google_ads/field.py +0 -0
  215. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/google_ads/metrics.py +0 -0
  216. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/google_ads/predicates.py +0 -0
  217. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/google_ads/reports.py +0 -0
  218. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/google_analytics/__init__.py +0 -0
  219. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/google_analytics/helpers.py +0 -0
  220. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/google_sheets/README.md +0 -0
  221. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/google_sheets/__init__.py +0 -0
  222. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/google_sheets/helpers/__init__.py +0 -0
  223. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/google_sheets/helpers/api_calls.py +0 -0
  224. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/google_sheets/helpers/data_processing.py +0 -0
  225. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/gorgias/__init__.py +0 -0
  226. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/gorgias/helpers.py +0 -0
  227. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/http/__init__.py +0 -0
  228. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/http/readers.py +0 -0
  229. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/http_client.py +0 -0
  230. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/hubspot/__init__.py +0 -0
  231. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/hubspot/helpers.py +0 -0
  232. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/hubspot/settings.py +0 -0
  233. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/influxdb/__init__.py +0 -0
  234. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/influxdb/client.py +0 -0
  235. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/intercom/__init__.py +0 -0
  236. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/intercom/helpers.py +0 -0
  237. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/intercom/settings.py +0 -0
  238. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/isoc_pulse/__init__.py +0 -0
  239. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/jira_source/__init__.py +0 -0
  240. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/jira_source/helpers.py +0 -0
  241. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/jira_source/settings.py +0 -0
  242. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/kafka/__init__.py +0 -0
  243. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/kafka/helpers.py +0 -0
  244. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/kinesis/__init__.py +0 -0
  245. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/kinesis/helpers.py +0 -0
  246. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/klaviyo/__init__.py +0 -0
  247. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/klaviyo/client.py +0 -0
  248. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/klaviyo/helpers.py +0 -0
  249. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/linear/__init__.py +0 -0
  250. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/linear/helpers.py +0 -0
  251. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/linkedin_ads/__init__.py +0 -0
  252. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/linkedin_ads/dimension_time_enum.py +0 -0
  253. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/linkedin_ads/helpers.py +0 -0
  254. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/loader.py +0 -0
  255. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/mailchimp/__init__.py +0 -0
  256. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/mailchimp/helpers.py +0 -0
  257. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/mailchimp/settings.py +0 -0
  258. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/masking.py +0 -0
  259. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/mixpanel/__init__.py +0 -0
  260. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/mixpanel/client.py +0 -0
  261. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/monday/__init__.py +0 -0
  262. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/monday/helpers.py +0 -0
  263. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/monday/settings.py +0 -0
  264. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/mongodb/__init__.py +0 -0
  265. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/mongodb/helpers.py +0 -0
  266. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/notion/__init__.py +0 -0
  267. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/notion/helpers/__init__.py +0 -0
  268. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/notion/helpers/client.py +0 -0
  269. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/notion/helpers/database.py +0 -0
  270. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/notion/settings.py +0 -0
  271. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/partition.py +0 -0
  272. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/personio/__init__.py +0 -0
  273. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/personio/helpers.py +0 -0
  274. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/phantombuster/__init__.py +0 -0
  275. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/phantombuster/client.py +0 -0
  276. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/pinterest/__init__.py +0 -0
  277. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/pipedrive/__init__.py +0 -0
  278. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/pipedrive/helpers/__init__.py +0 -0
  279. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/pipedrive/helpers/custom_fields_munger.py +0 -0
  280. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/pipedrive/helpers/pages.py +0 -0
  281. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/pipedrive/settings.py +0 -0
  282. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/pipedrive/typing.py +0 -0
  283. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/plusvibeai/__init__.py +0 -0
  284. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/plusvibeai/helpers.py +0 -0
  285. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/plusvibeai/settings.py +0 -0
  286. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/quickbooks/__init__.py +0 -0
  287. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/resource.py +0 -0
  288. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/salesforce/__init__.py +0 -0
  289. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/salesforce/helpers.py +0 -0
  290. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/shopify/__init__.py +0 -0
  291. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/shopify/exceptions.py +0 -0
  292. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/shopify/helpers.py +0 -0
  293. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/shopify/settings.py +0 -0
  294. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/slack/__init__.py +0 -0
  295. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/slack/helpers.py +0 -0
  296. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/slack/settings.py +0 -0
  297. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/smartsheets/__init__.py +0 -0
  298. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/solidgate/__init__.py +0 -0
  299. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/solidgate/helpers.py +0 -0
  300. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/sql_database/__init__.py +0 -0
  301. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/sql_database/callbacks.py +0 -0
  302. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/stripe_analytics/__init__.py +0 -0
  303. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/stripe_analytics/helpers.py +0 -0
  304. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/stripe_analytics/settings.py +0 -0
  305. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/table_definition.py +0 -0
  306. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/telemetry/event.py +0 -0
  307. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/testdata/fakebqcredentials.json +0 -0
  308. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/tiktok_ads/__init__.py +0 -0
  309. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/tiktok_ads/tiktok_helpers.py +0 -0
  310. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/time.py +0 -0
  311. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/trustpilot/__init__.py +0 -0
  312. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/trustpilot/client.py +0 -0
  313. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/version.py +0 -0
  314. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/wise/__init__.py +0 -0
  315. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/wise/client.py +0 -0
  316. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/zendesk/__init__.py +0 -0
  317. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/zendesk/helpers/__init__.py +0 -0
  318. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/zendesk/helpers/api_helpers.py +0 -0
  319. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/zendesk/helpers/credentials.py +0 -0
  320. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/zendesk/helpers/talk_api.py +0 -0
  321. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/zendesk/settings.py +0 -0
  322. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/zoom/__init__.py +0 -0
  323. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/src/zoom/helpers.py +0 -0
  324. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/testdata/.gitignore +0 -0
  325. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/testdata/create_replace.csv +0 -0
  326. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/testdata/delete_insert_expected.csv +0 -0
  327. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/testdata/delete_insert_part1.csv +0 -0
  328. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/testdata/delete_insert_part2.csv +0 -0
  329. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/testdata/merge_expected.csv +0 -0
  330. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/testdata/merge_part1.csv +0 -0
  331. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/testdata/merge_part2.csv +0 -0
  332. {ingestr-0.14.93 → ingestr-0.14.94}/ingestr/tests/unit/test_smartsheets.py +0 -0
  333. {ingestr-0.14.93 → ingestr-0.14.94}/package-lock.json +0 -0
  334. {ingestr-0.14.93 → ingestr-0.14.94}/package.json +0 -0
  335. {ingestr-0.14.93 → ingestr-0.14.94}/pyproject.toml +0 -0
  336. {ingestr-0.14.93 → ingestr-0.14.94}/requirements-dev.txt +0 -0
  337. {ingestr-0.14.93 → ingestr-0.14.94}/resources/demo.gif +0 -0
  338. {ingestr-0.14.93 → ingestr-0.14.94}/resources/demo.tape +0 -0
  339. {ingestr-0.14.93 → ingestr-0.14.94}/resources/ingestr.svg +0 -0
  340. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/AMPM.yml +0 -0
  341. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Acronyms.yml +0 -0
  342. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Colons.yml +0 -0
  343. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Contractions.yml +0 -0
  344. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/DateFormat.yml +0 -0
  345. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Ellipses.yml +0 -0
  346. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/EmDash.yml +0 -0
  347. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Exclamation.yml +0 -0
  348. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/FirstPerson.yml +0 -0
  349. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Gender.yml +0 -0
  350. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/GenderBias.yml +0 -0
  351. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/HeadingPunctuation.yml +0 -0
  352. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Headings.yml +0 -0
  353. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Latin.yml +0 -0
  354. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/LyHyphens.yml +0 -0
  355. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/OptionalPlurals.yml +0 -0
  356. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Ordinal.yml +0 -0
  357. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/OxfordComma.yml +0 -0
  358. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Parens.yml +0 -0
  359. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Passive.yml +0 -0
  360. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Periods.yml +0 -0
  361. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Quotes.yml +0 -0
  362. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Ranges.yml +0 -0
  363. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Semicolons.yml +0 -0
  364. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Slang.yml +0 -0
  365. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Spacing.yml +0 -0
  366. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Spelling.yml +0 -0
  367. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Units.yml +0 -0
  368. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/We.yml +0 -0
  369. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/Will.yml +0 -0
  370. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/WordList.yml +0 -0
  371. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/meta.json +0 -0
  372. {ingestr-0.14.93 → ingestr-0.14.94}/styles/Google/vocab.txt +0 -0
  373. {ingestr-0.14.93 → ingestr-0.14.94}/styles/bruin/Ingestr.yml +0 -0
  374. {ingestr-0.14.93 → ingestr-0.14.94}/styles/config/vocabularies/bruin/accept.txt +0 -0
  375. {ingestr-0.14.93 → ingestr-0.14.94}/test.env.template +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ingestr
3
- Version: 0.14.93
3
+ Version: 0.14.94
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
@@ -39,6 +39,7 @@ Requires-Dist: clickhouse-connect==0.8.14
39
39
  Requires-Dist: clickhouse-driver==0.2.9
40
40
  Requires-Dist: clickhouse-sqlalchemy==0.2.7
41
41
  Requires-Dist: confluent-kafka==2.8.0
42
+ Requires-Dist: couchbase==4.3.6
42
43
  Requires-Dist: crate==2.0.0
43
44
  Requires-Dist: cryptography==44.0.2
44
45
  Requires-Dist: curlify==2.2.1
@@ -82,6 +82,7 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
82
82
  { text: "AWS Athena", link: "/supported-sources/athena.md" },
83
83
  { text: "AWS Redshift", link: "/supported-sources/redshift.md" },
84
84
  { text: "ClickHouse", link: "/supported-sources/clickhouse.md" },
85
+ { text: "Couchbase", link: "/supported-sources/couchbase.md" },
85
86
  { text: "CrateDB", link: "/supported-sources/cratedb.md" },
86
87
  { text: "Databricks", link: "/supported-sources/databricks.md" },
87
88
  { text: "DuckDB", link: "/supported-sources/duckdb.md" },
@@ -0,0 +1,136 @@
1
+ # Couchbase
2
+
3
+ [Couchbase](https://www.couchbase.com/) is a distributed NoSQL cloud database that delivers unmatched performance, scalability, and flexibility for building modern applications.
4
+
5
+ ingestr supports Couchbase as a source.
6
+
7
+ ## URI format
8
+
9
+ ### Standard format (without SSL)
10
+ ```plaintext
11
+ couchbase://username:password@host
12
+ ```
13
+
14
+ ### With SSL/TLS enabled
15
+ ```plaintext
16
+ couchbase://username:password@host?ssl=true
17
+ ```
18
+
19
+ ### Including bucket in URI
20
+ ```plaintext
21
+ couchbase://username:password@host/bucket
22
+ couchbase://username:password@host/bucket?ssl=true
23
+ ```
24
+
25
+ URI parameters:
26
+ - `username`: the username to connect to the Couchbase cluster
27
+ - `password`: the password for the user
28
+ - `host`: the host address of the Couchbase server
29
+ - `bucket`: optional bucket name in the URI path
30
+ - `ssl`: SSL/TLS connection parameter
31
+ - `ssl=true`: Required for Couchbase Capella (cloud) deployments
32
+ - `ssl=false` or omitted: Use for Couchbase Server (self-hosted/on-premises) deployments
33
+
34
+ > [!NOTE]
35
+ > **SSL Parameter Usage:**
36
+ > - Use `ssl=true` when connecting to **Couchbase Capella (cloud)**
37
+ > - Use `ssl=false` or omit the parameter when connecting to **Couchbase Server (self-hosted/on-premises)**
38
+
39
+ The URI structure can be used for connecting to both local/self-hosted Couchbase instances and Couchbase Capella (cloud).
40
+
41
+ ## Source table format
42
+
43
+ The `--source-table` option for Couchbase supports two formats depending on whether the bucket is specified in the URI:
44
+
45
+ ### When bucket is NOT in URI
46
+ ```plaintext
47
+ bucket.scope.collection
48
+ ```
49
+
50
+ ### When bucket IS in URI path
51
+ ```plaintext
52
+ scope.collection
53
+ ```
54
+
55
+ For default scope and collection, you can use:
56
+ ```plaintext
57
+ bucket._default._default
58
+ ```
59
+
60
+ ## Using Couchbase as a source
61
+
62
+ ### Local/self-hosted Couchbase
63
+
64
+ #### Basic connection without SSL
65
+ ```bash
66
+ ingestr ingest \
67
+ --source-uri "couchbase://admin:password123@localhost" \
68
+ --source-table "mybucket.myscope.mycollection" \
69
+ --dest-uri "duckdb:///output.db" \
70
+ --dest-table "main.couchbase_data"
71
+ ```
72
+
73
+ #### For Couchbase Capella (Cloud)
74
+ ```bash
75
+ ingestr ingest \
76
+ --source-uri "couchbase://admin:password123@localhost?ssl=true" \
77
+ --source-table "mybucket._default._default" \
78
+ --dest-uri "duckdb:///output.db" \
79
+ --dest-table "main.couchbase_data"
80
+ ```
81
+
82
+ #### With bucket in URI
83
+ ```bash
84
+ ingestr ingest \
85
+ --source-uri "couchbase://admin:password123@localhost/mybucket" \
86
+ --source-table "myscope.mycollection" \
87
+ --dest-uri "duckdb:///output.db" \
88
+ --dest-table "main.couchbase_data"
89
+ ```
90
+
91
+ ### Couchbase Capella (Cloud)
92
+
93
+ > [!IMPORTANT]
94
+ > Couchbase Capella (cloud) **requires SSL connections**. You must use `?ssl=true` in your connection URI and prefix the host with `cb.`
95
+
96
+ > [!TIP]
97
+ > You can obtain the connection string for Capella from the SDK connection details in your Couchbase Capella dashboard.
98
+
99
+ Use the `couchbase://` scheme with `ssl=true` parameter. Note the `cb.` prefix in the hostname:
100
+
101
+ ```bash
102
+ ingestr ingest \
103
+ --source-uri "couchbase://username:password@cb.xxx.cloud.couchbase.com?ssl=true" \
104
+ --source-table "travel-sample.inventory.airport" \
105
+ --dest-uri "duckdb:///airports.db" \
106
+ --dest-table "main.airports"
107
+ ```
108
+
109
+ With bucket in URI for Couchbase Capella
110
+
111
+ ```bash
112
+ ingestr ingest \
113
+ --source-uri "couchbase://username:password@cb.xxx.cloud.couchbase.com/travel-sample?ssl=true" \
114
+ --source-table "inventory.airport" \
115
+ --dest-uri "duckdb:///airports.db" \
116
+ --dest-table "main.airports"
117
+ ```
118
+
119
+
120
+ ### With URL-encoded password
121
+
122
+ > [!IMPORTANT]
123
+ > When using ingestr CLI, passwords containing special characters (`@`, `:`, `/`, `#`, `?`, etc.) **must be URL-encoded** in the connection URI.
124
+
125
+ If your password contains special characters, you need to URL-encode them:
126
+
127
+ ```bash
128
+ ingestr ingest \
129
+ --source-uri "couchbase://admin:MyPass%40123%21@localhost" \
130
+ --source-table "mybucket.myscope.mycollection" \
131
+ --dest-uri "duckdb:///output.db" \
132
+ --dest-table "main.couchbase_data"
133
+ ```
134
+
135
+ This example encodes the password `MyPass@123!` as `MyPass%40123%21`.
136
+
@@ -0,0 +1 @@
1
+ version = "v0.14.94"
@@ -0,0 +1,118 @@
1
+ """Source that loads data from Couchbase buckets, supports incremental loads."""
2
+
3
+ from typing import Optional
4
+
5
+ import dlt
6
+ from dlt.sources import DltResource
7
+
8
+ from .helpers import (
9
+ CouchbaseConfiguration,
10
+ client_from_credentials,
11
+ fetch_documents,
12
+ )
13
+
14
+
15
+ @dlt.source(max_table_nesting=0)
16
+ def couchbase_source(
17
+ connection_string: str = dlt.secrets.value,
18
+ username: str = dlt.secrets.value,
19
+ password: str = dlt.secrets.value,
20
+ bucket: str = dlt.config.value,
21
+ scope: Optional[str] = dlt.config.value,
22
+ collection: Optional[str] = dlt.config.value,
23
+ incremental: Optional[dlt.sources.incremental] = None, # type: ignore[type-arg]
24
+ write_disposition: Optional[str] = dlt.config.value,
25
+ limit: Optional[int] = None,
26
+ ) -> DltResource:
27
+ """
28
+ A DLT source which loads data from a Couchbase bucket using Couchbase Python SDK.
29
+
30
+ Args:
31
+ connection_string (str): Couchbase connection string (e.g., 'couchbase://localhost')
32
+ username (str): Couchbase username
33
+ password (str): Couchbase password
34
+ bucket (str): Bucket name to load data from
35
+ scope (Optional[str]): Scope name (defaults to '_default')
36
+ collection (Optional[str]): Collection name (defaults to '_default')
37
+ incremental (Optional[dlt.sources.incremental]): Option to enable incremental loading.
38
+ E.g., `incremental=dlt.sources.incremental('updated_at', pendulum.parse('2022-01-01T00:00:00Z'))`
39
+ write_disposition (str): Write disposition of the resource.
40
+ limit (Optional[int]): The maximum number of documents to load.
41
+
42
+ Returns:
43
+ DltResource: A DLT resource for the Couchbase collection.
44
+ """
45
+ # Set up Couchbase client
46
+ cluster = client_from_credentials(connection_string, username, password)
47
+
48
+ resource_name = f"{bucket}_{scope}_{collection}"
49
+
50
+ return dlt.resource( # type: ignore[call-overload, arg-type]
51
+ fetch_documents,
52
+ name=resource_name,
53
+ primary_key="id",
54
+ write_disposition=write_disposition or "replace",
55
+ spec=CouchbaseConfiguration,
56
+ max_table_nesting=0,
57
+ )(
58
+ cluster=cluster,
59
+ bucket_name=bucket,
60
+ scope_name=scope,
61
+ collection_name=collection,
62
+ incremental=incremental,
63
+ limit=limit,
64
+ )
65
+
66
+
67
+ @dlt.resource(
68
+ name=lambda args: f"{args['bucket']}_{args['scope']}_{args['collection']}",
69
+ standalone=True,
70
+ spec=CouchbaseConfiguration, # type: ignore[arg-type]
71
+ )
72
+ def couchbase_collection(
73
+ connection_string: str = dlt.secrets.value,
74
+ username: str = dlt.secrets.value,
75
+ password: str = dlt.secrets.value,
76
+ bucket: str = dlt.config.value,
77
+ scope: Optional[str] = dlt.config.value,
78
+ collection: Optional[str] = dlt.config.value,
79
+ incremental: Optional[dlt.sources.incremental] = None, # type: ignore[type-arg]
80
+ write_disposition: Optional[str] = dlt.config.value,
81
+ limit: Optional[int] = None,
82
+ chunk_size: Optional[int] = 1000,
83
+ ) -> DltResource:
84
+ """
85
+ A DLT resource which loads a collection from Couchbase.
86
+
87
+ Args:
88
+ connection_string (str): Couchbase connection string (e.g., 'couchbase://localhost')
89
+ username (str): Couchbase username
90
+ password (str): Couchbase password
91
+ bucket (str): Bucket name to load data from
92
+ scope (Optional[str]): Scope name (defaults to '_default')
93
+ collection (Optional[str]): Collection name (defaults to '_default')
94
+ incremental (Optional[dlt.sources.incremental]): Option to enable incremental loading.
95
+ write_disposition (str): Write disposition of the resource.
96
+ limit (Optional[int]): The maximum number of documents to load.
97
+ chunk_size (Optional[int]): The number of documents to load in each batch.
98
+
99
+ Returns:
100
+ DltResource: A DLT resource for the Couchbase collection.
101
+ """
102
+ # Set up Couchbase client
103
+ cluster = client_from_credentials(connection_string, username, password)
104
+
105
+ return dlt.resource( # type: ignore[call-overload]
106
+ fetch_documents,
107
+ name=f"{bucket}_{scope}_{collection}",
108
+ primary_key="id",
109
+ write_disposition=write_disposition or "replace",
110
+ )(
111
+ cluster=cluster,
112
+ bucket_name=bucket,
113
+ scope_name=scope,
114
+ collection_name=collection,
115
+ incremental=incremental,
116
+ limit=limit,
117
+ chunk_size=chunk_size,
118
+ )
@@ -0,0 +1,135 @@
1
+ """Helper functions for Couchbase source."""
2
+
3
+ from datetime import datetime, timedelta
4
+ from typing import Any, Dict, Iterator, Optional
5
+
6
+ import dlt
7
+ from couchbase.auth import PasswordAuthenticator # type: ignore[import-untyped]
8
+ from couchbase.cluster import Cluster # type: ignore[import-untyped]
9
+ from couchbase.options import ( # type: ignore[import-untyped]
10
+ ClusterOptions,
11
+ QueryOptions,
12
+ )
13
+ from dlt.common.configuration import configspec
14
+ from dlt.common.time import ensure_pendulum_datetime
15
+
16
+
17
+ @configspec
18
+ class CouchbaseConfiguration:
19
+ """Configuration for Couchbase source."""
20
+
21
+ connection_string: str = dlt.secrets.value
22
+ username: str = dlt.secrets.value
23
+ password: str = dlt.secrets.value
24
+ bucket: str = dlt.config.value
25
+ scope: Optional[str] = dlt.config.value
26
+ collection: Optional[str] = dlt.config.value
27
+
28
+
29
+ def client_from_credentials(
30
+ connection_string: str, username: str, password: str
31
+ ) -> Cluster:
32
+ """
33
+ Create a Couchbase cluster client from credentials.
34
+
35
+ Args:
36
+ connection_string: Couchbase connection string
37
+ - Local/self-hosted: 'couchbase://localhost'
38
+ - Capella (cloud): 'couchbases://your-instance.cloud.couchbase.com'
39
+ username: Couchbase username
40
+ password: Couchbase password
41
+
42
+ Returns:
43
+ Cluster: Connected Couchbase cluster instance
44
+ """
45
+ auth = PasswordAuthenticator(username, password)
46
+ options = ClusterOptions(auth)
47
+
48
+ # Apply wan_development profile for Capella (couchbases://) connections
49
+ # This helps avoid latency issues when accessing from different networks
50
+ if connection_string.startswith("couchbases://"):
51
+ options.apply_profile("wan_development")
52
+
53
+ cluster = Cluster(connection_string, options)
54
+ cluster.wait_until_ready(timedelta(seconds=30))
55
+
56
+ return cluster
57
+
58
+
59
+ def fetch_documents(
60
+ cluster: Cluster,
61
+ bucket_name: str,
62
+ scope_name: str,
63
+ collection_name: str,
64
+ incremental: Optional[dlt.sources.incremental] = None, # type: ignore[type-arg]
65
+ limit: Optional[int] = None,
66
+ chunk_size: Optional[int] = 1000,
67
+ ) -> Iterator[Dict[str, Any]]:
68
+ """
69
+ Fetch documents from a Couchbase collection using N1QL queries.
70
+
71
+ Args:
72
+ cluster: Couchbase cluster instance
73
+ bucket_name: Name of the bucket
74
+ scope_name: Name of the scope
75
+ collection_name: Name of the collection
76
+ incremental: Incremental loading configuration
77
+ limit: Maximum number of documents to fetch
78
+ chunk_size: Number of documents to fetch per batch
79
+
80
+ Yields:
81
+ Dict[str, Any]: Document data
82
+ """
83
+ # Build N1QL query with full path
84
+ full_collection_path = f"`{bucket_name}`.`{scope_name}`.`{collection_name}`"
85
+ n1ql_query = f"SELECT META().id as id, c.* FROM {full_collection_path} c"
86
+
87
+ # Add incremental filter if provided
88
+ if incremental and incremental.cursor_path:
89
+ where_clause = f" WHERE {incremental.cursor_path} >= $start_value"
90
+ if incremental.end_value is not None:
91
+ where_clause += f" AND {incremental.cursor_path} < $end_value"
92
+ n1ql_query += where_clause
93
+
94
+ # Add limit if provided
95
+ if limit:
96
+ n1ql_query += f" LIMIT {limit}"
97
+
98
+ # Execute query
99
+ try:
100
+ query_options = QueryOptions()
101
+
102
+ # Add parameters if incremental
103
+ if incremental and incremental.cursor_path:
104
+ named_parameters = {"start_value": incremental.last_value}
105
+ if incremental.end_value is not None:
106
+ named_parameters["end_value"] = incremental.end_value
107
+ query_options = QueryOptions(named_parameters=named_parameters)
108
+
109
+ result = cluster.query(n1ql_query, query_options)
110
+
111
+ # Yield documents
112
+ count = 0
113
+ for row in result:
114
+ doc = dict(row)
115
+
116
+ # Convert datetime fields to proper format
117
+ if (
118
+ incremental
119
+ and incremental.cursor_path
120
+ and incremental.cursor_path in doc
121
+ ):
122
+ cursor_value = doc[incremental.cursor_path]
123
+ if isinstance(cursor_value, (str, datetime)):
124
+ doc[incremental.cursor_path] = ensure_pendulum_datetime(
125
+ cursor_value
126
+ )
127
+
128
+ yield doc
129
+
130
+ count += 1
131
+ if limit and count >= limit:
132
+ break
133
+
134
+ except Exception as e:
135
+ raise Exception(f"Error executing Couchbase N1QL query: {str(e)}")
@@ -39,6 +39,7 @@ from ingestr.src.sources import (
39
39
  AttioSource,
40
40
  ChessSource,
41
41
  ClickupSource,
42
+ CouchbaseSource,
42
43
  DoceboSource,
43
44
  DynamoDBSource,
44
45
  ElasticsearchSource,
@@ -160,6 +161,7 @@ class SourceDestinationFactory:
160
161
  "allium": AlliumSource,
161
162
  "anthropic": AnthropicSource,
162
163
  "csv": LocalCsvSource,
164
+ "couchbase": CouchbaseSource,
163
165
  "docebo": DoceboSource,
164
166
  "http": HttpSource,
165
167
  "https": HttpSource,
@@ -1,4 +1,3 @@
1
- import asyncio
2
1
  from typing import Any, Dict, Iterable, Iterator
3
2
 
4
3
  import aiohttp
@@ -40,51 +39,26 @@ def revenuecat_source(
40
39
  yield project
41
40
 
42
41
  @dlt.resource(
43
- name="customers", primary_key="id", write_disposition="merge", parallelized=True
42
+ name="customer_ids",
43
+ write_disposition="replace",
44
+ selected=False,
45
+ parallelized=True,
44
46
  )
45
- def customers() -> Iterator[Dict[str, Any]]:
46
- """Get list of customers with nested purchases and subscriptions."""
47
+ def customer_ids():
47
48
  if project_id is None:
48
49
  raise ValueError("project_id is required for customers resource")
49
- endpoint = f"/projects/{project_id}/customers"
50
50
 
51
- async def process_customer_batch(customer_batch):
52
- """Process a batch of customers with async operations."""
53
- async with aiohttp.ClientSession() as session:
54
- tasks = []
55
- for customer in customer_batch:
56
- task = process_customer_with_nested_resources_async(
57
- session, api_key, project_id, customer
58
- )
59
- tasks.append(task)
51
+ yield _paginate(api_key, f"/projects/{project_id}/customers")
60
52
 
61
- return await asyncio.gather(*tasks)
62
-
63
- def process_customers_sync():
64
- """Process customers in batches using asyncio."""
65
- batch_size = 50 # Conservative batch size due to 60 req/min rate limit
66
- current_batch = []
67
-
68
- for customer in _paginate(api_key, endpoint):
69
- current_batch.append(customer)
70
-
71
- if len(current_batch) >= batch_size:
72
- # Process the batch asynchronously
73
- processed_customers = asyncio.run(
74
- process_customer_batch(current_batch)
75
- )
76
- for processed_customer in processed_customers:
77
- yield processed_customer
78
- current_batch = []
79
-
80
- # Process any remaining customers in the final batch
81
- if current_batch:
82
- processed_customers = asyncio.run(process_customer_batch(current_batch))
83
- for processed_customer in processed_customers:
84
- yield processed_customer
85
-
86
- # Yield each processed customer
87
- yield from process_customers_sync()
53
+ @dlt.transformer(
54
+ data_from=customer_ids, write_disposition="replace", parallelized=True
55
+ )
56
+ async def customers(customers) -> Iterator[Dict[str, Any]]:
57
+ async with aiohttp.ClientSession() as session:
58
+ for customer in customers:
59
+ yield await process_customer_with_nested_resources_async(
60
+ session, api_key, project_id, customer
61
+ )
88
62
 
89
63
  # Create project-dependent resources dynamically
90
64
  project_resources = []
@@ -103,6 +77,7 @@ def revenuecat_source(
103
77
 
104
78
  return [
105
79
  projects,
80
+ customer_ids,
106
81
  customers,
107
82
  *project_resources,
108
83
  ]
@@ -64,12 +64,9 @@ def _paginate(
64
64
  while True:
65
65
  data = _make_request(api_key, endpoint, current_params)
66
66
 
67
- # Yield items from the current page
68
67
  if "items" in data and data["items"] is not None:
69
- for item in data["items"]:
70
- yield item
68
+ yield data["items"]
71
69
 
72
- # Check if there's a next page
73
70
  if "next_page" not in data:
74
71
  break
75
72
 
@@ -88,7 +85,6 @@ def convert_timestamps_to_iso(
88
85
  """Convert timestamp fields from milliseconds to ISO format."""
89
86
  for field in timestamp_fields:
90
87
  if field in record and record[field] is not None:
91
- # Convert from milliseconds timestamp to ISO datetime string
92
88
  timestamp_ms = record[field]
93
89
  dt = pendulum.from_timestamp(timestamp_ms / 1000)
94
90
  record[field] = dt.to_iso8601_string()
@@ -177,87 +173,37 @@ async def _paginate_async(
177
173
  return items
178
174
 
179
175
 
180
- async def fetch_and_process_nested_resource_async(
181
- session: aiohttp.ClientSession,
182
- api_key: str,
183
- project_id: str,
184
- customer_id: str,
185
- customer: Dict[str, Any],
186
- resource_name: str,
187
- timestamp_fields: Optional[List[str]] = None,
188
- ) -> None:
189
- """
190
- Fetch and process any nested resource for a customer asynchronously.
191
-
192
- Args:
193
- session: aiohttp ClientSession
194
- api_key: RevenueCat API key
195
- project_id: Project ID
196
- customer_id: Customer ID
197
- customer: Customer data dictionary to modify
198
- resource_name: Name of the nested resource (e.g., 'purchases', 'subscriptions', 'events')
199
- timestamp_fields: List of timestamp fields to convert to ISO format
200
- """
201
- # If resource not included in customer data, fetch separately
202
- if resource_name not in customer or customer[resource_name] is None:
203
- endpoint = f"/projects/{project_id}/customers/{customer_id}/{resource_name}"
204
- customer[resource_name] = await _paginate_async(session, api_key, endpoint)
205
-
206
- # Convert timestamps if fields specified
207
- if (
208
- timestamp_fields
209
- and resource_name in customer
210
- and customer[resource_name] is not None
211
- ):
212
- for item in customer[resource_name]:
213
- convert_timestamps_to_iso(item, timestamp_fields)
214
-
215
-
216
176
  async def process_customer_with_nested_resources_async(
217
177
  session: aiohttp.ClientSession,
218
178
  api_key: str,
219
179
  project_id: str,
220
180
  customer: Dict[str, Any],
221
181
  ) -> Dict[str, Any]:
222
- """
223
- Process a customer and fetch nested resources concurrently.
224
-
225
- Args:
226
- session: aiohttp ClientSession
227
- api_key: RevenueCat API key
228
- project_id: Project ID
229
- customer: Customer data to process
230
-
231
- Returns:
232
- Customer data with nested resources populated
233
- """
234
182
  customer_id = customer["id"]
235
-
236
- # Convert customer timestamps
237
183
  customer = convert_timestamps_to_iso(customer, ["first_seen_at", "last_seen_at"])
238
-
239
- # Define nested resources to fetch concurrently
240
184
  nested_resources = [
241
185
  ("subscriptions", ["purchased_at", "expires_at", "grace_period_expires_at"]),
242
186
  ("purchases", ["purchased_at", "expires_at"]),
243
187
  ]
244
188
 
245
- # Create concurrent tasks for fetching nested resources
246
- tasks = []
247
- for resource_name, timestamp_fields in nested_resources:
248
- task = fetch_and_process_nested_resource_async(
249
- session,
250
- api_key,
251
- project_id,
252
- customer_id,
253
- customer,
254
- resource_name,
255
- timestamp_fields,
256
- )
257
- tasks.append(task)
258
-
259
- # Wait for all nested resources to be fetched
260
- await asyncio.gather(*tasks)
189
+ async def fetch_and_convert(resource_name, timestamp_fields):
190
+ if resource_name not in customer or customer[resource_name] is None:
191
+ endpoint = f"/projects/{project_id}/customers/{customer_id}/{resource_name}"
192
+ customer[resource_name] = await _paginate_async(session, api_key, endpoint)
193
+ if (
194
+ timestamp_fields
195
+ and resource_name in customer
196
+ and customer[resource_name] is not None
197
+ ):
198
+ for item in customer[resource_name]:
199
+ convert_timestamps_to_iso(item, timestamp_fields)
200
+
201
+ await asyncio.gather(
202
+ *[
203
+ fetch_and_convert(resource_name, timestamp_fields)
204
+ for resource_name, timestamp_fields in nested_resources
205
+ ]
206
+ )
261
207
 
262
208
  return customer
263
209