castor-extractor 0.24.29__tar.gz → 0.24.33__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.33}/CHANGELOG.md +16 -0
  2. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/PKG-INFO +17 -1
  3. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_looker_studio.py +8 -0
  4. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_tableau.py +15 -0
  5. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker_studio/client/client.py +23 -6
  6. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker_studio/extract.py +32 -0
  7. castor_extractor-0.24.33/castor_extractor/visualization/looker_studio/extract_test.py +19 -0
  8. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/salesforce_reporting/assets.py +1 -0
  9. castor_extractor-0.24.33/castor_extractor/visualization/salesforce_reporting/client/rest.py +114 -0
  10. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/sigma/client/client.py +47 -7
  11. castor_extractor-0.24.33/castor_extractor/visualization/sigma/client/client_test.py +19 -0
  12. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/sigma/client/pagination.py +1 -0
  13. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/tableau/client/client.py +9 -1
  14. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/tableau/client/client_metadata_api.py +49 -11
  15. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/tableau/extract.py +4 -0
  16. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/sql_client.py +14 -12
  17. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/pyproject.toml +1 -1
  18. castor_extractor-0.24.29/castor_extractor/visualization/salesforce_reporting/client/rest.py +0 -62
  19. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/Dockerfile +0 -0
  20. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/DockerfileUsage.md +0 -0
  21. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/LICENCE +0 -0
  22. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/README.md +0 -0
  23. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/__init__.py +0 -0
  24. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/__init__.py +0 -0
  25. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_bigquery.py +0 -0
  26. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_confluence.py +0 -0
  27. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_databricks.py +0 -0
  28. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_domo.py +0 -0
  29. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_looker.py +0 -0
  30. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_metabase_api.py +0 -0
  31. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_metabase_db.py +0 -0
  32. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_mode.py +0 -0
  33. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_mysql.py +0 -0
  34. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_notion.py +0 -0
  35. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_postgres.py +0 -0
  36. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_powerbi.py +0 -0
  37. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_qlik.py +0 -0
  38. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_redshift.py +0 -0
  39. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_salesforce.py +0 -0
  40. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_salesforce_reporting.py +0 -0
  41. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_sigma.py +0 -0
  42. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_snowflake.py +0 -0
  43. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_sqlserver.py +0 -0
  44. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_strategy.py +0 -0
  45. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/extract_thoughtspot.py +0 -0
  46. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/file_check.py +0 -0
  47. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/commands/upload.py +0 -0
  48. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/file_checker/__init__.py +0 -0
  49. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/file_checker/column.py +0 -0
  50. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/file_checker/column_test.py +0 -0
  51. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/file_checker/constants.py +0 -0
  52. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/file_checker/enums.py +0 -0
  53. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/file_checker/file.py +0 -0
  54. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/file_checker/file_test.py +0 -0
  55. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/file_checker/file_test_users.csv +0 -0
  56. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/file_checker/file_test_users_valid.csv +0 -0
  57. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/file_checker/templates/__init__.py +0 -0
  58. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/file_checker/templates/generic_warehouse.py +0 -0
  59. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/__init__.py +0 -0
  60. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/confluence/__init__.py +0 -0
  61. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/confluence/assets.py +0 -0
  62. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/confluence/client/__init__.py +0 -0
  63. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/confluence/client/client.py +0 -0
  64. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/confluence/client/client_test.py +0 -0
  65. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/confluence/client/credentials.py +0 -0
  66. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/confluence/client/endpoints.py +0 -0
  67. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/confluence/client/pagination.py +0 -0
  68. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/confluence/extract.py +0 -0
  69. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/confluence/utils.py +0 -0
  70. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/confluence/utils_test.py +0 -0
  71. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/notion/__init__.py +0 -0
  72. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/notion/assets.py +0 -0
  73. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/notion/client/__init__.py +0 -0
  74. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/notion/client/client.py +0 -0
  75. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/notion/client/client_test.py +0 -0
  76. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/notion/client/constants.py +0 -0
  77. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/notion/client/credentials.py +0 -0
  78. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/notion/client/endpoints.py +0 -0
  79. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/notion/client/pagination.py +0 -0
  80. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/knowledge/notion/extract.py +0 -0
  81. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/logger.py +0 -0
  82. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/quality/__init__.py +0 -0
  83. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/quality/soda/__init__.py +0 -0
  84. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/quality/soda/assets.py +0 -0
  85. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/quality/soda/client/__init__.py +0 -0
  86. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/quality/soda/client/client.py +0 -0
  87. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/quality/soda/client/credentials.py +0 -0
  88. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/quality/soda/client/endpoints.py +0 -0
  89. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/quality/soda/client/pagination.py +0 -0
  90. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/transformation/__init__.py +0 -0
  91. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/transformation/coalesce/__init__.py +0 -0
  92. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/transformation/coalesce/assets.py +0 -0
  93. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/transformation/coalesce/client/__init__.py +0 -0
  94. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/transformation/coalesce/client/client.py +0 -0
  95. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/transformation/coalesce/client/credentials.py +0 -0
  96. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/transformation/coalesce/client/endpoint.py +0 -0
  97. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/transformation/coalesce/client/type.py +0 -0
  98. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/transformation/coalesce/client/utils.py +0 -0
  99. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/transformation/coalesce/client/utils_test.py +0 -0
  100. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/transformation/dbt/__init__.py +0 -0
  101. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/transformation/dbt/assets.py +0 -0
  102. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/transformation/dbt/client.py +0 -0
  103. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/transformation/dbt/client_test.py +0 -0
  104. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/transformation/dbt/credentials.py +0 -0
  105. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/types.py +0 -0
  106. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/uploader/__init__.py +0 -0
  107. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/uploader/constant.py +0 -0
  108. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/uploader/env.py +0 -0
  109. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/uploader/env_test.py +0 -0
  110. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/uploader/settings.py +0 -0
  111. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/uploader/upload.py +0 -0
  112. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/uploader/upload_test.py +0 -0
  113. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/uploader/utils.py +0 -0
  114. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/__init__.py +0 -0
  115. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/argument_parser.py +0 -0
  116. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/argument_parser_test.py +0 -0
  117. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/batch.py +0 -0
  118. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/batch_test.py +0 -0
  119. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/__init__.py +0 -0
  120. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/abstract.py +0 -0
  121. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/api/__init__.py +0 -0
  122. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/api/auth.py +0 -0
  123. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/api/auth_test.py +0 -0
  124. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/api/client.py +0 -0
  125. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/api/client_test.py +0 -0
  126. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/api/pagination.py +0 -0
  127. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/api/pagination_test.py +0 -0
  128. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/api/safe_request.py +0 -0
  129. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/api/safe_request_test.py +0 -0
  130. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/api/utils.py +0 -0
  131. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/api/utils_test.py +0 -0
  132. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/postgres.py +0 -0
  133. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/query.py +0 -0
  134. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/uri.py +0 -0
  135. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/client/uri_test.py +0 -0
  136. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/collection.py +0 -0
  137. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/collection_test.py +0 -0
  138. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/constants.py +0 -0
  139. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/deprecate.py +0 -0
  140. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/env.py +0 -0
  141. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/files.py +0 -0
  142. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/files_test.py +0 -0
  143. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/formatter.py +0 -0
  144. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/formatter_test.csv +0 -0
  145. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/formatter_test.json +0 -0
  146. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/formatter_test.py +0 -0
  147. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/json_stream_write.py +0 -0
  148. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/load.py +0 -0
  149. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/object.py +0 -0
  150. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/object_test.py +0 -0
  151. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/pager/__init__.py +0 -0
  152. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/pager/pager.py +0 -0
  153. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/pager/pager_on_id.py +0 -0
  154. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/pager/pager_on_id_test.py +0 -0
  155. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/pager/pager_test.py +0 -0
  156. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/retry.py +0 -0
  157. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/retry_test.py +0 -0
  158. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/safe.py +0 -0
  159. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/safe_test.py +0 -0
  160. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/salesforce/__init__.py +0 -0
  161. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/salesforce/client.py +0 -0
  162. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/salesforce/client_test.py +0 -0
  163. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/salesforce/constants.py +0 -0
  164. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/salesforce/credentials.py +0 -0
  165. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/salesforce/credentials_test.py +0 -0
  166. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/salesforce/pagination.py +0 -0
  167. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/store.py +0 -0
  168. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/string.py +0 -0
  169. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/string_test.py +0 -0
  170. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/time.py +0 -0
  171. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/time_test.py +0 -0
  172. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/type.py +0 -0
  173. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/url.py +0 -0
  174. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/url_test.py +0 -0
  175. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/validation.py +0 -0
  176. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/validation_test.py +0 -0
  177. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/utils/write.py +0 -0
  178. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/__init__.py +0 -0
  179. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/domo/__init__.py +0 -0
  180. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/domo/assets.py +0 -0
  181. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/domo/client/__init__.py +0 -0
  182. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/domo/client/client.py +0 -0
  183. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/domo/client/credentials.py +0 -0
  184. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/domo/client/endpoints.py +0 -0
  185. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/domo/client/pagination.py +0 -0
  186. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/domo/client/pagination_test.py +0 -0
  187. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/domo/extract.py +0 -0
  188. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/__init__.py +0 -0
  189. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/api/__init__.py +0 -0
  190. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/api/client.py +0 -0
  191. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/api/client_test.py +0 -0
  192. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/api/constants.py +0 -0
  193. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/api/credentials.py +0 -0
  194. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/api/extraction_parameters.py +0 -0
  195. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/api/sdk.py +0 -0
  196. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/api/sdk_test.py +0 -0
  197. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/api/utils.py +0 -0
  198. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/assets.py +0 -0
  199. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/constant.py +0 -0
  200. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/constants.py +0 -0
  201. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/extract.py +0 -0
  202. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/fields.py +0 -0
  203. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/fields_test.py +0 -0
  204. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker/multithreading.py +0 -0
  205. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker_studio/__init__.py +0 -0
  206. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker_studio/assets.py +0 -0
  207. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker_studio/client/__init__.py +0 -0
  208. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker_studio/client/admin_sdk_client.py +0 -0
  209. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker_studio/client/credentials.py +0 -0
  210. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker_studio/client/endpoints.py +0 -0
  211. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker_studio/client/enums.py +0 -0
  212. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker_studio/client/looker_studio_api_client.py +0 -0
  213. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker_studio/client/pagination.py +0 -0
  214. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/looker_studio/client/queries/query.sql +0 -0
  215. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/__init__.py +0 -0
  216. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/assets.py +0 -0
  217. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/__init__.py +0 -0
  218. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/api/__init__.py +0 -0
  219. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/api/client.py +0 -0
  220. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/api/client_test.py +0 -0
  221. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/api/credentials.py +0 -0
  222. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/db/__init__.py +0 -0
  223. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/db/client.py +0 -0
  224. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/db/credentials.py +0 -0
  225. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/db/queries/.sqlfluff +0 -0
  226. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/db/queries/base_url.sql +0 -0
  227. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/db/queries/card.sql +0 -0
  228. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/db/queries/collection.sql +0 -0
  229. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/db/queries/dashboard.sql +0 -0
  230. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/db/queries/dashboard_cards.sql +0 -0
  231. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/db/queries/database.sql +0 -0
  232. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/db/queries/table.sql +0 -0
  233. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/db/queries/user.sql +0 -0
  234. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/decryption.py +0 -0
  235. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/decryption_test.py +0 -0
  236. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/client/shared.py +0 -0
  237. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/errors.py +0 -0
  238. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/extract.py +0 -0
  239. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/metabase/types.py +0 -0
  240. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/mode/__init__.py +0 -0
  241. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/mode/assets.py +0 -0
  242. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/mode/client/__init__.py +0 -0
  243. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/mode/client/client.py +0 -0
  244. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/mode/client/client_test.json +0 -0
  245. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/mode/client/client_test.py +0 -0
  246. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/mode/client/constants.py +0 -0
  247. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/mode/client/credentials.py +0 -0
  248. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/mode/errors.py +0 -0
  249. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/mode/extract.py +0 -0
  250. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/powerbi/__init__.py +0 -0
  251. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/powerbi/assets.py +0 -0
  252. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/powerbi/client/__init__.py +0 -0
  253. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/powerbi/client/authentication.py +0 -0
  254. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/powerbi/client/client.py +0 -0
  255. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/powerbi/client/client_test.py +0 -0
  256. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/powerbi/client/constants.py +0 -0
  257. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/powerbi/client/credentials.py +0 -0
  258. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/powerbi/client/credentials_test.py +0 -0
  259. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/powerbi/client/endpoints.py +0 -0
  260. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/powerbi/client/pagination.py +0 -0
  261. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/powerbi/extract.py +0 -0
  262. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/__init__.py +0 -0
  263. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/assets.py +0 -0
  264. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/client/__init__.py +0 -0
  265. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/client/constants.py +0 -0
  266. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/client/engine/__init__.py +0 -0
  267. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/client/engine/client.py +0 -0
  268. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/client/engine/constants.py +0 -0
  269. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/client/engine/credentials.py +0 -0
  270. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/client/engine/error.py +0 -0
  271. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/client/engine/error_test.py +0 -0
  272. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/client/engine/json_rpc.py +0 -0
  273. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/client/engine/json_rpc_test.py +0 -0
  274. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/client/engine/websocket.py +0 -0
  275. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/client/master.py +0 -0
  276. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/client/rest.py +0 -0
  277. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/client/rest_test.py +0 -0
  278. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/qlik/extract.py +0 -0
  279. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/salesforce_reporting/__init__.py +0 -0
  280. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/salesforce_reporting/client/__init__.py +0 -0
  281. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/salesforce_reporting/client/soql.py +0 -0
  282. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/salesforce_reporting/extract.py +0 -0
  283. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/sigma/__init__.py +0 -0
  284. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/sigma/assets.py +0 -0
  285. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/sigma/client/__init__.py +0 -0
  286. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/sigma/client/credentials.py +0 -0
  287. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/sigma/client/endpoints.py +0 -0
  288. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/sigma/extract.py +0 -0
  289. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/strategy/__init__.py +0 -0
  290. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/strategy/assets.py +0 -0
  291. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/strategy/client/__init__.py +0 -0
  292. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/strategy/client/client.py +0 -0
  293. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/strategy/client/credentials.py +0 -0
  294. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/strategy/client/properties.py +0 -0
  295. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/strategy/extract.py +0 -0
  296. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/tableau/__init__.py +0 -0
  297. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/tableau/assets.py +0 -0
  298. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/tableau/client/__init__.py +0 -0
  299. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/tableau/client/client_metadata_api_test.py +0 -0
  300. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/tableau/client/client_rest_api.py +0 -0
  301. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/tableau/client/client_tsc.py +0 -0
  302. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/tableau/client/credentials.py +0 -0
  303. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/tableau/client/errors.py +0 -0
  304. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/tableau/client/gql_queries.py +0 -0
  305. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/tableau/client/rest_fields.py +0 -0
  306. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/tableau/constants.py +0 -0
  307. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/thoughtspot/__init__.py +0 -0
  308. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/thoughtspot/assets.py +0 -0
  309. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/thoughtspot/client/__init__.py +0 -0
  310. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/thoughtspot/client/client.py +0 -0
  311. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/thoughtspot/client/credentials.py +0 -0
  312. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/thoughtspot/client/endpoints.py +0 -0
  313. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/thoughtspot/client/pagination.py +0 -0
  314. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/visualization/thoughtspot/extract.py +0 -0
  315. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/__init__.py +0 -0
  316. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/abstract/__init__.py +0 -0
  317. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/abstract/asset.py +0 -0
  318. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/abstract/asset_test.py +0 -0
  319. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/abstract/extract.py +0 -0
  320. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/abstract/query.py +0 -0
  321. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/abstract/time_filter.py +0 -0
  322. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/abstract/time_filter_test.py +0 -0
  323. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/__init__.py +0 -0
  324. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/client.py +0 -0
  325. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/client_test.py +0 -0
  326. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/credentials.py +0 -0
  327. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/extract.py +0 -0
  328. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/queries/.sqlfluff +0 -0
  329. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/queries/column.sql +0 -0
  330. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/queries/cte/sharded.sql +0 -0
  331. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/queries/database.sql +0 -0
  332. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/queries/query.sql +0 -0
  333. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/queries/schema.sql +0 -0
  334. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/queries/table.sql +0 -0
  335. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/queries/table_with_tags.sql +0 -0
  336. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/queries/user.sql +0 -0
  337. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/queries/view_ddl.sql +0 -0
  338. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/query.py +0 -0
  339. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/bigquery/types.py +0 -0
  340. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/__init__.py +0 -0
  341. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/api_client.py +0 -0
  342. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/api_client_test.py +0 -0
  343. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/client.py +0 -0
  344. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/client_test.py +0 -0
  345. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/credentials.py +0 -0
  346. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/endpoints.py +0 -0
  347. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/enums.py +0 -0
  348. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/extract.py +0 -0
  349. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/format.py +0 -0
  350. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/format_test.py +0 -0
  351. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/lineage.py +0 -0
  352. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/lineage_test.py +0 -0
  353. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/pagination.py +0 -0
  354. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/types.py +0 -0
  355. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/utils.py +0 -0
  356. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/databricks/utils_test.py +0 -0
  357. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/mysql/__init__.py +0 -0
  358. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/mysql/client.py +0 -0
  359. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/mysql/client_test.py +0 -0
  360. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/mysql/extract.py +0 -0
  361. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/mysql/queries/.sqlfluff +0 -0
  362. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/mysql/queries/column.sql +0 -0
  363. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/mysql/queries/database.sql +0 -0
  364. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/mysql/queries/query.sql +0 -0
  365. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/mysql/queries/schema.sql +0 -0
  366. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/mysql/queries/table.sql +0 -0
  367. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/mysql/queries/user.sql +0 -0
  368. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/mysql/queries/view_ddl.sql +0 -0
  369. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/mysql/query.py +0 -0
  370. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/postgres/__init__.py +0 -0
  371. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/postgres/extract.py +0 -0
  372. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/postgres/queries/.sqlfluff +0 -0
  373. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/postgres/queries/column.sql +0 -0
  374. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/postgres/queries/database.sql +0 -0
  375. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/postgres/queries/group.sql +0 -0
  376. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/postgres/queries/schema.sql +0 -0
  377. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/postgres/queries/table.sql +0 -0
  378. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/postgres/queries/user.sql +0 -0
  379. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/postgres/query.py +0 -0
  380. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/__init__.py +0 -0
  381. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/client.py +0 -0
  382. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/client_test.py +0 -0
  383. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/extract.py +0 -0
  384. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/extract_test.py +0 -0
  385. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/queries/.sqlfluff +0 -0
  386. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/queries/column.sql +0 -0
  387. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/queries/database.sql +0 -0
  388. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/queries/group.sql +0 -0
  389. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/queries/query.sql +0 -0
  390. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/queries/query_serverless.sql +0 -0
  391. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/queries/schema.sql +0 -0
  392. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/queries/table.sql +0 -0
  393. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/queries/table_freshness.sql +0 -0
  394. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/queries/user.sql +0 -0
  395. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/queries/view_ddl.sql +0 -0
  396. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/redshift/query.py +0 -0
  397. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/salesforce/__init__.py +0 -0
  398. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/salesforce/client.py +0 -0
  399. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/salesforce/constants.py +0 -0
  400. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/salesforce/extract.py +0 -0
  401. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/salesforce/format.py +0 -0
  402. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/salesforce/format_test.py +0 -0
  403. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/salesforce/pagination.py +0 -0
  404. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/salesforce/soql.py +0 -0
  405. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/__init__.py +0 -0
  406. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/client.py +0 -0
  407. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/client_test.py +0 -0
  408. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/credentials.py +0 -0
  409. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/credentials_test.py +0 -0
  410. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/extract.py +0 -0
  411. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/queries/.sqlfluff +0 -0
  412. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/queries/column.sql +0 -0
  413. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/queries/column_lineage.sql +0 -0
  414. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/queries/database.sql +0 -0
  415. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/queries/function.sql +0 -0
  416. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/queries/grant_to_role.sql +0 -0
  417. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/queries/grant_to_user.sql +0 -0
  418. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/queries/query.sql +0 -0
  419. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/queries/role.sql +0 -0
  420. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/queries/schema.sql +0 -0
  421. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/queries/table.sql +0 -0
  422. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/queries/user.sql +0 -0
  423. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/queries/view_ddl.sql +0 -0
  424. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/snowflake/query.py +0 -0
  425. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/sqlserver/__init__.py +0 -0
  426. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/sqlserver/client.py +0 -0
  427. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/sqlserver/extract.py +0 -0
  428. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/sqlserver/queries/.sqlfluff +0 -0
  429. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/sqlserver/queries/column.sql +0 -0
  430. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/sqlserver/queries/database.sql +0 -0
  431. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/sqlserver/queries/schema.sql +0 -0
  432. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/sqlserver/queries/table.sql +0 -0
  433. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/sqlserver/queries/user.sql +0 -0
  434. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/sqlserver/query.py +0 -0
  435. {castor_extractor-0.24.29 → castor_extractor-0.24.33}/castor_extractor/warehouse/synapse/queries/column.sql +0 -0
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.24.33 - 2025-07-10
4
+
5
+ * Tableau - Add an option to skip fields ingestion
6
+
7
+ ## 0.24.32 - 2025-07-02
8
+
9
+ * Salesforce reporting - extract report's metadata
10
+
11
+ ## 0.24.31 - 2025-07-02
12
+
13
+ * Looker Studio: add option to list users via a provided JSON file
14
+
15
+ ## 0.24.30 - 2025-06-26
16
+
17
+ * Sigma: remove retry on timeout, decrease pagination for queries
18
+
3
19
  ## 0.24.29 - 2025-06-24
4
20
 
5
21
  * 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.33
4
4
  Summary: Extract your metadata assets.
5
5
  Home-page: https://www.castordoc.com/
6
6
  License: EULA
@@ -215,6 +215,22 @@ For any questions or bug report, contact us at [support@coalesce.io](mailto:supp
215
215
 
216
216
  # Changelog
217
217
 
218
+ ## 0.24.33 - 2025-07-10
219
+
220
+ * Tableau - Add an option to skip fields ingestion
221
+
222
+ ## 0.24.32 - 2025-07-02
223
+
224
+ * Salesforce reporting - extract report's metadata
225
+
226
+ ## 0.24.31 - 2025-07-02
227
+
228
+ * Looker Studio: add option to list users via a provided JSON file
229
+
230
+ ## 0.24.30 - 2025-06-26
231
+
232
+ * Sigma: remove retry on timeout, decrease pagination for queries
233
+
218
234
  ## 0.24.29 - 2025-06-24
219
235
 
220
236
  * 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
 
@@ -28,6 +28,13 @@ def main():
28
28
  help="Option to avoid extracting Tableau columns, default to False",
29
29
  )
30
30
 
31
+ parser.add_argument(
32
+ "--skip-fields",
33
+ dest="skip_fields",
34
+ action="store_true",
35
+ help="Option to avoid extracting Tableau fields, default to False",
36
+ )
37
+
31
38
  parser.add_argument(
32
39
  "--with-pulse",
33
40
  dest="with_pulse",
@@ -41,6 +48,14 @@ def main():
41
48
  required=False,
42
49
  )
43
50
 
51
+ parser.add_argument(
52
+ "-ie",
53
+ "--ignore-errors",
54
+ action="store_true",
55
+ dest="ignore_errors",
56
+ help="Allow partial extraction of Fields and Columns: skip batch in case of Timeout errors",
57
+ )
58
+
44
59
  parser.add_argument("-o", "--output", help="Directory to write to")
45
60
 
46
61
  tableau.extract_all(**parse_filled_arguments(parser))
@@ -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):
@@ -122,13 +122,17 @@ class TableauClient:
122
122
  credentials: TableauCredentials,
123
123
  timeout_sec: int = DEFAULT_TIMEOUT_SECONDS,
124
124
  with_columns: bool = True,
125
+ with_fields: bool = True,
125
126
  with_pulse: bool = False,
126
127
  override_page_size: Optional[int] = None,
128
+ ignore_errors: bool = False,
127
129
  ):
128
130
  self._credentials = credentials
129
131
  self._server = _server(credentials.server_url, timeout_sec)
130
132
  self._with_columns = with_columns
133
+ self._with_fields = with_fields
131
134
  self._with_pulse = with_pulse
135
+ self._ignore_errors = ignore_errors
132
136
 
133
137
  self._client_metadata = TableauClientMetadataApi(
134
138
  server=self._server,
@@ -221,6 +225,10 @@ class TableauClient:
221
225
  logger.info(f"Skipping asset {asset} - deactivated columns")
222
226
  return []
223
227
 
228
+ if asset == TableauAsset.FIELD and not self._with_fields:
229
+ logger.info(f"Skipping asset {asset} - deactivated fields")
230
+ return []
231
+
224
232
  logger.info(f"Extracting {asset.name}...")
225
233
 
226
234
  if asset == TableauAsset.DATASOURCE:
@@ -240,4 +248,4 @@ class TableauClient:
240
248
  return self._client_rest.fetch(asset)
241
249
 
242
250
  # other assets can be extracted via Metadata API
243
- return self._client_metadata.fetch(asset)
251
+ return self._client_metadata.fetch(asset, self._ignore_errors)
@@ -2,6 +2,7 @@ import logging
2
2
  from collections.abc import Iterator
3
3
  from typing import Optional
4
4
 
5
+ import requests
5
6
  import tableauserverclient as TSC # type: ignore
6
7
 
7
8
  from ....utils import SerializedAsset, retry
@@ -12,6 +13,13 @@ from .gql_queries import FIELDS_QUERIES, GQL_QUERIES, QUERY_TEMPLATE
12
13
 
13
14
  logger = logging.getLogger(__name__)
14
15
 
16
+ # These assets are known to be error-prone, so it's acceptable if a few are missed.
17
+ # If errors occur, skip the current batch.
18
+ _SAFE_MODE_ASSETS = (
19
+ TableauAsset.COLUMN,
20
+ TableauAsset.FIELD,
21
+ )
22
+
15
23
  # increase the value when extraction is too slow
16
24
  # decrease the value when timeouts arise
17
25
  _CUSTOM_PAGE_SIZE: dict[TableauAsset, int] = {
@@ -92,6 +100,7 @@ def gql_query_scroll(
92
100
  resource: str,
93
101
  fields: str,
94
102
  page_size: int,
103
+ skip_batch: bool,
95
104
  ) -> Iterator[SerializedAsset]:
96
105
  """
97
106
  Iterate over GQL query results, handling pagination and cursor
@@ -119,15 +128,22 @@ def gql_query_scroll(
119
128
 
120
129
  current_offset = 0
121
130
  while True:
122
- payload = _call(first=page_size, offset=current_offset)
123
- yield payload["nodes"]
131
+ try:
132
+ payload = _call(first=page_size, offset=current_offset)
133
+ yield payload["nodes"]
134
+
135
+ current_offset += len(payload["nodes"])
136
+ total = payload["totalCount"]
137
+ logger.info(f"Extracted {current_offset}/{total} {resource}")
124
138
 
125
- current_offset += len(payload["nodes"])
126
- total = payload["totalCount"]
127
- logger.info(f"Extracted {current_offset}/{total} {resource}")
139
+ if not payload["pageInfo"]["hasNextPage"]:
140
+ break
141
+ except requests.exceptions.ReadTimeout:
142
+ if not skip_batch:
143
+ raise
128
144
 
129
- if not payload["pageInfo"]["hasNextPage"]:
130
- break
145
+ logger.warning("Skipping batch because of TableauServer Timeout")
146
+ current_offset += page_size
131
147
 
132
148
 
133
149
  def _deduplicate(result_pages: Iterator[SerializedAsset]) -> SerializedAsset:
@@ -177,12 +193,14 @@ class TableauClientMetadataApi:
177
193
  resource: str,
178
194
  fields: str,
179
195
  page_size: int = DEFAULT_PAGE_SIZE,
196
+ skip_batch: bool = False,
180
197
  ) -> SerializedAsset:
181
198
  result_pages = gql_query_scroll(
182
199
  self._server,
183
200
  resource=resource,
184
201
  fields=fields,
185
202
  page_size=page_size,
203
+ skip_batch=skip_batch,
186
204
  )
187
205
  return _deduplicate(result_pages)
188
206
 
@@ -193,21 +211,41 @@ class TableauClientMetadataApi:
193
211
  or DEFAULT_PAGE_SIZE
194
212
  )
195
213
 
196
- def _fetch_fields(self) -> SerializedAsset:
214
+ def _fetch_fields(self, skip_batch: bool = False) -> SerializedAsset:
197
215
  result: SerializedAsset = []
198
216
  page_size = self._page_size(TableauAsset.FIELD)
199
217
  for resource, fields in FIELDS_QUERIES:
200
- current = self._call(resource, fields, page_size)
218
+ current = self._call(
219
+ resource,
220
+ fields,
221
+ page_size,
222
+ skip_batch=skip_batch,
223
+ )
201
224
  result.extend(current)
202
225
  return result
203
226
 
227
+ @staticmethod
228
+ def _should_skip_batch_with_timeout(
229
+ asset: TableauAsset,
230
+ ignore_metadata_errors: bool = False,
231
+ ) -> bool:
232
+ return asset in _SAFE_MODE_ASSETS and ignore_metadata_errors
233
+
204
234
  def fetch(
205
235
  self,
206
236
  asset: TableauAsset,
237
+ ignore_errors: bool = False,
207
238
  ) -> SerializedAsset:
239
+ skip_batch = self._should_skip_batch_with_timeout(asset, ignore_errors)
240
+
208
241
  if asset == TableauAsset.FIELD:
209
- return self._fetch_fields()
242
+ return self._fetch_fields(skip_batch=skip_batch)
210
243
 
211
244
  page_size = self._page_size(asset)
212
245
  resource, fields = GQL_QUERIES[asset]
213
- return self._call(resource, fields, page_size)
246
+ return self._call(
247
+ resource=resource,
248
+ fields=fields,
249
+ page_size=page_size,
250
+ skip_batch=skip_batch,
251
+ )
@@ -33,16 +33,20 @@ def extract_all(**kwargs) -> None:
33
33
  """
34
34
  output_directory = kwargs.get("output") or from_env(OUTPUT_DIR)
35
35
  with_columns = not kwargs.get("skip_columns")
36
+ with_fields = not kwargs.get("skip_fields")
36
37
  with_pulse = kwargs.get("with_pulse") or False
37
38
  page_size = kwargs.get("page_size")
39
+ ignore_errors = kwargs.get("ignore_errors") or False
38
40
  timestamp = current_timestamp()
39
41
 
40
42
  credentials = TableauCredentials(**kwargs)
41
43
  client = TableauClient(
42
44
  credentials,
43
45
  with_columns=with_columns,
46
+ with_fields=with_fields,
44
47
  with_pulse=with_pulse,
45
48
  override_page_size=page_size,
49
+ ignore_errors=ignore_errors,
46
50
  )
47
51
  client.login()
48
52