ingestr 0.13.47__tar.gz → 0.13.49__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 (289) hide show
  1. {ingestr-0.13.47 → ingestr-0.13.49}/.gitleaksignore +1 -0
  2. {ingestr-0.13.47 → ingestr-0.13.49}/PKG-INFO +7 -1
  3. {ingestr-0.13.47 → ingestr-0.13.49}/README.md +5 -0
  4. {ingestr-0.13.47 → ingestr-0.13.49}/docs/.vitepress/config.mjs +2 -0
  5. ingestr-0.13.49/docs/supported-sources/frankfurter.md +48 -0
  6. ingestr-0.13.49/docs/supported-sources/s3.md +136 -0
  7. ingestr-0.13.49/docs/supported-sources/smartsheets.md +47 -0
  8. ingestr-0.13.49/docs/supported-sources/solidgate.md +46 -0
  9. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/main.py +8 -0
  10. ingestr-0.13.49/ingestr/src/buildinfo.py +1 -0
  11. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/factory.py +2 -0
  12. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/mongodb/__init__.py +2 -1
  13. ingestr-0.13.49/ingestr/src/resource.py +40 -0
  14. ingestr-0.13.49/ingestr/src/smartsheets/__init__.py +54 -0
  15. ingestr-0.13.49/ingestr/src/solidgate/__init__.py +97 -0
  16. ingestr-0.13.49/ingestr/src/solidgate/helpers.py +74 -0
  17. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/sources.py +113 -38
  18. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/stripe_analytics/__init__.py +2 -3
  19. ingestr-0.13.49/ingestr/src/stripe_analytics/settings.py +26 -0
  20. ingestr-0.13.49/ingestr/tests/unit/test_smartsheets.py +136 -0
  21. {ingestr-0.13.47 → ingestr-0.13.49}/requirements.in +1 -0
  22. {ingestr-0.13.47 → ingestr-0.13.49}/requirements.txt +9 -1
  23. {ingestr-0.13.47 → ingestr-0.13.49}/requirements_arm64.txt +9 -1
  24. ingestr-0.13.47/docs/supported-sources/frankfurter.md +0 -173
  25. ingestr-0.13.47/docs/supported-sources/s3.md +0 -46
  26. ingestr-0.13.47/ingestr/src/buildinfo.py +0 -1
  27. ingestr-0.13.47/ingestr/src/resource.py +0 -17
  28. ingestr-0.13.47/ingestr/src/stripe_analytics/settings.py +0 -14
  29. {ingestr-0.13.47 → ingestr-0.13.49}/.dockerignore +0 -0
  30. {ingestr-0.13.47 → ingestr-0.13.49}/.githooks/pre-commit-hook.sh +0 -0
  31. {ingestr-0.13.47 → ingestr-0.13.49}/.github/workflows/deploy-docs.yml +0 -0
  32. {ingestr-0.13.47 → ingestr-0.13.49}/.github/workflows/release.yml +0 -0
  33. {ingestr-0.13.47 → ingestr-0.13.49}/.github/workflows/secrets-scan.yml +0 -0
  34. {ingestr-0.13.47 → ingestr-0.13.49}/.github/workflows/tests.yml +0 -0
  35. {ingestr-0.13.47 → ingestr-0.13.49}/.gitignore +0 -0
  36. {ingestr-0.13.47 → ingestr-0.13.49}/.python-version +0 -0
  37. {ingestr-0.13.47 → ingestr-0.13.49}/.vale.ini +0 -0
  38. {ingestr-0.13.47 → ingestr-0.13.49}/Dockerfile +0 -0
  39. {ingestr-0.13.47 → ingestr-0.13.49}/LICENSE.md +0 -0
  40. {ingestr-0.13.47 → ingestr-0.13.49}/Makefile +0 -0
  41. {ingestr-0.13.47 → ingestr-0.13.49}/docs/.vitepress/theme/custom.css +0 -0
  42. {ingestr-0.13.47 → ingestr-0.13.49}/docs/.vitepress/theme/index.js +0 -0
  43. {ingestr-0.13.47 → ingestr-0.13.49}/docs/commands/example-uris.md +0 -0
  44. {ingestr-0.13.47 → ingestr-0.13.49}/docs/commands/ingest.md +0 -0
  45. {ingestr-0.13.47 → ingestr-0.13.49}/docs/getting-started/core-concepts.md +0 -0
  46. {ingestr-0.13.47 → ingestr-0.13.49}/docs/getting-started/incremental-loading.md +0 -0
  47. {ingestr-0.13.47 → ingestr-0.13.49}/docs/getting-started/quickstart.md +0 -0
  48. {ingestr-0.13.47 → ingestr-0.13.49}/docs/getting-started/telemetry.md +0 -0
  49. {ingestr-0.13.47 → ingestr-0.13.49}/docs/index.md +0 -0
  50. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/applovin_max.png +0 -0
  51. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/athena.png +0 -0
  52. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/clickhouse_img.png +0 -0
  53. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/freshdesk_ingestion.png +0 -0
  54. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/github.png +0 -0
  55. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/google_analytics_realtime_report.png +0 -0
  56. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/googleanalytics.png +0 -0
  57. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/ingestion_elasticsearch_img.png +0 -0
  58. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/kinesis.bigquery.png +0 -0
  59. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/linkedin_ads.png +0 -0
  60. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/personio.png +0 -0
  61. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/personio_duckdb.png +0 -0
  62. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/phantombuster.png +0 -0
  63. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/pipedrive.png +0 -0
  64. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/spanner_ingestion.png +0 -0
  65. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/stripe_postgres.png +0 -0
  66. {ingestr-0.13.47 → ingestr-0.13.49}/docs/media/tiktok.png +0 -0
  67. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/adjust.md +0 -0
  68. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/airtable.md +0 -0
  69. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/applovin.md +0 -0
  70. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/applovin_max.md +0 -0
  71. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/appsflyer.md +0 -0
  72. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/appstore.md +0 -0
  73. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/asana.md +0 -0
  74. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/athena.md +0 -0
  75. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/attio.md +0 -0
  76. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/bigquery.md +0 -0
  77. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/chess.md +0 -0
  78. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/clickhouse.md +0 -0
  79. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/csv.md +0 -0
  80. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/custom_queries.md +0 -0
  81. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/databricks.md +0 -0
  82. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/db2.md +0 -0
  83. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/duckdb.md +0 -0
  84. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/dynamodb.md +0 -0
  85. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/elasticsearch.md +0 -0
  86. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/facebook-ads.md +0 -0
  87. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/freshdesk.md +0 -0
  88. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/gcs.md +0 -0
  89. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/github.md +0 -0
  90. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/google-ads.md +0 -0
  91. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/google_analytics.md +0 -0
  92. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/gorgias.md +0 -0
  93. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/gsheets.md +0 -0
  94. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/hubspot.md +0 -0
  95. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/kafka.md +0 -0
  96. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/kinesis.md +0 -0
  97. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/klaviyo.md +0 -0
  98. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/linkedin_ads.md +0 -0
  99. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/mongodb.md +0 -0
  100. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/mssql.md +0 -0
  101. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/mysql.md +0 -0
  102. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/notion.md +0 -0
  103. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/oracle.md +0 -0
  104. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/personio.md +0 -0
  105. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/phantombuster.md +0 -0
  106. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/pipedrive.md +0 -0
  107. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/postgres.md +0 -0
  108. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/redshift.md +0 -0
  109. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/salesforce.md +0 -0
  110. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/sap-hana.md +0 -0
  111. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/shopify.md +0 -0
  112. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/slack.md +0 -0
  113. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/snowflake.md +0 -0
  114. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/spanner.md +0 -0
  115. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/sqlite.md +0 -0
  116. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/stripe.md +0 -0
  117. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/tiktok-ads.md +0 -0
  118. {ingestr-0.13.47 → ingestr-0.13.49}/docs/supported-sources/zendesk.md +0 -0
  119. {ingestr-0.13.47 → ingestr-0.13.49}/docs/tutorials/load-kinesis-bigquery.md +0 -0
  120. {ingestr-0.13.47 → ingestr-0.13.49}/docs/tutorials/load-personio-duckdb.md +0 -0
  121. {ingestr-0.13.47 → ingestr-0.13.49}/docs/tutorials/load-stripe-postgres.md +0 -0
  122. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/conftest.py +0 -0
  123. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/.gitignore +0 -0
  124. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/adjust/__init__.py +0 -0
  125. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/adjust/adjust_helpers.py +0 -0
  126. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/airtable/__init__.py +0 -0
  127. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/applovin/__init__.py +0 -0
  128. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/applovin_max/__init__.py +0 -0
  129. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/appsflyer/__init__.py +0 -0
  130. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/appsflyer/client.py +0 -0
  131. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/appstore/__init__.py +0 -0
  132. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/appstore/client.py +0 -0
  133. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/appstore/errors.py +0 -0
  134. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/appstore/models.py +0 -0
  135. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/appstore/resources.py +0 -0
  136. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/arrow/__init__.py +0 -0
  137. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/asana_source/__init__.py +0 -0
  138. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/asana_source/helpers.py +0 -0
  139. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/asana_source/settings.py +0 -0
  140. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/attio/__init__.py +0 -0
  141. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/attio/helpers.py +0 -0
  142. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/blob.py +0 -0
  143. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/chess/__init__.py +0 -0
  144. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/chess/helpers.py +0 -0
  145. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/chess/settings.py +0 -0
  146. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/collector/spinner.py +0 -0
  147. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/destinations.py +0 -0
  148. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/dynamodb/__init__.py +0 -0
  149. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/elasticsearch/__init__.py +0 -0
  150. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/errors.py +0 -0
  151. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/facebook_ads/__init__.py +0 -0
  152. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/facebook_ads/exceptions.py +0 -0
  153. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/facebook_ads/helpers.py +0 -0
  154. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/facebook_ads/settings.py +0 -0
  155. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/filesystem/__init__.py +0 -0
  156. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/filesystem/helpers.py +0 -0
  157. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/filesystem/readers.py +0 -0
  158. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/filters.py +0 -0
  159. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/frankfurter/__init__.py +0 -0
  160. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/frankfurter/helpers.py +0 -0
  161. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/freshdesk/__init__.py +0 -0
  162. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/freshdesk/freshdesk_client.py +0 -0
  163. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/freshdesk/settings.py +0 -0
  164. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/github/__init__.py +0 -0
  165. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/github/helpers.py +0 -0
  166. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/github/queries.py +0 -0
  167. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/github/settings.py +0 -0
  168. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/google_ads/__init__.py +0 -0
  169. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/google_ads/field.py +0 -0
  170. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/google_ads/metrics.py +0 -0
  171. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/google_ads/predicates.py +0 -0
  172. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/google_ads/reports.py +0 -0
  173. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/google_analytics/__init__.py +0 -0
  174. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/google_analytics/helpers.py +0 -0
  175. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/google_sheets/README.md +0 -0
  176. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/google_sheets/__init__.py +0 -0
  177. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/google_sheets/helpers/__init__.py +0 -0
  178. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/google_sheets/helpers/api_calls.py +0 -0
  179. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/google_sheets/helpers/data_processing.py +0 -0
  180. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/gorgias/__init__.py +0 -0
  181. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/gorgias/helpers.py +0 -0
  182. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/http_client.py +0 -0
  183. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/hubspot/__init__.py +0 -0
  184. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/hubspot/helpers.py +0 -0
  185. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/hubspot/settings.py +0 -0
  186. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/kafka/__init__.py +0 -0
  187. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/kafka/helpers.py +0 -0
  188. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/kinesis/__init__.py +0 -0
  189. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/kinesis/helpers.py +0 -0
  190. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/klaviyo/__init__.py +0 -0
  191. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/klaviyo/client.py +0 -0
  192. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/klaviyo/helpers.py +0 -0
  193. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/linkedin_ads/__init__.py +0 -0
  194. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/linkedin_ads/dimension_time_enum.py +0 -0
  195. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/linkedin_ads/helpers.py +0 -0
  196. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/loader.py +0 -0
  197. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/mongodb/helpers.py +0 -0
  198. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/notion/__init__.py +0 -0
  199. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/notion/helpers/__init__.py +0 -0
  200. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/notion/helpers/client.py +0 -0
  201. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/notion/helpers/database.py +0 -0
  202. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/notion/settings.py +0 -0
  203. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/partition.py +0 -0
  204. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/personio/__init__.py +0 -0
  205. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/personio/helpers.py +0 -0
  206. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/phantombuster/__init__.py +0 -0
  207. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/phantombuster/client.py +0 -0
  208. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/pipedrive/__init__.py +0 -0
  209. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/pipedrive/helpers/__init__.py +0 -0
  210. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/pipedrive/helpers/custom_fields_munger.py +0 -0
  211. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/pipedrive/helpers/pages.py +0 -0
  212. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/pipedrive/settings.py +0 -0
  213. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/pipedrive/typing.py +0 -0
  214. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/salesforce/__init__.py +0 -0
  215. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/salesforce/helpers.py +0 -0
  216. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/shopify/__init__.py +0 -0
  217. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/shopify/exceptions.py +0 -0
  218. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/shopify/helpers.py +0 -0
  219. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/shopify/settings.py +0 -0
  220. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/slack/__init__.py +0 -0
  221. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/slack/helpers.py +0 -0
  222. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/slack/settings.py +0 -0
  223. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/sql_database/__init__.py +0 -0
  224. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/sql_database/callbacks.py +0 -0
  225. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/stripe_analytics/helpers.py +0 -0
  226. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/table_definition.py +0 -0
  227. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/telemetry/event.py +0 -0
  228. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/testdata/fakebqcredentials.json +0 -0
  229. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/tiktok_ads/__init__.py +0 -0
  230. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/tiktok_ads/tiktok_helpers.py +0 -0
  231. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/time.py +0 -0
  232. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/version.py +0 -0
  233. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/zendesk/__init__.py +0 -0
  234. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/zendesk/helpers/__init__.py +0 -0
  235. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/zendesk/helpers/api_helpers.py +0 -0
  236. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/zendesk/helpers/credentials.py +0 -0
  237. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/zendesk/helpers/talk_api.py +0 -0
  238. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/src/zendesk/settings.py +0 -0
  239. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/testdata/.gitignore +0 -0
  240. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/testdata/create_replace.csv +0 -0
  241. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/testdata/delete_insert_expected.csv +0 -0
  242. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/testdata/delete_insert_part1.csv +0 -0
  243. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/testdata/delete_insert_part2.csv +0 -0
  244. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/testdata/merge_expected.csv +0 -0
  245. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/testdata/merge_part1.csv +0 -0
  246. {ingestr-0.13.47 → ingestr-0.13.49}/ingestr/testdata/merge_part2.csv +0 -0
  247. {ingestr-0.13.47 → ingestr-0.13.49}/package-lock.json +0 -0
  248. {ingestr-0.13.47 → ingestr-0.13.49}/package.json +0 -0
  249. {ingestr-0.13.47 → ingestr-0.13.49}/pyproject.toml +0 -0
  250. {ingestr-0.13.47 → ingestr-0.13.49}/requirements-dev.txt +0 -0
  251. {ingestr-0.13.47 → ingestr-0.13.49}/resources/demo.gif +0 -0
  252. {ingestr-0.13.47 → ingestr-0.13.49}/resources/demo.tape +0 -0
  253. {ingestr-0.13.47 → ingestr-0.13.49}/resources/ingestr.svg +0 -0
  254. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/AMPM.yml +0 -0
  255. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Acronyms.yml +0 -0
  256. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Colons.yml +0 -0
  257. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Contractions.yml +0 -0
  258. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/DateFormat.yml +0 -0
  259. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Ellipses.yml +0 -0
  260. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/EmDash.yml +0 -0
  261. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Exclamation.yml +0 -0
  262. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/FirstPerson.yml +0 -0
  263. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Gender.yml +0 -0
  264. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/GenderBias.yml +0 -0
  265. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/HeadingPunctuation.yml +0 -0
  266. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Headings.yml +0 -0
  267. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Latin.yml +0 -0
  268. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/LyHyphens.yml +0 -0
  269. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/OptionalPlurals.yml +0 -0
  270. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Ordinal.yml +0 -0
  271. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/OxfordComma.yml +0 -0
  272. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Parens.yml +0 -0
  273. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Passive.yml +0 -0
  274. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Periods.yml +0 -0
  275. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Quotes.yml +0 -0
  276. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Ranges.yml +0 -0
  277. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Semicolons.yml +0 -0
  278. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Slang.yml +0 -0
  279. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Spacing.yml +0 -0
  280. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Spelling.yml +0 -0
  281. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Units.yml +0 -0
  282. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/We.yml +0 -0
  283. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/Will.yml +0 -0
  284. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/WordList.yml +0 -0
  285. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/meta.json +0 -0
  286. {ingestr-0.13.47 → ingestr-0.13.49}/styles/Google/vocab.txt +0 -0
  287. {ingestr-0.13.47 → ingestr-0.13.49}/styles/bruin/Ingestr.yml +0 -0
  288. {ingestr-0.13.47 → ingestr-0.13.49}/styles/config/vocabularies/bruin/accept.txt +0 -0
  289. {ingestr-0.13.47 → ingestr-0.13.49}/test.env.template +0 -0
@@ -1,4 +1,5 @@
1
1
  ingestr/src/telemetry/event.py:generic-api-key:17
2
2
  ingestr/src/testdata/fakebqcredentials.json:private-key:5
3
3
  docs/supported-sources/shopify.md:generic-api-key:26
4
+ docs/supported-sources/smartsheets.md:generic-api-key:38
4
5
  ingestr/src/google_analytics/helpers_test.py:generic-api-key:36
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ingestr
3
- Version: 0.13.47
3
+ Version: 0.13.49
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
@@ -163,6 +163,7 @@ Requires-Dist: shellingham==1.5.4
163
163
  Requires-Dist: simple-salesforce==1.12.6
164
164
  Requires-Dist: simplejson==3.20.1
165
165
  Requires-Dist: six==1.17.0
166
+ Requires-Dist: smartsheet-python-sdk==3.0.5
166
167
  Requires-Dist: smmap==5.0.2
167
168
  Requires-Dist: snowflake-connector-python==3.14.0
168
169
  Requires-Dist: snowflake-sqlalchemy==1.6.1
@@ -467,6 +468,11 @@ Pull requests are welcome. However, please open an issue first to discuss what y
467
468
  <td>✅</td>
468
469
  <td>-</td>
469
470
  </tr>
471
+ <tr>
472
+ <td>Smartsheet</td>
473
+ <td>✅</td>
474
+ <td>-</td>
475
+ </tr>
470
476
  <tr>
471
477
  <td>Stripe</td>
472
478
  <td>✅</td>
@@ -263,6 +263,11 @@ Pull requests are welcome. However, please open an issue first to discuss what y
263
263
  <td>✅</td>
264
264
  <td>-</td>
265
265
  </tr>
266
+ <tr>
267
+ <td>Smartsheet</td>
268
+ <td>✅</td>
269
+ <td>-</td>
270
+ </tr>
266
271
  <tr>
267
272
  <td>Stripe</td>
268
273
  <td>✅</td>
@@ -139,7 +139,9 @@ export default defineConfig({
139
139
  { text: "Salesforce", link: "/supported-sources/salesforce.md" },
140
140
  { text: "Shopify", link: "/supported-sources/shopify.md" },
141
141
  { text: "Slack", link: "/supported-sources/slack.md" },
142
+ { text: "Smartsheet", link: "/supported-sources/smartsheets.md" },
142
143
  { text: "Spanner", link: "/supported-sources/spanner.md" },
144
+ { text: "Solidgate", link: "/supported-sources/solidgate.md" },
143
145
  { text: "Stripe", link: "/supported-sources/stripe.md" },
144
146
  { text: "TikTok Ads", link: "/supported-sources/tiktok-ads.md" },
145
147
  { text: "Zendesk", link: "/supported-sources/zendesk.md" },
@@ -0,0 +1,48 @@
1
+ # Frankfurter
2
+
3
+ [Frankfurter API](https://www.frankfurter.dev/) is an online platform which fetches current and historical exchnge rate data.
4
+
5
+ ingestr supports Frankfurter as a source primarily to demonstrate ingestr's features since the API doesn't require any authentication.
6
+
7
+ ## URI format
8
+
9
+ The URI format for Frankfurter is as follows:
10
+
11
+ ```plaintext
12
+ frankfurter://?base=<currency-code-here>
13
+ ```
14
+
15
+ URI parameters:
16
+ - `base`: defines the base currency code (e.g. EUR, USD, IDR) used to calculate the exchange rates.
17
+
18
+
19
+ ## Set-Up Frankfurter Integration
20
+
21
+ Let's say you want to fetch the exchange rates for a certain period with the base currency as Indian Rupees. Here's a sample command that will copy this data into your DuckDB database:
22
+
23
+ ```bash
24
+ ingestr ingest \
25
+ --source-uri 'frankfurter://?base=INR' \
26
+ --interval-start '2025-03-20' \
27
+ --interval-end '2025-03-28' \
28
+ --source-table 'exchange_rates' \
29
+ --dest-uri 'duckdb///frankfurter.duckdb' \
30
+ --dest-table 'my_schema.exchange_rates'
31
+ ```
32
+
33
+ The result of this command will be a list of currency exchange rates from 20.03.2025-28.03.2025 with INR as the base currency in your DuckDB database.
34
+
35
+ ## Tables
36
+
37
+ - `latest`: Fetches the latest exchange rates.
38
+ - `exchange_rates`: Fetches historical exchange rates for a specified date range.
39
+ - `currencies`: Fetches a list of the available currencies and their ISO 4217 currency code (e.g. `USD`, `EUR`, `GBP`).
40
+
41
+ Use these as `--source-table` parameter in the `ingestr ingest` command.
42
+
43
+
44
+ **Notes**:
45
+ - The arguments `--interval-start` and `--interval-end` are only relevant for the table exchange_rates.
46
+ - If a start date but no end date is specified, then the end date will default to today's date and ingestr will retrieve data up until the latest published data.
47
+ - Note that the [Frankfurter API](https://www.frankfurter.dev/) only publishes updates Monday-Friday. If the given date is on the weekend, the date will default to the previous Friday.
48
+
@@ -0,0 +1,136 @@
1
+ # Amazon S3
2
+
3
+ Amazon Simple Storage Service (S3) is a scalable cloud storage service offered by Amazon Web Services (AWS). It allows users to store and retrieve extensive amounts of data from anywhere on the web.
4
+
5
+ `ingestr` supports Amazon S3 as both a data source and destination.
6
+
7
+ ## URI Format
8
+
9
+ The URI for connecting to Amazon S3 is structured as follows:
10
+
11
+ ```plaintext
12
+ s3://?access_key_id=<your_access_key_id>&secret_access_key=<your_secret_access_key>
13
+ ```
14
+
15
+ **URI Parameters:**
16
+
17
+ * `access_key_id`: Your AWS access key ID.
18
+ * `secret_access_key`: Your AWS secret access key.
19
+ * `endpoint_url`: URL of an S3-Compatiable API Server (optional, destination only)
20
+ * `layout`: Layout template (optional, destination only)
21
+
22
+ These credentials are required to authenticate and authorize access to your S3 buckets.
23
+
24
+ The `--source-table` parameter specifies the S3 bucket and file pattern using the following format:
25
+
26
+ ```
27
+ <bucket-name>/<file-glob-pattern>
28
+ ```
29
+
30
+ ## Setting up an S3 Integration
31
+
32
+ To integrate `ingestr` with Amazon S3, you need an `access_key_id` and a `secret_access_key`. For guidance on obtaining these credentials, refer to the dltHub documentation on [AWS credentials](https://dlthub.com/docs/dlt-ecosystem/verified-sources/filesystem/basic#get-credentials).
33
+
34
+ Once you have your credentials, you can configure the S3 URI. The `bucket_name` and `path_to_files` (file glob pattern) are specified in the `--source-table` argument.
35
+
36
+ ### Example: Loading data from S3
37
+
38
+ Let's assume the following details:
39
+ * `access_key_id`: `AKC3YOW7E`
40
+ * `secret_access_key`: `XCtkpL5B`
41
+ * S3 bucket name: `my_bucket`
42
+ * Path to files within the bucket: `students/students_details.csv`
43
+
44
+ The following command demonstrates how to copy data from the specified S3 location to a DuckDB database:
45
+
46
+ ```sh
47
+ ingestr ingest \
48
+ --source-uri 's3://?access_key_id=AKC3YOW7E&secret_access_key=XCtkpL5B' \
49
+ --source-table 'my_bucket/students/students_details.csv' \
50
+ --dest-uri duckdb:///s3_data.duckdb \
51
+ --dest-table 'processed_students.student_details'
52
+ ```
53
+
54
+ This command will create a table named `student_details` within the `processed_students` schema (or equivalent grouping) in the DuckDB database file located at `s3_data.duckdb`.
55
+
56
+ ### Example: Uploading data to S3
57
+ For this, example we'll assume that:
58
+ * `records.db` is a duckdb database.
59
+ * has a table called `public.users`.
60
+ * the S3 credentials are the same as the example above.
61
+
62
+ The following command demonstrates how to copy data from a local duckdb database to S3:
63
+ ```sh
64
+ ingestr ingest \
65
+ --source-uri 'duckdb:///records.db' \
66
+ --source-table 'public.users' \
67
+ --dest-uri 's3://?access_key_id=AKC3YOW7E&secret_access_key=XCtkpL5B' \
68
+ --dest-table 'my_bucket/records'
69
+ ```
70
+
71
+ This will result in a file structure like the following:
72
+ ```
73
+ my_bucket/
74
+ └── records
75
+ ├── _dlt_loads
76
+ ├── _dlt_pipeline_state
77
+ ├── _dlt_version
78
+ └── users
79
+ └── <load_id>.<file_id>.parquet
80
+ ```
81
+
82
+ The value of `load_id` and `file_id` is determined at runtime. The default layout creates a folder with the same table name as the source and places the data inside a parquet file. This layout is configurable using the `layout` parameter.
83
+
84
+ For example, if you would like to create a parquet file with the same name as the source table (as opposed to a folder) you can set `layout` to `{table_name}.{ext}` in the commandline above:
85
+
86
+ ```sh
87
+ ingestr ingest \
88
+ --source-uri 'duckdb:///records.db' \
89
+ --source-table 'public.users' \
90
+ --dest-uri 's3://?layout={table_name}.{ext}&access_key_id=AKC3YOW7E&secret_access_key=XCtkpL5B' \
91
+ --dest-table 'my_bucket/records'
92
+ ```
93
+
94
+ Result:
95
+ ```
96
+ my_bucket/
97
+ └── records
98
+ ├── _dlt_loads
99
+ ├── _dlt_pipeline_state
100
+ ├── _dlt_version
101
+ └── users.parquet
102
+ ```
103
+
104
+ List of available Layout variables is available [here](https://dlthub.com/docs/dlt-ecosystem/destinations/filesystem#available-layout-placeholders)
105
+
106
+ ### Working with S3-Compatiable object stores
107
+ `ingestr` support S3 compatiable storage services like [Minio](https://min.io/), Digital Ocean [spaces](https://www.digitalocean.com/products/spaces) and Cloudflare [R2](https://developers.cloudflare.com/r2/). You can set the `endpoint_url` in your destination URI to write data to these object stores.
108
+
109
+ For example, if you're running minio on `localhost:9000`, you can write the same data as the [example above](#example-uploading-data-to-s3) by running:
110
+ ```sh
111
+ ingestr ingest \
112
+ --source-uri 'duckdb:///records.db' \
113
+ --source-table 'public.users' \
114
+ --dest-uri 's3://?endpoint_url=http://localhost:9000&access_key_id=AKC3YOW7E&secret_access_key=XCtkpL5B' \
115
+ --dest-table 'my_bucket/records'
116
+ ```
117
+
118
+ ::: info NOTE
119
+ S3-Compatiable object stores are currently only supported as destinations.
120
+ :::
121
+
122
+ ### File Glob Pattern Examples:
123
+
124
+ ::: warning
125
+ Glob patterns only apply when loading data from S3 as source.
126
+ :::
127
+
128
+ The `<file-glob-pattern>` in the `--source-table` argument allows for flexible file selection. Here are some common patterns and their descriptions:
129
+
130
+ | Pattern | Description |
131
+ | :------------------------------------------ | :--------------------------------------------------------------------------------------------------------- |
132
+ | `bucket/**/*.csv` | Retrieves all CSV files recursively from `s3://bucket`. |
133
+ | `bucket/*.csv` | Retrieves all CSV files located at the root level of `s3://bucket`. |
134
+ | `bucket/myFolder/**/*.jsonl` | Retrieves all JSONL files recursively from the `myFolder` directory and its subdirectories in `s3://bucket`. |
135
+ | `bucket/myFolder/mySubFolder/users.parquet` | Retrieves the specific `users.parquet` file from the `myFolder/mySubFolder/` path in `s3://bucket`. |
136
+ | `bucket/employees.jsonl` | Retrieves the `employees.jsonl` file located at the root level of the `s3://bucket`. |
@@ -0,0 +1,47 @@
1
+ # Smartsheet
2
+
3
+ [Smartsheet](https://www.smartsheet.com/) is a software as a service (SaaS) offering for collaboration and work management.
4
+
5
+ ingestr supports Smartsheet as a source.
6
+
7
+ ## URI format
8
+
9
+ The URI format for Smartsheet is as follows:
10
+
11
+ ```plaintext
12
+ smartsheet://?access_token=<access_token>
13
+ ```
14
+
15
+ URI parameters:
16
+
17
+ - `access_token`: Your Smartsheet API access token.
18
+
19
+ The URI is used to connect to the Smartsheet API for extracting data. You can generate an access token in Smartsheet by navigating to Account > Personal Settings > API Access.
20
+
21
+ ## Setting up a Smartsheet Integration
22
+
23
+ To set up a Smartsheet integration, you'll need an API Access Token.
24
+
25
+ 1. Log in to Smartsheet.
26
+ 2. Click on "Account" in the bottom left corner, then "Personal Settings".
27
+ 3. Go to the "API Access" tab.
28
+ 4. Click "Generate new access token".
29
+ 5. Give your token a name and click "OK".
30
+ 6. Copy the generated token. This will be your `access_token`.
31
+
32
+ The source table you'll use for ingestr will be the `sheet_id` of the Smartsheet you want to ingest. You can find the `sheet_id` by opening the sheet in Smartsheet and going to File > Properties. The Sheet ID will be listed there.
33
+
34
+ Let's say your access token is `llk2k3j4l5k6j7h8g9f0` and the sheet ID is `1234567890123456`, here's a sample command that will copy the data from Smartsheet into a DuckDB database:
35
+
36
+ ```sh
37
+ ingestr ingest \
38
+ --source-uri 'smartsheet://?access_token=llk2k3j4l5k6j7h8g9f0' \
39
+ --source-table '1234567890123456' \
40
+ --dest-uri 'duckdb:///smartsheet_data.duckdb' \
41
+ --dest-table 'des.my_sheet_data'
42
+ ```
43
+
44
+ The result of this command will be a `my_sheet_data` table containing data from your Smartsheet in the `smartsheet_data.duckdb` database.
45
+
46
+ > [!CAUTION]
47
+ > Smartsheet integration does not currently support incremental loading. Every time you run the command, the entire sheet will be copied from Smartsheet to the destination. This can be slow for large sheets.
@@ -0,0 +1,46 @@
1
+ # Solidgate
2
+
3
+ [Solidgate](https://solidgate.com//) is a one-stop payment processing platform that ensures seamless online purchases and streamlined payment infrastructure.
4
+
5
+ ingestr supports Solidgate as a source.
6
+
7
+ ## URI format
8
+
9
+ The URI format for Solidgate is as follows:
10
+
11
+ ```plaintext
12
+ solidgate://?public_key=<your-public-key>&secret_key=<your-secret-key>
13
+ ```
14
+
15
+ URI parameters:
16
+
17
+ - `public_key`: The public API key used to identify the account.
18
+
19
+ - `secret_key`: The secret API key used to authenticate requests to the Solidgate API.
20
+
21
+
22
+ ## Setting up a Solidgate Integration
23
+
24
+ Solidgate requires a few steps to set up an integration. Please follow the [guide](https://docs.solidgate.com/payments/integrate/access-to-api/#retrieve-your-credentials).
25
+
26
+ Once you complete the guide, you should have `public_key` and `secret_key`, here’s a sample command that ingests data from Solidgate into a DuckDB database:
27
+
28
+ ```sh
29
+ ingestr ingestr ingest \
30
+ --source-uri "solidgate://?public_key=api_pk_test&secret_key=api_sk_test" \
31
+ --source-table "apm-orders" \
32
+ --dest-uri "duckdb:///solidgate.db" \
33
+ --dest-table "dest.apmorders"
34
+ ```
35
+
36
+ The result of this command will be a table in the `Solidgate.duckdb` database with JSON columns.
37
+
38
+ ## Tables
39
+
40
+ Solidgate source allows ingesting the following sources into separate tables:
41
+ - `subscriptions`: Provides a comprehensive view of customer subscriptions, including subscription IDs, statuses, and key timestamps such as creation, update, and expiration dates.
42
+ - `apm-orders`: Provides essential information for anti-fraud purposes, including order IDs, transaction statuses, amounts, currencies, and payment methods, along with crucial customer details such as email addresses.
43
+ - `card-orders`: Provides detailed information on orders processed via card payments, including transaction data, payment status, and customer details.
44
+
45
+ Use these as `--source-table` parameter in the `ingestr ingest` command.
46
+
@@ -290,6 +290,7 @@ def ingest(
290
290
  from ingestr.src.destinations import AthenaDestination
291
291
  from ingestr.src.factory import SourceDestinationFactory
292
292
  from ingestr.src.filters import cast_set_to_list, handle_mysql_empty_dates
293
+ from ingestr.src.sources import MongoDbSource
293
294
 
294
295
  def report_errors(run_info: LoadInfo):
295
296
  for load_package in run_info.load_packages:
@@ -537,6 +538,13 @@ def ingest(
537
538
  if yield_limit:
538
539
  resource.for_each(dlt_source, lambda x: x.add_limit(yield_limit))
539
540
 
541
+ if isinstance(source, MongoDbSource):
542
+ from ingestr.src.resource import TypeHintMap
543
+
544
+ resource.for_each(
545
+ dlt_source, lambda x: x.add_map(TypeHintMap().type_hint_map)
546
+ )
547
+
540
548
  def col_h(x):
541
549
  if column_hints:
542
550
  x.apply_hints(columns=column_hints)
@@ -0,0 +1 @@
1
+ version = "v0.13.49"
@@ -54,6 +54,7 @@ from ingestr.src.sources import (
54
54
  SalesforceSource,
55
55
  ShopifySource,
56
56
  SlackSource,
57
+ SolidgateSource,
57
58
  SqlSource,
58
59
  StripeAnalyticsSource,
59
60
  TikTokSource,
@@ -159,6 +160,7 @@ class SourceDestinationFactory:
159
160
  "phantombuster": PhantombusterSource,
160
161
  "elasticsearch": ElasticsearchSource,
161
162
  "attio": AttioSource,
163
+ "solidgate": SolidgateSource,
162
164
  }
163
165
  destinations: Dict[str, Type[DestinationProtocol]] = {
164
166
  "bigquery": BigQueryDestination,
@@ -14,7 +14,7 @@ from .helpers import (
14
14
  )
15
15
 
16
16
 
17
- @dlt.source
17
+ @dlt.source(max_table_nesting=0)
18
18
  def mongodb(
19
19
  connection_url: str = dlt.secrets.value,
20
20
  database: Optional[str] = dlt.config.value,
@@ -75,6 +75,7 @@ def mongodb(
75
75
  primary_key="_id",
76
76
  write_disposition=write_disposition,
77
77
  spec=MongoDbCollectionConfiguration,
78
+ max_table_nesting=0,
78
79
  )(
79
80
  client,
80
81
  collection,
@@ -0,0 +1,40 @@
1
+ from typing import Callable
2
+
3
+ from dlt.sources import DltResource, DltSource
4
+
5
+
6
+ def for_each(
7
+ source: DltSource | DltResource, ex: Callable[[DltResource], None | DltResource]
8
+ ):
9
+ """
10
+ Apply a function to each resource in a source.
11
+ """
12
+ if hasattr(source, "selected_resources") and source.selected_resources:
13
+ resource_names = list(source.selected_resources.keys())
14
+ for res in resource_names:
15
+ ex(source.resources[res]) # type: ignore[union-attr]
16
+ else:
17
+ ex(source) # type: ignore[arg-type]
18
+
19
+
20
+ class TypeHintMap:
21
+ def __init__(self):
22
+ self.handled_typehints = False
23
+
24
+ def type_hint_map(self, item):
25
+ if self.handled_typehints:
26
+ return item
27
+
28
+ array_cols = []
29
+ for col in item:
30
+ if isinstance(item[col], (list, tuple)):
31
+ array_cols.append(col)
32
+ if array_cols:
33
+ import dlt
34
+
35
+ source = dlt.current.source()
36
+ columns = [{"name": col, "data_type": "json"} for col in array_cols]
37
+ for_each(source, lambda x: x.apply_hints(columns=columns))
38
+
39
+ self.handled_typehints = True
40
+ return item
@@ -0,0 +1,54 @@
1
+ from typing import Iterable
2
+
3
+ import dlt
4
+ import smartsheet # type: ignore
5
+ from dlt.extract import DltResource
6
+
7
+
8
+ @dlt.source
9
+ def smartsheet_source(
10
+ access_token: str,
11
+ sheet_id: str,
12
+ ) -> Iterable[DltResource]:
13
+ """
14
+ A DLT source for Smartsheet.
15
+
16
+ Args:
17
+ access_token: The Smartsheet API access token.
18
+ sheet_id: The ID of the sheet to load.
19
+
20
+ Returns:
21
+ An iterable of DLT resources.
22
+ """
23
+
24
+ # Initialize Smartsheet client
25
+ smartsheet_client = smartsheet.Smartsheet(access_token)
26
+ smartsheet_client.errors_as_exceptions(True)
27
+
28
+ # The SDK expects sheet_id to be an int
29
+ sheet_id_int = int(sheet_id)
30
+ # Sanitize the sheet name to be a valid resource name
31
+ # We get objectValue to ensure `name` attribute is populated for the sheet
32
+ sheet_details = smartsheet_client.Sheets.get_sheet(
33
+ sheet_id_int, include=["objectValue"]
34
+ )
35
+ sheet_name = sheet_details.name
36
+ resource_name = f"sheet_{sheet_name.replace(' ', '_').lower()}"
37
+
38
+ yield dlt.resource(
39
+ _get_sheet_data(smartsheet_client, sheet_id_int),
40
+ name=resource_name,
41
+ write_disposition="replace",
42
+ )
43
+
44
+
45
+ def _get_sheet_data(smartsheet_client: smartsheet.Smartsheet, sheet_id: int):
46
+ """Helper function to get all rows from a sheet."""
47
+ sheet = smartsheet_client.Sheets.get_sheet(sheet_id)
48
+ # Transform rows to a list of dictionaries
49
+ column_titles = [col.title for col in sheet.columns]
50
+ for row in sheet.rows:
51
+ row_data = {}
52
+ for i, cell in enumerate(row.cells):
53
+ row_data[column_titles[i]] = cell.value
54
+ yield row_data
@@ -0,0 +1,97 @@
1
+ from typing import Iterable, Iterator
2
+
3
+ import dlt
4
+ import pendulum
5
+ from dlt.sources import DltResource
6
+
7
+ from .helpers import SolidgateClient
8
+
9
+
10
+ @dlt.source(max_table_nesting=0)
11
+ def solidgate_source(
12
+ start_date: pendulum.DateTime,
13
+ end_date: pendulum.DateTime | None,
14
+ public_key: str,
15
+ secret_key: str,
16
+ ) -> Iterable[DltResource]:
17
+ solidgate_client = SolidgateClient(public_key, secret_key)
18
+
19
+ @dlt.resource(
20
+ name="subscriptions",
21
+ write_disposition="merge",
22
+ primary_key="id",
23
+ columns={
24
+ "created_at": {"data_type": "timestamp", "partition": True},
25
+ },
26
+ )
27
+ def fetch_all_subscriptions(
28
+ dateTime=dlt.sources.incremental(
29
+ "updated_at",
30
+ initial_value=start_date,
31
+ end_value=end_date,
32
+ range_start="closed",
33
+ range_end="closed",
34
+ ),
35
+ ) -> Iterator[dict]:
36
+ path = "subscriptions"
37
+ if dateTime.end_value is None:
38
+ end_dt = pendulum.now(tz="UTC")
39
+ else:
40
+ end_dt = dateTime.end_value
41
+
42
+ start_dt = dateTime.last_value
43
+ yield solidgate_client.fetch_data(path, date_from=start_dt, date_to=end_dt)
44
+
45
+ @dlt.resource(
46
+ name="apm-orders",
47
+ write_disposition="merge",
48
+ primary_key="order_id",
49
+ columns={
50
+ "created_at": {"data_type": "timestamp", "partition": True},
51
+ },
52
+ )
53
+ def fetch_apm_orders(
54
+ dateTime=dlt.sources.incremental(
55
+ "updated_at",
56
+ initial_value=start_date,
57
+ end_value=end_date,
58
+ range_start="closed",
59
+ range_end="closed",
60
+ ),
61
+ ) -> Iterator[dict]:
62
+ path = "apm-orders"
63
+ if dateTime.end_value is None:
64
+ end_dt = pendulum.now(tz="UTC")
65
+ else:
66
+ end_dt = dateTime.end_value
67
+
68
+ start_dt = dateTime.last_value
69
+ yield solidgate_client.fetch_data(path, date_from=start_dt, date_to=end_dt)
70
+
71
+ @dlt.resource(
72
+ name="card-orders",
73
+ write_disposition="merge",
74
+ primary_key="order_id",
75
+ columns={
76
+ "created_at": {"data_type": "timestamp", "partition": True},
77
+ },
78
+ )
79
+ def fetch_card_orders(
80
+ dateTime=dlt.sources.incremental(
81
+ "updated_at",
82
+ initial_value=start_date,
83
+ end_value=end_date,
84
+ range_start="closed",
85
+ range_end="closed",
86
+ ),
87
+ ) -> Iterator[dict]:
88
+ path = "card-orders"
89
+ if dateTime.end_value is None:
90
+ end_dt = pendulum.now(tz="UTC")
91
+ else:
92
+ end_dt = dateTime.end_value
93
+
94
+ start_dt = dateTime.last_value
95
+ yield solidgate_client.fetch_data(path, date_from=start_dt, date_to=end_dt)
96
+
97
+ return fetch_all_subscriptions, fetch_apm_orders, fetch_card_orders
@@ -0,0 +1,74 @@
1
+ import base64
2
+ import hashlib
3
+ import hmac
4
+ import json
5
+
6
+ import pendulum
7
+
8
+ from ingestr.src.http_client import create_client
9
+
10
+
11
+ class SolidgateClient:
12
+ def __init__(self, public_key, secret_key):
13
+ self.base_url = "https://reports.solidgate.com/api/v1"
14
+ self.public_key = public_key
15
+ self.secret_key = secret_key
16
+ self.client = create_client()
17
+
18
+ def fetch_data(
19
+ self,
20
+ path: str,
21
+ date_from: pendulum.DateTime,
22
+ date_to: pendulum.DateTime,
23
+ ):
24
+ request_payload = {
25
+ "date_from": date_from.format("YYYY-MM-DD HH:mm:ss"),
26
+ "date_to": date_to.format("YYYY-MM-DD HH:mm:ss"),
27
+ }
28
+
29
+ json_string = json.dumps(request_payload)
30
+ signature = self.generateSignature(json_string)
31
+ headers = {
32
+ "merchant": self.public_key,
33
+ "Signature": signature,
34
+ "Content-Type": "application/json",
35
+ }
36
+
37
+ next_page_iterator = None
38
+ url = f"{self.base_url}/{path}"
39
+
40
+ while True:
41
+ payload = request_payload.copy()
42
+ if next_page_iterator:
43
+ payload["page_iterator"] = next_page_iterator
44
+
45
+ response = self.client.post(url, headers=headers, json=payload)
46
+ response.raise_for_status()
47
+ response_json = response.json()
48
+
49
+ if path == "subscriptions":
50
+ data = response_json["subscriptions"]
51
+ for _, value in data.items():
52
+ if "updated_at" in value:
53
+ value["updated_at"] = pendulum.parse(value["updated_at"])
54
+ yield value
55
+
56
+ else:
57
+ data = response_json["orders"]
58
+ for value in data:
59
+ if "updated_at" in value:
60
+ value["updated_at"] = pendulum.parse(value["updated_at"])
61
+ yield value
62
+
63
+ next_page_iterator = response_json.get("metadata", {}).get(
64
+ "next_page_iterator"
65
+ )
66
+ if not next_page_iterator or next_page_iterator == "None":
67
+ break
68
+
69
+ def generateSignature(self, json_string):
70
+ data = self.public_key + json_string + self.public_key
71
+ hmac_hash = hmac.new(
72
+ self.secret_key.encode("utf-8"), data.encode("utf-8"), hashlib.sha512
73
+ ).digest()
74
+ return base64.b64encode(hmac_hash.hex().encode("utf-8")).decode("utf-8")