castor-extractor 0.24.29__tar.gz → 0.24.32__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 castor-extractor might be problematic. Click here for more details.

Files changed (435) hide show
  1. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/CHANGELOG.md +12 -0
  2. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/PKG-INFO +13 -1
  3. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_looker_studio.py +8 -0
  4. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker_studio/client/client.py +23 -6
  5. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker_studio/extract.py +32 -0
  6. castor_extractor-0.24.32/castor_extractor/visualization/looker_studio/extract_test.py +19 -0
  7. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/salesforce_reporting/assets.py +1 -0
  8. castor_extractor-0.24.32/castor_extractor/visualization/salesforce_reporting/client/rest.py +114 -0
  9. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/sigma/client/client.py +47 -7
  10. castor_extractor-0.24.32/castor_extractor/visualization/sigma/client/client_test.py +19 -0
  11. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/sigma/client/pagination.py +1 -0
  12. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/sql_client.py +14 -12
  13. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/pyproject.toml +1 -1
  14. castor_extractor-0.24.29/castor_extractor/visualization/salesforce_reporting/client/rest.py +0 -62
  15. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/Dockerfile +0 -0
  16. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/DockerfileUsage.md +0 -0
  17. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/LICENCE +0 -0
  18. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/README.md +0 -0
  19. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/__init__.py +0 -0
  20. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/__init__.py +0 -0
  21. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_bigquery.py +0 -0
  22. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_confluence.py +0 -0
  23. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_databricks.py +0 -0
  24. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_domo.py +0 -0
  25. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_looker.py +0 -0
  26. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_metabase_api.py +0 -0
  27. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_metabase_db.py +0 -0
  28. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_mode.py +0 -0
  29. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_mysql.py +0 -0
  30. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_notion.py +0 -0
  31. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_postgres.py +0 -0
  32. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_powerbi.py +0 -0
  33. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_qlik.py +0 -0
  34. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_redshift.py +0 -0
  35. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_salesforce.py +0 -0
  36. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_salesforce_reporting.py +0 -0
  37. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_sigma.py +0 -0
  38. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_snowflake.py +0 -0
  39. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_sqlserver.py +0 -0
  40. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_strategy.py +0 -0
  41. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_tableau.py +0 -0
  42. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/extract_thoughtspot.py +0 -0
  43. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/file_check.py +0 -0
  44. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/commands/upload.py +0 -0
  45. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/file_checker/__init__.py +0 -0
  46. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/file_checker/column.py +0 -0
  47. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/file_checker/column_test.py +0 -0
  48. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/file_checker/constants.py +0 -0
  49. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/file_checker/enums.py +0 -0
  50. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/file_checker/file.py +0 -0
  51. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/file_checker/file_test.py +0 -0
  52. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/file_checker/file_test_users.csv +0 -0
  53. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/file_checker/file_test_users_valid.csv +0 -0
  54. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/file_checker/templates/__init__.py +0 -0
  55. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/file_checker/templates/generic_warehouse.py +0 -0
  56. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/__init__.py +0 -0
  57. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/confluence/__init__.py +0 -0
  58. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/confluence/assets.py +0 -0
  59. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/confluence/client/__init__.py +0 -0
  60. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/confluence/client/client.py +0 -0
  61. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/confluence/client/client_test.py +0 -0
  62. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/confluence/client/credentials.py +0 -0
  63. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/confluence/client/endpoints.py +0 -0
  64. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/confluence/client/pagination.py +0 -0
  65. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/confluence/extract.py +0 -0
  66. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/confluence/utils.py +0 -0
  67. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/confluence/utils_test.py +0 -0
  68. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/notion/__init__.py +0 -0
  69. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/notion/assets.py +0 -0
  70. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/notion/client/__init__.py +0 -0
  71. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/notion/client/client.py +0 -0
  72. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/notion/client/client_test.py +0 -0
  73. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/notion/client/constants.py +0 -0
  74. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/notion/client/credentials.py +0 -0
  75. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/notion/client/endpoints.py +0 -0
  76. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/notion/client/pagination.py +0 -0
  77. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/knowledge/notion/extract.py +0 -0
  78. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/logger.py +0 -0
  79. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/quality/__init__.py +0 -0
  80. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/quality/soda/__init__.py +0 -0
  81. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/quality/soda/assets.py +0 -0
  82. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/quality/soda/client/__init__.py +0 -0
  83. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/quality/soda/client/client.py +0 -0
  84. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/quality/soda/client/credentials.py +0 -0
  85. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/quality/soda/client/endpoints.py +0 -0
  86. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/quality/soda/client/pagination.py +0 -0
  87. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/transformation/__init__.py +0 -0
  88. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/transformation/coalesce/__init__.py +0 -0
  89. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/transformation/coalesce/assets.py +0 -0
  90. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/transformation/coalesce/client/__init__.py +0 -0
  91. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/transformation/coalesce/client/client.py +0 -0
  92. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/transformation/coalesce/client/credentials.py +0 -0
  93. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/transformation/coalesce/client/endpoint.py +0 -0
  94. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/transformation/coalesce/client/type.py +0 -0
  95. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/transformation/coalesce/client/utils.py +0 -0
  96. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/transformation/coalesce/client/utils_test.py +0 -0
  97. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/transformation/dbt/__init__.py +0 -0
  98. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/transformation/dbt/assets.py +0 -0
  99. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/transformation/dbt/client.py +0 -0
  100. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/transformation/dbt/client_test.py +0 -0
  101. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/transformation/dbt/credentials.py +0 -0
  102. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/types.py +0 -0
  103. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/uploader/__init__.py +0 -0
  104. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/uploader/constant.py +0 -0
  105. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/uploader/env.py +0 -0
  106. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/uploader/env_test.py +0 -0
  107. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/uploader/settings.py +0 -0
  108. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/uploader/upload.py +0 -0
  109. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/uploader/upload_test.py +0 -0
  110. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/uploader/utils.py +0 -0
  111. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/__init__.py +0 -0
  112. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/argument_parser.py +0 -0
  113. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/argument_parser_test.py +0 -0
  114. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/batch.py +0 -0
  115. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/batch_test.py +0 -0
  116. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/__init__.py +0 -0
  117. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/abstract.py +0 -0
  118. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/api/__init__.py +0 -0
  119. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/api/auth.py +0 -0
  120. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/api/auth_test.py +0 -0
  121. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/api/client.py +0 -0
  122. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/api/client_test.py +0 -0
  123. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/api/pagination.py +0 -0
  124. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/api/pagination_test.py +0 -0
  125. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/api/safe_request.py +0 -0
  126. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/api/safe_request_test.py +0 -0
  127. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/api/utils.py +0 -0
  128. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/api/utils_test.py +0 -0
  129. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/postgres.py +0 -0
  130. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/query.py +0 -0
  131. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/uri.py +0 -0
  132. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/client/uri_test.py +0 -0
  133. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/collection.py +0 -0
  134. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/collection_test.py +0 -0
  135. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/constants.py +0 -0
  136. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/deprecate.py +0 -0
  137. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/env.py +0 -0
  138. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/files.py +0 -0
  139. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/files_test.py +0 -0
  140. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/formatter.py +0 -0
  141. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/formatter_test.csv +0 -0
  142. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/formatter_test.json +0 -0
  143. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/formatter_test.py +0 -0
  144. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/json_stream_write.py +0 -0
  145. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/load.py +0 -0
  146. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/object.py +0 -0
  147. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/object_test.py +0 -0
  148. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/pager/__init__.py +0 -0
  149. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/pager/pager.py +0 -0
  150. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/pager/pager_on_id.py +0 -0
  151. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/pager/pager_on_id_test.py +0 -0
  152. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/pager/pager_test.py +0 -0
  153. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/retry.py +0 -0
  154. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/retry_test.py +0 -0
  155. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/safe.py +0 -0
  156. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/safe_test.py +0 -0
  157. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/salesforce/__init__.py +0 -0
  158. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/salesforce/client.py +0 -0
  159. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/salesforce/client_test.py +0 -0
  160. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/salesforce/constants.py +0 -0
  161. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/salesforce/credentials.py +0 -0
  162. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/salesforce/credentials_test.py +0 -0
  163. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/salesforce/pagination.py +0 -0
  164. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/store.py +0 -0
  165. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/string.py +0 -0
  166. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/string_test.py +0 -0
  167. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/time.py +0 -0
  168. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/time_test.py +0 -0
  169. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/type.py +0 -0
  170. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/url.py +0 -0
  171. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/url_test.py +0 -0
  172. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/validation.py +0 -0
  173. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/validation_test.py +0 -0
  174. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/utils/write.py +0 -0
  175. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/__init__.py +0 -0
  176. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/domo/__init__.py +0 -0
  177. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/domo/assets.py +0 -0
  178. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/domo/client/__init__.py +0 -0
  179. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/domo/client/client.py +0 -0
  180. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/domo/client/credentials.py +0 -0
  181. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/domo/client/endpoints.py +0 -0
  182. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/domo/client/pagination.py +0 -0
  183. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/domo/client/pagination_test.py +0 -0
  184. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/domo/extract.py +0 -0
  185. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/__init__.py +0 -0
  186. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/api/__init__.py +0 -0
  187. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/api/client.py +0 -0
  188. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/api/client_test.py +0 -0
  189. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/api/constants.py +0 -0
  190. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/api/credentials.py +0 -0
  191. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/api/extraction_parameters.py +0 -0
  192. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/api/sdk.py +0 -0
  193. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/api/sdk_test.py +0 -0
  194. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/api/utils.py +0 -0
  195. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/assets.py +0 -0
  196. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/constant.py +0 -0
  197. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/constants.py +0 -0
  198. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/extract.py +0 -0
  199. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/fields.py +0 -0
  200. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/fields_test.py +0 -0
  201. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker/multithreading.py +0 -0
  202. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker_studio/__init__.py +0 -0
  203. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker_studio/assets.py +0 -0
  204. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker_studio/client/__init__.py +0 -0
  205. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker_studio/client/admin_sdk_client.py +0 -0
  206. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker_studio/client/credentials.py +0 -0
  207. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker_studio/client/endpoints.py +0 -0
  208. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker_studio/client/enums.py +0 -0
  209. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker_studio/client/looker_studio_api_client.py +0 -0
  210. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker_studio/client/pagination.py +0 -0
  211. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/looker_studio/client/queries/query.sql +0 -0
  212. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/__init__.py +0 -0
  213. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/assets.py +0 -0
  214. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/__init__.py +0 -0
  215. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/api/__init__.py +0 -0
  216. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/api/client.py +0 -0
  217. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/api/client_test.py +0 -0
  218. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/api/credentials.py +0 -0
  219. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/db/__init__.py +0 -0
  220. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/db/client.py +0 -0
  221. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/db/credentials.py +0 -0
  222. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/db/queries/.sqlfluff +0 -0
  223. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/db/queries/base_url.sql +0 -0
  224. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/db/queries/card.sql +0 -0
  225. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/db/queries/collection.sql +0 -0
  226. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/db/queries/dashboard.sql +0 -0
  227. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/db/queries/dashboard_cards.sql +0 -0
  228. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/db/queries/database.sql +0 -0
  229. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/db/queries/table.sql +0 -0
  230. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/db/queries/user.sql +0 -0
  231. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/decryption.py +0 -0
  232. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/decryption_test.py +0 -0
  233. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/client/shared.py +0 -0
  234. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/errors.py +0 -0
  235. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/extract.py +0 -0
  236. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/metabase/types.py +0 -0
  237. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/mode/__init__.py +0 -0
  238. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/mode/assets.py +0 -0
  239. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/mode/client/__init__.py +0 -0
  240. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/mode/client/client.py +0 -0
  241. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/mode/client/client_test.json +0 -0
  242. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/mode/client/client_test.py +0 -0
  243. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/mode/client/constants.py +0 -0
  244. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/mode/client/credentials.py +0 -0
  245. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/mode/errors.py +0 -0
  246. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/mode/extract.py +0 -0
  247. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/powerbi/__init__.py +0 -0
  248. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/powerbi/assets.py +0 -0
  249. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/powerbi/client/__init__.py +0 -0
  250. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/powerbi/client/authentication.py +0 -0
  251. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/powerbi/client/client.py +0 -0
  252. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/powerbi/client/client_test.py +0 -0
  253. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/powerbi/client/constants.py +0 -0
  254. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/powerbi/client/credentials.py +0 -0
  255. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/powerbi/client/credentials_test.py +0 -0
  256. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/powerbi/client/endpoints.py +0 -0
  257. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/powerbi/client/pagination.py +0 -0
  258. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/powerbi/extract.py +0 -0
  259. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/__init__.py +0 -0
  260. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/assets.py +0 -0
  261. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/client/__init__.py +0 -0
  262. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/client/constants.py +0 -0
  263. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/client/engine/__init__.py +0 -0
  264. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/client/engine/client.py +0 -0
  265. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/client/engine/constants.py +0 -0
  266. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/client/engine/credentials.py +0 -0
  267. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/client/engine/error.py +0 -0
  268. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/client/engine/error_test.py +0 -0
  269. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/client/engine/json_rpc.py +0 -0
  270. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/client/engine/json_rpc_test.py +0 -0
  271. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/client/engine/websocket.py +0 -0
  272. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/client/master.py +0 -0
  273. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/client/rest.py +0 -0
  274. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/client/rest_test.py +0 -0
  275. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/qlik/extract.py +0 -0
  276. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/salesforce_reporting/__init__.py +0 -0
  277. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/salesforce_reporting/client/__init__.py +0 -0
  278. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/salesforce_reporting/client/soql.py +0 -0
  279. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/salesforce_reporting/extract.py +0 -0
  280. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/sigma/__init__.py +0 -0
  281. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/sigma/assets.py +0 -0
  282. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/sigma/client/__init__.py +0 -0
  283. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/sigma/client/credentials.py +0 -0
  284. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/sigma/client/endpoints.py +0 -0
  285. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/sigma/extract.py +0 -0
  286. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/strategy/__init__.py +0 -0
  287. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/strategy/assets.py +0 -0
  288. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/strategy/client/__init__.py +0 -0
  289. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/strategy/client/client.py +0 -0
  290. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/strategy/client/credentials.py +0 -0
  291. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/strategy/client/properties.py +0 -0
  292. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/strategy/extract.py +0 -0
  293. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/tableau/__init__.py +0 -0
  294. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/tableau/assets.py +0 -0
  295. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/tableau/client/__init__.py +0 -0
  296. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/tableau/client/client.py +0 -0
  297. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/tableau/client/client_metadata_api.py +0 -0
  298. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/tableau/client/client_metadata_api_test.py +0 -0
  299. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/tableau/client/client_rest_api.py +0 -0
  300. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/tableau/client/client_tsc.py +0 -0
  301. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/tableau/client/credentials.py +0 -0
  302. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/tableau/client/errors.py +0 -0
  303. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/tableau/client/gql_queries.py +0 -0
  304. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/tableau/client/rest_fields.py +0 -0
  305. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/tableau/constants.py +0 -0
  306. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/tableau/extract.py +0 -0
  307. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/thoughtspot/__init__.py +0 -0
  308. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/thoughtspot/assets.py +0 -0
  309. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/thoughtspot/client/__init__.py +0 -0
  310. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/thoughtspot/client/client.py +0 -0
  311. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/thoughtspot/client/credentials.py +0 -0
  312. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/thoughtspot/client/endpoints.py +0 -0
  313. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/thoughtspot/client/pagination.py +0 -0
  314. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/visualization/thoughtspot/extract.py +0 -0
  315. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/__init__.py +0 -0
  316. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/abstract/__init__.py +0 -0
  317. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/abstract/asset.py +0 -0
  318. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/abstract/asset_test.py +0 -0
  319. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/abstract/extract.py +0 -0
  320. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/abstract/query.py +0 -0
  321. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/abstract/time_filter.py +0 -0
  322. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/abstract/time_filter_test.py +0 -0
  323. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/__init__.py +0 -0
  324. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/client.py +0 -0
  325. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/client_test.py +0 -0
  326. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/credentials.py +0 -0
  327. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/extract.py +0 -0
  328. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/queries/.sqlfluff +0 -0
  329. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/queries/column.sql +0 -0
  330. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/queries/cte/sharded.sql +0 -0
  331. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/queries/database.sql +0 -0
  332. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/queries/query.sql +0 -0
  333. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/queries/schema.sql +0 -0
  334. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/queries/table.sql +0 -0
  335. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/queries/table_with_tags.sql +0 -0
  336. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/queries/user.sql +0 -0
  337. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/queries/view_ddl.sql +0 -0
  338. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/query.py +0 -0
  339. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/bigquery/types.py +0 -0
  340. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/__init__.py +0 -0
  341. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/api_client.py +0 -0
  342. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/api_client_test.py +0 -0
  343. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/client.py +0 -0
  344. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/client_test.py +0 -0
  345. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/credentials.py +0 -0
  346. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/endpoints.py +0 -0
  347. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/enums.py +0 -0
  348. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/extract.py +0 -0
  349. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/format.py +0 -0
  350. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/format_test.py +0 -0
  351. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/lineage.py +0 -0
  352. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/lineage_test.py +0 -0
  353. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/pagination.py +0 -0
  354. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/types.py +0 -0
  355. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/utils.py +0 -0
  356. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/databricks/utils_test.py +0 -0
  357. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/mysql/__init__.py +0 -0
  358. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/mysql/client.py +0 -0
  359. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/mysql/client_test.py +0 -0
  360. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/mysql/extract.py +0 -0
  361. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/mysql/queries/.sqlfluff +0 -0
  362. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/mysql/queries/column.sql +0 -0
  363. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/mysql/queries/database.sql +0 -0
  364. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/mysql/queries/query.sql +0 -0
  365. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/mysql/queries/schema.sql +0 -0
  366. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/mysql/queries/table.sql +0 -0
  367. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/mysql/queries/user.sql +0 -0
  368. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/mysql/queries/view_ddl.sql +0 -0
  369. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/mysql/query.py +0 -0
  370. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/postgres/__init__.py +0 -0
  371. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/postgres/extract.py +0 -0
  372. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/postgres/queries/.sqlfluff +0 -0
  373. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/postgres/queries/column.sql +0 -0
  374. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/postgres/queries/database.sql +0 -0
  375. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/postgres/queries/group.sql +0 -0
  376. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/postgres/queries/schema.sql +0 -0
  377. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/postgres/queries/table.sql +0 -0
  378. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/postgres/queries/user.sql +0 -0
  379. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/postgres/query.py +0 -0
  380. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/__init__.py +0 -0
  381. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/client.py +0 -0
  382. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/client_test.py +0 -0
  383. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/extract.py +0 -0
  384. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/extract_test.py +0 -0
  385. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/queries/.sqlfluff +0 -0
  386. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/queries/column.sql +0 -0
  387. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/queries/database.sql +0 -0
  388. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/queries/group.sql +0 -0
  389. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/queries/query.sql +0 -0
  390. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/queries/query_serverless.sql +0 -0
  391. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/queries/schema.sql +0 -0
  392. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/queries/table.sql +0 -0
  393. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/queries/table_freshness.sql +0 -0
  394. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/queries/user.sql +0 -0
  395. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/queries/view_ddl.sql +0 -0
  396. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/redshift/query.py +0 -0
  397. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/salesforce/__init__.py +0 -0
  398. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/salesforce/client.py +0 -0
  399. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/salesforce/constants.py +0 -0
  400. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/salesforce/extract.py +0 -0
  401. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/salesforce/format.py +0 -0
  402. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/salesforce/format_test.py +0 -0
  403. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/salesforce/pagination.py +0 -0
  404. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/salesforce/soql.py +0 -0
  405. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/__init__.py +0 -0
  406. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/client.py +0 -0
  407. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/client_test.py +0 -0
  408. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/credentials.py +0 -0
  409. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/credentials_test.py +0 -0
  410. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/extract.py +0 -0
  411. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/queries/.sqlfluff +0 -0
  412. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/queries/column.sql +0 -0
  413. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/queries/column_lineage.sql +0 -0
  414. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/queries/database.sql +0 -0
  415. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/queries/function.sql +0 -0
  416. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/queries/grant_to_role.sql +0 -0
  417. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/queries/grant_to_user.sql +0 -0
  418. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/queries/query.sql +0 -0
  419. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/queries/role.sql +0 -0
  420. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/queries/schema.sql +0 -0
  421. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/queries/table.sql +0 -0
  422. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/queries/user.sql +0 -0
  423. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/queries/view_ddl.sql +0 -0
  424. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/snowflake/query.py +0 -0
  425. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/sqlserver/__init__.py +0 -0
  426. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/sqlserver/client.py +0 -0
  427. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/sqlserver/extract.py +0 -0
  428. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/sqlserver/queries/.sqlfluff +0 -0
  429. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/sqlserver/queries/column.sql +0 -0
  430. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/sqlserver/queries/database.sql +0 -0
  431. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/sqlserver/queries/schema.sql +0 -0
  432. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/sqlserver/queries/table.sql +0 -0
  433. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/sqlserver/queries/user.sql +0 -0
  434. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/sqlserver/query.py +0 -0
  435. {castor_extractor-0.24.29 → castor_extractor-0.24.32}/castor_extractor/warehouse/synapse/queries/column.sql +0 -0
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.24.32 - 2025-07-02
4
+
5
+ * Salesforce reporting - extract report's metadata
6
+ *
7
+ ## 0.24.31 - 2025-07-02
8
+
9
+ * Looker Studio: add option to list users via a provided JSON file
10
+
11
+ ## 0.24.30 - 2025-06-26
12
+
13
+ * Sigma: remove retry on timeout, decrease pagination for queries
14
+
3
15
  ## 0.24.29 - 2025-06-24
4
16
 
5
17
  * Strategy: skip descriptions on ValueErrors
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: castor-extractor
3
- Version: 0.24.29
3
+ Version: 0.24.32
4
4
  Summary: Extract your metadata assets.
5
5
  Home-page: https://www.castordoc.com/
6
6
  License: EULA
@@ -215,6 +215,18 @@ For any questions or bug report, contact us at [support@coalesce.io](mailto:supp
215
215
 
216
216
  # Changelog
217
217
 
218
+ ## 0.24.32 - 2025-07-02
219
+
220
+ * Salesforce reporting - extract report's metadata
221
+ *
222
+ ## 0.24.31 - 2025-07-02
223
+
224
+ * Looker Studio: add option to list users via a provided JSON file
225
+
226
+ ## 0.24.30 - 2025-06-26
227
+
228
+ * Sigma: remove retry on timeout, decrease pagination for queries
229
+
218
230
  ## 0.24.29 - 2025-06-24
219
231
 
220
232
  * Strategy: skip descriptions on ValueErrors
@@ -30,6 +30,14 @@ def main():
30
30
  default=False,
31
31
  help="Skips the extraction of activity logs",
32
32
  )
33
+ parser.add_argument(
34
+ "--users-file-path",
35
+ help=(
36
+ "Optional path to a JSON file with user email addresses "
37
+ 'as a list of strings (e.g. ["foo@bar.com", "fee@bar.com"]). '
38
+ "If provided, only extracts assets owned by the specified users."
39
+ ),
40
+ )
33
41
 
34
42
  parser.add_argument("-o", "--output", help="Directory to write to")
35
43
 
@@ -36,23 +36,40 @@ class LookerStudioClient:
36
36
  self,
37
37
  credentials: LookerStudioCredentials,
38
38
  bigquery_credentials: Optional[dict] = None,
39
+ user_emails: Optional[list[str]] = None,
39
40
  ):
40
41
  self.admin_sdk_client = AdminSDKClient(credentials)
41
42
  self.looker_studio_client = LookerStudioAPIClient(credentials)
43
+ self.user_emails = user_emails
42
44
 
43
45
  self.bigquery_client: Optional[BigQueryClient] = None
44
46
  if bigquery_credentials:
45
47
  self.bigquery_client = BigQueryClient(bigquery_credentials)
46
48
 
47
- def _get_assets(self) -> Iterator[dict]:
49
+ def _list_user_emails(self) -> Iterator[str]:
48
50
  """
49
- Extracts reports and data sources user by user.
51
+ Lists user emails either from a provided JSON file or via the Admin SDK API.
52
+
53
+ Using all Google Workspace users can be inefficient for large clients -
54
+ the client might spend hours checking thousands of users for Looker Studio
55
+ assets when only a handful actually own any. A JSON file allows
56
+ targeting known owners instead.
50
57
  """
51
- users = self.admin_sdk_client.list_users()
58
+ if self.user_emails is not None:
59
+ yield from self.user_emails
60
+ return
61
+
62
+ for user in self.admin_sdk_client.list_users():
63
+ yield user[USER_EMAIL_FIELD]
52
64
 
53
- for user in users:
54
- email = user[USER_EMAIL_FIELD]
55
- yield from self.looker_studio_client.fetch_user_assets(email)
65
+ def _get_assets(self) -> Iterator[dict]:
66
+ """
67
+ Extracts reports and data sources user by user. The loop is necessary
68
+ because the Looker Studio API can only retrieve the assets owned by a
69
+ single user.
70
+ """
71
+ for user_email in self._list_user_emails():
72
+ yield from self.looker_studio_client.fetch_user_assets(user_email)
56
73
 
57
74
  def _get_source_queries(self) -> Iterator[dict]:
58
75
  """
@@ -70,21 +70,53 @@ def _bigquery_credentials_or_none(params: dict) -> Optional[dict]:
70
70
  return cast(dict, json.load(file))
71
71
 
72
72
 
73
+ def _validate_user_emails(user_emails: list[str]):
74
+ """
75
+ Raises an error if the user emails are not in the expected format (list of strings),
76
+ or if the list is empty.
77
+ """
78
+ if not isinstance(user_emails, list):
79
+ raise TypeError("The users file must be a list")
80
+
81
+ if len(user_emails) == 0:
82
+ raise ValueError("The users file must contain at least one user email")
83
+
84
+ if not all(isinstance(email, str) for email in user_emails):
85
+ raise TypeError("All items in users list must be strings")
86
+
87
+
88
+ def _read_optional_user_emails(
89
+ users_file_path: Optional[str],
90
+ ) -> Optional[list[str]]:
91
+ """Loads the user emails from a file, if it was provided."""
92
+ if not users_file_path:
93
+ return None
94
+
95
+ with open(users_file_path, "r") as file:
96
+ user_emails = json.load(file)
97
+
98
+ _validate_user_emails(user_emails)
99
+ return user_emails
100
+
101
+
73
102
  def extract_all(**kwargs) -> None:
74
103
  """
75
104
  Extracts data from Looker Studio and stores the output files locally under
76
105
  the given output_directory.
77
106
  """
107
+ users_file_path = kwargs.get("users_file_path")
78
108
  output_directory = kwargs.get("output") or from_env(OUTPUT_DIR)
79
109
 
80
110
  credentials = _credentials(kwargs)
81
111
  has_view_activity_logs = bool(credentials.has_view_activity_logs)
112
+ user_emails = _read_optional_user_emails(users_file_path)
82
113
 
83
114
  bigquery_credentials = _bigquery_credentials_or_none(kwargs)
84
115
 
85
116
  client = LookerStudioClient(
86
117
  credentials=credentials,
87
118
  bigquery_credentials=bigquery_credentials,
119
+ user_emails=user_emails,
88
120
  )
89
121
  ts = current_timestamp()
90
122
 
@@ -0,0 +1,19 @@
1
+ import pytest
2
+
3
+ from .extract import _validate_user_emails
4
+
5
+
6
+ def test__validate_user_emails():
7
+ with pytest.raises(TypeError):
8
+ _validate_user_emails("toto@tata.com")
9
+
10
+ with pytest.raises(TypeError):
11
+ _validate_user_emails({"not": "the", "right": "format"})
12
+
13
+ with pytest.raises(ValueError):
14
+ _validate_user_emails([])
15
+
16
+ with pytest.raises(TypeError):
17
+ _validate_user_emails([1, 2, 3, 4])
18
+
19
+ _validate_user_emails(["admin@toto.com", "tata@toto.com"])
@@ -8,4 +8,5 @@ class SalesforceReportingAsset(ExternalAsset):
8
8
  DASHBOARD_COMPONENTS = "dashboard_components"
9
9
  FOLDERS = "folders"
10
10
  REPORTS = "reports"
11
+ REPORTS_METADATA = "reports_metadata"
11
12
  USERS = "users"
@@ -0,0 +1,114 @@
1
+ import logging
2
+ from collections.abc import Iterator
3
+ from concurrent.futures import ThreadPoolExecutor
4
+ from typing import Optional
5
+
6
+ import requests
7
+
8
+ from ....utils import build_url
9
+ from ....utils.salesforce import SalesforceBaseClient
10
+ from ..assets import SalesforceReportingAsset
11
+ from .soql import queries
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ REQUIRING_URL_ASSETS = (
16
+ SalesforceReportingAsset.REPORTS,
17
+ SalesforceReportingAsset.DASHBOARDS,
18
+ SalesforceReportingAsset.FOLDERS,
19
+ )
20
+
21
+ _CONCURRENT_THREADS = 50
22
+
23
+
24
+ class SalesforceReportingClient(SalesforceBaseClient):
25
+ """
26
+ Salesforce Reporting API client
27
+ """
28
+
29
+ def _get_asset_url(
30
+ self, asset_type: SalesforceReportingAsset, asset: dict
31
+ ) -> Optional[str]:
32
+ """
33
+ Fetch the given Asset + add the corresponding URL.
34
+ """
35
+
36
+ if asset_type == SalesforceReportingAsset.DASHBOARDS:
37
+ path = f"lightning/r/Dashboard/{asset['Id']}/view"
38
+ return build_url(self._host, path)
39
+
40
+ if asset_type == SalesforceReportingAsset.FOLDERS:
41
+ path = asset["attributes"]["url"].lstrip("/")
42
+ return build_url(self._host, path)
43
+
44
+ if asset_type == SalesforceReportingAsset.REPORTS:
45
+ path = f"lightning/r/Report/{asset['Id']}/view"
46
+ return build_url(self._host, path)
47
+
48
+ return None
49
+
50
+ def _fetch_and_add_url(
51
+ self, asset_type: SalesforceReportingAsset
52
+ ) -> Iterator[dict]:
53
+ assets = self._query_all(queries[asset_type])
54
+ for asset in assets:
55
+ url = self._get_asset_url(asset_type, asset)
56
+ yield {**asset, "Url": url}
57
+
58
+ def _metadata(self, report_id: str) -> Optional[dict]:
59
+ url = f"services/data/v60.0/analytics/reports/{report_id}/describe"
60
+ try:
61
+ metadata = self._get(url, retry_on_timeout=False)
62
+ # pick only what we need to build the lineage
63
+ columns = metadata["reportExtendedMetadata"]["detailColumnInfo"]
64
+ return {
65
+ "reportId": report_id,
66
+ "detailColumnInfo": columns or dict(),
67
+ }
68
+ except (requests.HTTPError, requests.RequestException) as ex:
69
+ # Extracting column metadata is used only for lineage purposes
70
+ # and is non-critical. API errors are common during this step,
71
+ # so we choose to skip them rather than fail the process. The same
72
+ # rows consistently fail, and retries have proven ineffective.
73
+ logger.info(ex)
74
+ return None
75
+
76
+ def _fetch_reports_metadata(self) -> Iterator[dict]:
77
+ """
78
+ Use the "describe" endpoint to extract report metadata.
79
+ Keep only the detailColumnInfo, which is required for building the lineage.
80
+
81
+ More info here:
82
+ https://developer.salesforce.com/docs/atlas.en-us.api_analytics.meta/api_analytics/sforce_analytics_rest_api_getbasic_reportmetadata.htm
83
+ https://www.notion.so/castordoc/Salesforce-Lineage-216a1c3d458580859888cf4ca2d7fa51?source=copy_link
84
+ """
85
+ # The "describe" endpoint requires report_ids. To avoid introducing
86
+ # task dependencies, we opted to re-extract the reports.
87
+ # It is fast anyway, since it's running a SQL query
88
+ reports = self.fetch(SalesforceReportingAsset.REPORTS)
89
+ report_ids = [report["Id"] for report in reports]
90
+
91
+ # Calling "describe" on each report individually can be slow,
92
+ # especially for accounts with thousands of reports. That's why
93
+ # we use multithreading here — it significantly improves performance.
94
+ with ThreadPoolExecutor(max_workers=_CONCURRENT_THREADS) as executor:
95
+ fetch_results = executor.map(self._metadata, report_ids)
96
+
97
+ for metadata in fetch_results:
98
+ if not metadata:
99
+ continue
100
+ yield metadata
101
+
102
+ def fetch(self, asset: SalesforceReportingAsset) -> list[dict]:
103
+ """
104
+ Fetch Salesforce Reporting assets
105
+ """
106
+ logger.info(f"Starting extraction of {asset}")
107
+
108
+ if asset in REQUIRING_URL_ASSETS:
109
+ return list(self._fetch_and_add_url(asset))
110
+
111
+ if asset == SalesforceReportingAsset.REPORTS_METADATA:
112
+ return list(self._fetch_reports_metadata())
113
+
114
+ return list(self._query_all(queries[asset]))
@@ -2,7 +2,7 @@ from collections.abc import Iterator
2
2
  from concurrent.futures import ThreadPoolExecutor
3
3
  from functools import partial
4
4
  from http import HTTPStatus
5
- from typing import Callable, Optional
5
+ from typing import Callable, Iterable, Optional
6
6
 
7
7
  import requests
8
8
  from pydantic import BaseModel
@@ -19,7 +19,11 @@ from ....utils import (
19
19
  from ..assets import SigmaAsset
20
20
  from .credentials import SigmaCredentials
21
21
  from .endpoints import SigmaEndpointFactory
22
- from .pagination import SIGMA_API_LIMIT, SigmaPagination
22
+ from .pagination import (
23
+ SIGMA_API_LIMIT,
24
+ SIGMA_QUERIES_PAGINATION_LIMIT,
25
+ SigmaPagination,
26
+ )
23
27
 
24
28
  _CONTENT_TYPE = "application/x-www-form-urlencoded"
25
29
 
@@ -101,9 +105,27 @@ class SigmaClient(APIClient):
101
105
  safe_mode=safe_mode or SIGMA_SAFE_MODE,
102
106
  )
103
107
 
104
- def _get_paginated(self, endpoint: str) -> Callable:
108
+ def _get_paginated(
109
+ self,
110
+ endpoint: str,
111
+ limit: int = SIGMA_API_LIMIT,
112
+ ) -> Callable:
113
+ """
114
+ Sigma’s API does not experience random timeouts, unlike some other APIs.
115
+ However, extracting queries from certain workbooks can take a
116
+ significant amount of time.
117
+ Previously, when a timeout occurred, the system would retry multiple
118
+ times — even though we knew it would eventually fail due to the inherent
119
+ slowness of the operation.
120
+ These retries only delayed the inevitable failure without adding value.
121
+ To address this, we've disabled retries on timeout and instead adjusted
122
+ the page size when extracting queries.
123
+ """
105
124
  return partial(
106
- self._get, endpoint=endpoint, params={"limit": SIGMA_API_LIMIT}
125
+ self._get,
126
+ retry_on_timeout=False, # explained in the docstring
127
+ endpoint=endpoint,
128
+ params={"limit": limit},
107
129
  )
108
130
 
109
131
  def _get_all_datasets(self) -> Iterator[dict]:
@@ -200,16 +222,34 @@ class SigmaClient(APIClient):
200
222
  "element_id": lineage.context.element_id,
201
223
  }
202
224
 
225
+ @staticmethod
226
+ def _yield_deduplicated_queries(
227
+ queries: Iterable[dict], workbook_id: str
228
+ ) -> Iterator[dict]:
229
+ """
230
+ Returns unique queries for a workbook. This is necessary because the API
231
+ unfortunately returns duplicate entries for some workbook elements.
232
+ """
233
+ seen_elements = set()
234
+
235
+ for query in queries:
236
+ element_id = query["elementId"]
237
+ if element_id in seen_elements:
238
+ continue
239
+
240
+ seen_elements.add(element_id)
241
+ yield {**query, "workbook_id": workbook_id}
242
+
203
243
  def _get_all_queries(self, workbooks: list[dict]) -> Iterator[dict]:
204
244
  for workbook in workbooks:
205
245
  workbook_id = workbook["workbookId"]
206
246
  request = self._get_paginated(
207
- SigmaEndpointFactory.queries(workbook_id)
247
+ SigmaEndpointFactory.queries(workbook_id),
248
+ limit=SIGMA_QUERIES_PAGINATION_LIMIT,
208
249
  )
209
250
  queries = fetch_all_pages(request, SigmaPagination)
210
251
 
211
- for query in queries:
212
- yield {**query, "workbook_id": workbook_id}
252
+ yield from self._yield_deduplicated_queries(queries, workbook_id)
213
253
 
214
254
  def fetch(
215
255
  self,
@@ -0,0 +1,19 @@
1
+ from .client import SigmaClient
2
+
3
+
4
+ def test_SigmaClient__yield_deduplicated_queries():
5
+ workbook_id = "workbook1"
6
+ mock_queries = [
7
+ {"elementId": "element1", "name": "Query 1"},
8
+ {"elementId": "element2", "name": "Query 2"},
9
+ {"elementId": "element1", "name": "Query 1"}, # Duplicate
10
+ {"elementId": "element3", "name": "Query 3"},
11
+ ]
12
+
13
+ queries = list(
14
+ SigmaClient._yield_deduplicated_queries(mock_queries, workbook_id)
15
+ )
16
+
17
+ assert len(queries) == 3
18
+ for query in queries:
19
+ assert query["workbook_id"] == workbook_id
@@ -6,6 +6,7 @@ from pydantic.alias_generators import to_camel
6
6
  from ....utils import PaginationModel
7
7
 
8
8
  SIGMA_API_LIMIT = 200 # default number of records per page
9
+ SIGMA_QUERIES_PAGINATION_LIMIT = 50
9
10
 
10
11
 
11
12
  class SigmaPagination(PaginationModel):
@@ -1,7 +1,6 @@
1
1
  import logging
2
2
  from collections import defaultdict
3
3
  from datetime import date
4
- from typing import Optional
5
4
 
6
5
  from databricks import sql # type: ignore
7
6
 
@@ -17,7 +16,7 @@ _INFORMATION_SCHEMA_SQL = "SELECT * FROM system.information_schema"
17
16
 
18
17
  _LINEAGE_SQL_TPL = """
19
18
  SELECT * FROM system.access.{table_name}
20
- WHERE event_date = :day
19
+ WHERE event_date = DATE('{day}')
21
20
  """
22
21
 
23
22
 
@@ -34,11 +33,7 @@ class DatabricksSQLClient:
34
33
  self._host = credentials.host
35
34
  self._token = credentials.token
36
35
 
37
- def execute_sql(
38
- self,
39
- query: str,
40
- params: Optional[dict] = None,
41
- ):
36
+ def execute_sql(self, query: str):
42
37
  """
43
38
  Execute a SQL query on Databricks system tables and return the results.
44
39
  https://docs.databricks.com/en/dev-tools/python-sql-connector.html
@@ -52,7 +47,7 @@ class DatabricksSQLClient:
52
47
  access_token=self._token,
53
48
  ) as connection:
54
49
  with connection.cursor() as cursor:
55
- cursor.execute(query, params)
50
+ cursor.execute(query)
56
51
  return cursor.fetchall()
57
52
 
58
53
  def _needs_extraction(self, entity: TagEntity) -> bool:
@@ -89,16 +84,23 @@ class DatabricksSQLClient:
89
84
  return mapping
90
85
 
91
86
  def get_lineage(
92
- self, lineage_entity: LineageEntity, day: date
87
+ self,
88
+ lineage_entity: LineageEntity,
89
+ day: date,
93
90
  ) -> list[dict]:
94
91
  """
95
92
  Fetch {TABLE|COLUMN} lineage of the given day, via system tables
96
93
  https://docs.databricks.com/en/admin/system-tables/lineage.html
94
+
95
+ Unfortunately, passing parameters is not always supported. We have to
96
+ format the query beforehand and pass it as plain text for execution.
97
97
  """
98
98
  table_name = f"{lineage_entity.value.lower()}_lineage"
99
- query = _LINEAGE_SQL_TPL.format(table_name=table_name)
100
- params = {"day": day}
101
- result = self.execute_sql(query, params)
99
+ query = _LINEAGE_SQL_TPL.format(
100
+ table_name=table_name,
101
+ day=day,
102
+ )
103
+ result = self.execute_sql(query)
102
104
  data = []
103
105
  for row in result:
104
106
  data.append(row.asDict())
@@ -3,7 +3,7 @@ requires = ["setuptools>=61.2"]
3
3
 
4
4
  [tool.poetry]
5
5
  name = "castor-extractor"
6
- version = "0.24.29"
6
+ version = "0.24.32"
7
7
  description = "Extract your metadata assets."
8
8
  authors = ["Castor <support@castordoc.com>"]
9
9
  license = "EULA"
@@ -1,62 +0,0 @@
1
- import logging
2
- from collections.abc import Iterator
3
- from typing import Optional
4
-
5
- from ....utils import build_url
6
- from ....utils.salesforce import SalesforceBaseClient
7
- from ..assets import SalesforceReportingAsset
8
- from .soql import queries
9
-
10
- logger = logging.getLogger(__name__)
11
-
12
- REQUIRING_URL_ASSETS = (
13
- SalesforceReportingAsset.REPORTS,
14
- SalesforceReportingAsset.DASHBOARDS,
15
- SalesforceReportingAsset.FOLDERS,
16
- )
17
-
18
-
19
- class SalesforceReportingClient(SalesforceBaseClient):
20
- """
21
- Salesforce Reporting API client
22
- """
23
-
24
- def _get_asset_url(
25
- self, asset_type: SalesforceReportingAsset, asset: dict
26
- ) -> Optional[str]:
27
- """
28
- Fetch the given Asset + add the corresponding URL.
29
- """
30
-
31
- if asset_type == SalesforceReportingAsset.DASHBOARDS:
32
- path = f"lightning/r/Dashboard/{asset['Id']}/view"
33
- return build_url(self._host, path)
34
-
35
- if asset_type == SalesforceReportingAsset.FOLDERS:
36
- path = asset["attributes"]["url"].lstrip("/")
37
- return build_url(self._host, path)
38
-
39
- if asset_type == SalesforceReportingAsset.REPORTS:
40
- path = f"lightning/r/Report/{asset['Id']}/view"
41
- return build_url(self._host, path)
42
-
43
- return None
44
-
45
- def _fetch_and_add_url(
46
- self, asset_type: SalesforceReportingAsset
47
- ) -> Iterator[dict]:
48
- assets = self._query_all(queries[asset_type])
49
- for asset in assets:
50
- url = self._get_asset_url(asset_type, asset)
51
- yield {**asset, "Url": url}
52
-
53
- def fetch(self, asset: SalesforceReportingAsset) -> list[dict]:
54
- """
55
- Fetch Salesforce Reporting assets
56
- """
57
- logger.info(f"Starting extraction of {asset}")
58
-
59
- if asset in REQUIRING_URL_ASSETS:
60
- return list(self._fetch_and_add_url(asset))
61
-
62
- return list(self._query_all(queries[asset]))