castor-extractor 0.24.7__tar.gz → 0.24.10__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 (420) hide show
  1. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/CHANGELOG.md +12 -0
  2. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/PKG-INFO +13 -1
  3. castor_extractor-0.24.10/castor_extractor/transformation/coalesce/__init__.py +2 -0
  4. castor_extractor-0.24.10/castor_extractor/transformation/coalesce/assets.py +18 -0
  5. castor_extractor-0.24.10/castor_extractor/transformation/coalesce/client/__init__.py +2 -0
  6. castor_extractor-0.24.10/castor_extractor/transformation/coalesce/client/client.py +180 -0
  7. castor_extractor-0.24.10/castor_extractor/transformation/coalesce/client/credentials.py +23 -0
  8. castor_extractor-0.24.10/castor_extractor/transformation/coalesce/client/endpoint.py +42 -0
  9. castor_extractor-0.24.10/castor_extractor/transformation/coalesce/client/type.py +1 -0
  10. castor_extractor-0.24.10/castor_extractor/transformation/coalesce/client/utils.py +52 -0
  11. castor_extractor-0.24.10/castor_extractor/transformation/coalesce/client/utils_test.py +54 -0
  12. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/tableau/client/client_metadata_api.py +74 -8
  13. castor_extractor-0.24.10/castor_extractor/visualization/tableau/client/client_metadata_api_test.py +111 -0
  14. castor_extractor-0.24.10/castor_extractor/warehouse/__init__.py +0 -0
  15. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/format.py +1 -1
  16. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/pyproject.toml +1 -1
  17. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/Dockerfile +0 -0
  18. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/DockerfileUsage.md +0 -0
  19. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/LICENCE +0 -0
  20. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/README.md +0 -0
  21. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/__init__.py +0 -0
  22. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/__init__.py +0 -0
  23. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_bigquery.py +0 -0
  24. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_confluence.py +0 -0
  25. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_databricks.py +0 -0
  26. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_domo.py +0 -0
  27. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_looker.py +0 -0
  28. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_looker_studio.py +0 -0
  29. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_metabase_api.py +0 -0
  30. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_metabase_db.py +0 -0
  31. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_mode.py +0 -0
  32. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_mysql.py +0 -0
  33. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_notion.py +0 -0
  34. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_postgres.py +0 -0
  35. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_powerbi.py +0 -0
  36. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_qlik.py +0 -0
  37. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_redshift.py +0 -0
  38. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_salesforce.py +0 -0
  39. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_salesforce_reporting.py +0 -0
  40. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_sigma.py +0 -0
  41. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_snowflake.py +0 -0
  42. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_sqlserver.py +0 -0
  43. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_tableau.py +0 -0
  44. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/extract_thoughtspot.py +0 -0
  45. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/file_check.py +0 -0
  46. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/commands/upload.py +0 -0
  47. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/file_checker/__init__.py +0 -0
  48. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/file_checker/column.py +0 -0
  49. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/file_checker/column_test.py +0 -0
  50. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/file_checker/constants.py +0 -0
  51. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/file_checker/enums.py +0 -0
  52. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/file_checker/file.py +0 -0
  53. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/file_checker/file_test.py +0 -0
  54. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/file_checker/file_test_users.csv +0 -0
  55. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/file_checker/file_test_users_valid.csv +0 -0
  56. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/file_checker/templates/__init__.py +0 -0
  57. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/file_checker/templates/generic_warehouse.py +0 -0
  58. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/__init__.py +0 -0
  59. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/confluence/__init__.py +0 -0
  60. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/confluence/assets.py +0 -0
  61. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/confluence/client/__init__.py +0 -0
  62. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/confluence/client/client.py +0 -0
  63. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/confluence/client/credentials.py +0 -0
  64. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/confluence/client/endpoints.py +0 -0
  65. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/confluence/client/pagination.py +0 -0
  66. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/confluence/extract.py +0 -0
  67. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/notion/__init__.py +0 -0
  68. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/notion/assets.py +0 -0
  69. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/notion/client/__init__.py +0 -0
  70. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/notion/client/client.py +0 -0
  71. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/notion/client/client_test.py +0 -0
  72. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/notion/client/constants.py +0 -0
  73. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/notion/client/credentials.py +0 -0
  74. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/notion/client/endpoints.py +0 -0
  75. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/notion/client/pagination.py +0 -0
  76. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/knowledge/notion/extract.py +0 -0
  77. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/logger.py +0 -0
  78. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/quality/__init__.py +0 -0
  79. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/quality/soda/__init__.py +0 -0
  80. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/quality/soda/assets.py +0 -0
  81. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/quality/soda/client/__init__.py +0 -0
  82. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/quality/soda/client/client.py +0 -0
  83. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/quality/soda/client/credentials.py +0 -0
  84. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/quality/soda/client/endpoints.py +0 -0
  85. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/quality/soda/client/pagination.py +0 -0
  86. {castor_extractor-0.24.7/castor_extractor/visualization → castor_extractor-0.24.10/castor_extractor/transformation}/__init__.py +0 -0
  87. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/types.py +0 -0
  88. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/uploader/__init__.py +0 -0
  89. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/uploader/constant.py +0 -0
  90. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/uploader/env.py +0 -0
  91. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/uploader/env_test.py +0 -0
  92. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/uploader/settings.py +0 -0
  93. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/uploader/upload.py +0 -0
  94. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/uploader/upload_test.py +0 -0
  95. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/uploader/utils.py +0 -0
  96. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/__init__.py +0 -0
  97. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/argument_parser.py +0 -0
  98. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/argument_parser_test.py +0 -0
  99. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/batch.py +0 -0
  100. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/batch_test.py +0 -0
  101. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/__init__.py +0 -0
  102. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/abstract.py +0 -0
  103. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/api/__init__.py +0 -0
  104. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/api/auth.py +0 -0
  105. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/api/auth_test.py +0 -0
  106. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/api/client.py +0 -0
  107. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/api/client_test.py +0 -0
  108. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/api/pagination.py +0 -0
  109. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/api/pagination_test.py +0 -0
  110. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/api/safe_request.py +0 -0
  111. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/api/safe_request_test.py +0 -0
  112. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/api/utils.py +0 -0
  113. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/api/utils_test.py +0 -0
  114. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/postgres.py +0 -0
  115. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/query.py +0 -0
  116. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/uri.py +0 -0
  117. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/client/uri_test.py +0 -0
  118. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/collection.py +0 -0
  119. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/collection_test.py +0 -0
  120. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/constants.py +0 -0
  121. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/dbt/__init__.py +0 -0
  122. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/dbt/assets.py +0 -0
  123. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/dbt/client.py +0 -0
  124. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/dbt/client_test.py +0 -0
  125. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/dbt/credentials.py +0 -0
  126. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/deprecate.py +0 -0
  127. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/env.py +0 -0
  128. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/files.py +0 -0
  129. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/files_test.py +0 -0
  130. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/formatter.py +0 -0
  131. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/formatter_test.csv +0 -0
  132. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/formatter_test.json +0 -0
  133. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/formatter_test.py +0 -0
  134. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/json_stream_write.py +0 -0
  135. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/load.py +0 -0
  136. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/object.py +0 -0
  137. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/object_test.py +0 -0
  138. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/pager/__init__.py +0 -0
  139. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/pager/pager.py +0 -0
  140. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/pager/pager_on_id.py +0 -0
  141. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/pager/pager_on_id_test.py +0 -0
  142. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/pager/pager_test.py +0 -0
  143. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/retry.py +0 -0
  144. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/retry_test.py +0 -0
  145. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/safe.py +0 -0
  146. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/safe_test.py +0 -0
  147. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/salesforce/__init__.py +0 -0
  148. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/salesforce/client.py +0 -0
  149. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/salesforce/client_test.py +0 -0
  150. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/salesforce/constants.py +0 -0
  151. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/salesforce/credentials.py +0 -0
  152. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/salesforce/credentials_test.py +0 -0
  153. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/salesforce/pagination.py +0 -0
  154. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/store.py +0 -0
  155. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/string.py +0 -0
  156. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/string_test.py +0 -0
  157. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/time.py +0 -0
  158. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/time_test.py +0 -0
  159. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/type.py +0 -0
  160. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/validation.py +0 -0
  161. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/validation_test.py +0 -0
  162. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/utils/write.py +0 -0
  163. {castor_extractor-0.24.7/castor_extractor/warehouse → castor_extractor-0.24.10/castor_extractor/visualization}/__init__.py +0 -0
  164. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/domo/__init__.py +0 -0
  165. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/domo/assets.py +0 -0
  166. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/domo/client/__init__.py +0 -0
  167. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/domo/client/client.py +0 -0
  168. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/domo/client/credentials.py +0 -0
  169. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/domo/client/endpoints.py +0 -0
  170. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/domo/client/pagination.py +0 -0
  171. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/domo/client/pagination_test.py +0 -0
  172. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/domo/extract.py +0 -0
  173. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/__init__.py +0 -0
  174. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/api/__init__.py +0 -0
  175. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/api/client.py +0 -0
  176. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/api/client_test.py +0 -0
  177. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/api/constants.py +0 -0
  178. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/api/credentials.py +0 -0
  179. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/api/extraction_parameters.py +0 -0
  180. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/api/sdk.py +0 -0
  181. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/api/sdk_test.py +0 -0
  182. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/api/utils.py +0 -0
  183. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/assets.py +0 -0
  184. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/constant.py +0 -0
  185. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/constants.py +0 -0
  186. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/extract.py +0 -0
  187. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/fields.py +0 -0
  188. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/fields_test.py +0 -0
  189. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker/multithreading.py +0 -0
  190. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker_studio/__init__.py +0 -0
  191. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker_studio/assets.py +0 -0
  192. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker_studio/client/__init__.py +0 -0
  193. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker_studio/client/admin_sdk_client.py +0 -0
  194. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker_studio/client/client.py +0 -0
  195. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker_studio/client/credentials.py +0 -0
  196. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker_studio/client/endpoints.py +0 -0
  197. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker_studio/client/enums.py +0 -0
  198. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker_studio/client/looker_studio_api_client.py +0 -0
  199. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker_studio/client/pagination.py +0 -0
  200. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker_studio/client/queries/query.sql +0 -0
  201. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker_studio/client/scopes.py +0 -0
  202. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/looker_studio/extract.py +0 -0
  203. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/__init__.py +0 -0
  204. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/assets.py +0 -0
  205. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/__init__.py +0 -0
  206. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/api/__init__.py +0 -0
  207. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/api/client.py +0 -0
  208. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/api/client_test.py +0 -0
  209. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/api/credentials.py +0 -0
  210. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/db/__init__.py +0 -0
  211. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/db/client.py +0 -0
  212. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/db/credentials.py +0 -0
  213. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/db/queries/.sqlfluff +0 -0
  214. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/db/queries/base_url.sql +0 -0
  215. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/db/queries/card.sql +0 -0
  216. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/db/queries/collection.sql +0 -0
  217. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/db/queries/dashboard.sql +0 -0
  218. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/db/queries/dashboard_cards.sql +0 -0
  219. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/db/queries/database.sql +0 -0
  220. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/db/queries/table.sql +0 -0
  221. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/db/queries/user.sql +0 -0
  222. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/decryption.py +0 -0
  223. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/decryption_test.py +0 -0
  224. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/client/shared.py +0 -0
  225. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/errors.py +0 -0
  226. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/extract.py +0 -0
  227. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/metabase/types.py +0 -0
  228. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/mode/__init__.py +0 -0
  229. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/mode/assets.py +0 -0
  230. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/mode/client/__init__.py +0 -0
  231. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/mode/client/client.py +0 -0
  232. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/mode/client/client_test.json +0 -0
  233. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/mode/client/client_test.py +0 -0
  234. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/mode/client/constants.py +0 -0
  235. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/mode/client/credentials.py +0 -0
  236. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/mode/errors.py +0 -0
  237. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/mode/extract.py +0 -0
  238. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/powerbi/__init__.py +0 -0
  239. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/powerbi/assets.py +0 -0
  240. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/powerbi/client/__init__.py +0 -0
  241. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/powerbi/client/authentication.py +0 -0
  242. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/powerbi/client/client.py +0 -0
  243. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/powerbi/client/client_test.py +0 -0
  244. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/powerbi/client/constants.py +0 -0
  245. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/powerbi/client/credentials.py +0 -0
  246. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/powerbi/client/credentials_test.py +0 -0
  247. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/powerbi/client/endpoints.py +0 -0
  248. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/powerbi/client/pagination.py +0 -0
  249. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/powerbi/extract.py +0 -0
  250. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/__init__.py +0 -0
  251. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/assets.py +0 -0
  252. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/client/__init__.py +0 -0
  253. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/client/constants.py +0 -0
  254. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/client/engine/__init__.py +0 -0
  255. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/client/engine/client.py +0 -0
  256. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/client/engine/constants.py +0 -0
  257. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/client/engine/credentials.py +0 -0
  258. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/client/engine/error.py +0 -0
  259. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/client/engine/error_test.py +0 -0
  260. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/client/engine/json_rpc.py +0 -0
  261. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/client/engine/json_rpc_test.py +0 -0
  262. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/client/engine/websocket.py +0 -0
  263. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/client/master.py +0 -0
  264. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/client/rest.py +0 -0
  265. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/client/rest_test.py +0 -0
  266. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/qlik/extract.py +0 -0
  267. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/salesforce_reporting/__init__.py +0 -0
  268. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/salesforce_reporting/assets.py +0 -0
  269. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/salesforce_reporting/client/__init__.py +0 -0
  270. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/salesforce_reporting/client/rest.py +0 -0
  271. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/salesforce_reporting/client/soql.py +0 -0
  272. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/salesforce_reporting/extract.py +0 -0
  273. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/sigma/__init__.py +0 -0
  274. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/sigma/assets.py +0 -0
  275. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/sigma/client/__init__.py +0 -0
  276. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/sigma/client/client.py +0 -0
  277. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/sigma/client/credentials.py +0 -0
  278. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/sigma/client/endpoints.py +0 -0
  279. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/sigma/client/pagination.py +0 -0
  280. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/sigma/extract.py +0 -0
  281. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/tableau/__init__.py +0 -0
  282. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/tableau/assets.py +0 -0
  283. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/tableau/client/__init__.py +0 -0
  284. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/tableau/client/client.py +0 -0
  285. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/tableau/client/client_rest_api.py +0 -0
  286. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/tableau/client/client_tsc.py +0 -0
  287. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/tableau/client/credentials.py +0 -0
  288. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/tableau/client/errors.py +0 -0
  289. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/tableau/client/gql_queries.py +0 -0
  290. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/tableau/client/rest_fields.py +0 -0
  291. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/tableau/constants.py +0 -0
  292. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/tableau/extract.py +0 -0
  293. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/thoughtspot/__init__.py +0 -0
  294. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/thoughtspot/assets.py +0 -0
  295. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/thoughtspot/client/__init__.py +0 -0
  296. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/thoughtspot/client/client.py +0 -0
  297. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/thoughtspot/client/credentials.py +0 -0
  298. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/thoughtspot/client/endpoints.py +0 -0
  299. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/thoughtspot/client/pagination.py +0 -0
  300. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/visualization/thoughtspot/extract.py +0 -0
  301. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/abstract/__init__.py +0 -0
  302. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/abstract/asset.py +0 -0
  303. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/abstract/asset_test.py +0 -0
  304. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/abstract/extract.py +0 -0
  305. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/abstract/query.py +0 -0
  306. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/abstract/time_filter.py +0 -0
  307. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/abstract/time_filter_test.py +0 -0
  308. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/__init__.py +0 -0
  309. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/client.py +0 -0
  310. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/client_test.py +0 -0
  311. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/credentials.py +0 -0
  312. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/extract.py +0 -0
  313. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/queries/.sqlfluff +0 -0
  314. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/queries/column.sql +0 -0
  315. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/queries/cte/sharded.sql +0 -0
  316. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/queries/database.sql +0 -0
  317. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/queries/query.sql +0 -0
  318. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/queries/schema.sql +0 -0
  319. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/queries/table.sql +0 -0
  320. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/queries/table_with_tags.sql +0 -0
  321. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/queries/user.sql +0 -0
  322. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/queries/view_ddl.sql +0 -0
  323. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/query.py +0 -0
  324. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/bigquery/types.py +0 -0
  325. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/__init__.py +0 -0
  326. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/api_client.py +0 -0
  327. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/api_client_test.py +0 -0
  328. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/client.py +0 -0
  329. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/client_test.py +0 -0
  330. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/credentials.py +0 -0
  331. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/endpoints.py +0 -0
  332. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/enums.py +0 -0
  333. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/extract.py +0 -0
  334. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/format_test.py +0 -0
  335. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/lineage.py +0 -0
  336. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/lineage_test.py +0 -0
  337. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/pagination.py +0 -0
  338. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/sql_client.py +0 -0
  339. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/types.py +0 -0
  340. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/utils.py +0 -0
  341. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/databricks/utils_test.py +0 -0
  342. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/mysql/__init__.py +0 -0
  343. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/mysql/client.py +0 -0
  344. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/mysql/client_test.py +0 -0
  345. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/mysql/extract.py +0 -0
  346. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/mysql/queries/.sqlfluff +0 -0
  347. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/mysql/queries/column.sql +0 -0
  348. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/mysql/queries/database.sql +0 -0
  349. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/mysql/queries/query.sql +0 -0
  350. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/mysql/queries/schema.sql +0 -0
  351. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/mysql/queries/table.sql +0 -0
  352. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/mysql/queries/user.sql +0 -0
  353. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/mysql/queries/view_ddl.sql +0 -0
  354. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/mysql/query.py +0 -0
  355. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/postgres/__init__.py +0 -0
  356. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/postgres/extract.py +0 -0
  357. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/postgres/queries/.sqlfluff +0 -0
  358. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/postgres/queries/column.sql +0 -0
  359. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/postgres/queries/database.sql +0 -0
  360. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/postgres/queries/group.sql +0 -0
  361. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/postgres/queries/schema.sql +0 -0
  362. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/postgres/queries/table.sql +0 -0
  363. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/postgres/queries/user.sql +0 -0
  364. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/postgres/query.py +0 -0
  365. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/__init__.py +0 -0
  366. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/client.py +0 -0
  367. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/client_test.py +0 -0
  368. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/extract.py +0 -0
  369. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/extract_test.py +0 -0
  370. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/queries/.sqlfluff +0 -0
  371. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/queries/column.sql +0 -0
  372. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/queries/database.sql +0 -0
  373. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/queries/group.sql +0 -0
  374. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/queries/query.sql +0 -0
  375. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/queries/query_serverless.sql +0 -0
  376. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/queries/schema.sql +0 -0
  377. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/queries/table.sql +0 -0
  378. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/queries/table_freshness.sql +0 -0
  379. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/queries/user.sql +0 -0
  380. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/queries/view_ddl.sql +0 -0
  381. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/redshift/query.py +0 -0
  382. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/salesforce/__init__.py +0 -0
  383. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/salesforce/client.py +0 -0
  384. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/salesforce/constants.py +0 -0
  385. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/salesforce/extract.py +0 -0
  386. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/salesforce/format.py +0 -0
  387. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/salesforce/format_test.py +0 -0
  388. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/salesforce/pagination.py +0 -0
  389. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/salesforce/soql.py +0 -0
  390. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/__init__.py +0 -0
  391. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/client.py +0 -0
  392. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/client_test.py +0 -0
  393. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/credentials.py +0 -0
  394. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/credentials_test.py +0 -0
  395. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/extract.py +0 -0
  396. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/queries/.sqlfluff +0 -0
  397. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/queries/column.sql +0 -0
  398. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/queries/column_lineage.sql +0 -0
  399. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/queries/database.sql +0 -0
  400. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/queries/function.sql +0 -0
  401. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/queries/grant_to_role.sql +0 -0
  402. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/queries/grant_to_user.sql +0 -0
  403. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/queries/query.sql +0 -0
  404. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/queries/role.sql +0 -0
  405. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/queries/schema.sql +0 -0
  406. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/queries/table.sql +0 -0
  407. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/queries/user.sql +0 -0
  408. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/queries/view_ddl.sql +0 -0
  409. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/snowflake/query.py +0 -0
  410. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/sqlserver/__init__.py +0 -0
  411. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/sqlserver/client.py +0 -0
  412. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/sqlserver/extract.py +0 -0
  413. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/sqlserver/queries/.sqlfluff +0 -0
  414. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/sqlserver/queries/column.sql +0 -0
  415. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/sqlserver/queries/database.sql +0 -0
  416. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/sqlserver/queries/schema.sql +0 -0
  417. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/sqlserver/queries/table.sql +0 -0
  418. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/sqlserver/queries/user.sql +0 -0
  419. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/sqlserver/query.py +0 -0
  420. {castor_extractor-0.24.7 → castor_extractor-0.24.10}/castor_extractor/warehouse/synapse/queries/column.sql +0 -0
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.24.10 - 2025-04-30
4
+
5
+ * Tableau - skip warnings instead of raising an error
6
+
7
+ ## 0.24.9 - 2025-04-16
8
+
9
+ * Introduce API client for **Coalesce**
10
+
11
+ ## 0.24.8 - 2025-04-16
12
+
13
+ * Tableau - remove duplicates introduced by `offset` pagination
14
+
3
15
  ## 0.24.7 - 2025-04-07
4
16
 
5
17
  * Tableau - switch from `cursor` to `offset` pagination to mitigate timeout issues
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: castor-extractor
3
- Version: 0.24.7
3
+ Version: 0.24.10
4
4
  Summary: Extract your metadata assets.
5
5
  Home-page: https://www.castordoc.com/
6
6
  License: EULA
@@ -210,6 +210,18 @@ For any questions or bug report, contact us at [support@castordoc.com](mailto:su
210
210
 
211
211
  # Changelog
212
212
 
213
+ ## 0.24.10 - 2025-04-30
214
+
215
+ * Tableau - skip warnings instead of raising an error
216
+
217
+ ## 0.24.9 - 2025-04-16
218
+
219
+ * Introduce API client for **Coalesce**
220
+
221
+ ## 0.24.8 - 2025-04-16
222
+
223
+ * Tableau - remove duplicates introduced by `offset` pagination
224
+
213
225
  ## 0.24.7 - 2025-04-07
214
226
 
215
227
  * Tableau - switch from `cursor` to `offset` pagination to mitigate timeout issues
@@ -0,0 +1,2 @@
1
+ from .assets import CoalesceAsset, CoalesceQualityAsset
2
+ from .client import CoalesceClient, CoalesceCredentials
@@ -0,0 +1,18 @@
1
+ from ...types import ExternalAsset
2
+
3
+
4
+ class CoalesceAsset(ExternalAsset):
5
+ """Coalesce assets"""
6
+
7
+ NODES = "nodes"
8
+
9
+
10
+ class CoalesceQualityAsset(ExternalAsset):
11
+ """
12
+ Coalesce Quality Assets
13
+ Remark: having a dedicated Enum for Quality simplifies the process of
14
+ searching pushed files
15
+ """
16
+
17
+ NODES = "nodes"
18
+ RUN_RESULTS = "run_results"
@@ -0,0 +1,2 @@
1
+ from .client import CoalesceClient
2
+ from .credentials import CoalesceCredentials
@@ -0,0 +1,180 @@
1
+ from http import HTTPStatus
2
+ from typing import Iterator, Optional
3
+
4
+ from ....utils import APIClient, BearerAuth, RequestSafeMode, SerializedAsset
5
+ from ..assets import CoalesceAsset, CoalesceQualityAsset
6
+ from .credentials import CoalesceCredentials
7
+ from .endpoint import (
8
+ CoalesceEndpointFactory,
9
+ )
10
+ from .type import NodeIDToNamesMapping
11
+ from .utils import column_names_per_node, is_test, test_names_per_node
12
+
13
+ _LIMIT_MAX = 1_000
14
+ _MAX_ERRORS = 50
15
+
16
+
17
+ def _run_result_payload(result: dict, query_result: dict) -> dict:
18
+ return {
19
+ "node_id": result["nodeID"],
20
+ "node_name": result["name"],
21
+ "test_name": query_result["name"],
22
+ "start_time": query_result["startTime"],
23
+ "end_time": query_result["endTime"],
24
+ "status": query_result["status"],
25
+ "success": query_result["success"],
26
+ "isRunning": query_result["isRunning"],
27
+ }
28
+
29
+
30
+ COALESCE_SAFE_MODE = RequestSafeMode(
31
+ status_codes=(HTTPStatus.INTERNAL_SERVER_ERROR,),
32
+ max_errors=_MAX_ERRORS,
33
+ )
34
+ COALESCE_TIMEOUT_SECONDS = 90
35
+
36
+
37
+ class CoalesceBearerAuth(BearerAuth):
38
+ """Bearer Authentication for Coalesce"""
39
+
40
+ def fetch_token(self) -> Optional[str]:
41
+ pass
42
+
43
+ def __init__(self, token: str):
44
+ self._token = token
45
+
46
+
47
+ class CoalesceClient(APIClient):
48
+ """REST API client to extract data from Coalesce"""
49
+
50
+ def __init__(
51
+ self,
52
+ credentials: CoalesceCredentials,
53
+ ):
54
+ auth = CoalesceBearerAuth(token=credentials.token)
55
+ super().__init__(
56
+ host=credentials.host,
57
+ auth=auth,
58
+ safe_mode=COALESCE_SAFE_MODE,
59
+ timeout=COALESCE_TIMEOUT_SECONDS,
60
+ )
61
+
62
+ def _fetch_environments(self) -> Iterator[dict]:
63
+ endpoint = CoalesceEndpointFactory.environments()
64
+ result = self._get(endpoint=endpoint)
65
+ return result["data"]
66
+
67
+ def _node_details(self, environment_id: int, node_id: str) -> dict:
68
+ endpoint = CoalesceEndpointFactory.nodes(
69
+ environment_id=environment_id, node_id=node_id
70
+ )
71
+ return self._get(endpoint=endpoint)
72
+
73
+ def _fetch_env_nodes(self, environment_id: int) -> SerializedAsset:
74
+ endpoint = CoalesceEndpointFactory.nodes(environment_id=environment_id)
75
+ result = self._get(endpoint=endpoint)
76
+ nodes: list[dict] = []
77
+ for node in result["data"]:
78
+ details = self._node_details(environment_id, node["id"])
79
+ nodes.append({**node, **details})
80
+ return nodes
81
+
82
+ def _fetch_all_nodes(self) -> SerializedAsset:
83
+ nodes: list[dict] = []
84
+ for environment in self._fetch_environments():
85
+ environment_id = environment["id"]
86
+ nodes.extend(self._fetch_env_nodes(environment_id))
87
+ return nodes
88
+
89
+ def _fetch_runs(self, starting_from: str) -> SerializedAsset:
90
+ """
91
+ fetch runs, per environment;
92
+ we break per environment to lower the chance of exceeding the 1k limit
93
+ """
94
+ runs: list[dict] = []
95
+ for environment in self._fetch_environments():
96
+ environment_id = environment["id"]
97
+ runs.extend(
98
+ self._fetch_recent_runs_per_env(environment_id, starting_from)
99
+ )
100
+ return runs
101
+
102
+ def _fetch_recent_runs_per_env(
103
+ self, environment_id: int, starting_from: str
104
+ ) -> SerializedAsset:
105
+ endpoint = CoalesceEndpointFactory.runs()
106
+ params = {
107
+ "environmentID": environment_id,
108
+ "limit": _LIMIT_MAX,
109
+ "orderBy": "runEndTime",
110
+ "orderByDirection": "asc",
111
+ "startingFrom": starting_from,
112
+ }
113
+ result = self._get(endpoint=endpoint, params=params)
114
+ return result["data"]
115
+
116
+ def _fetch_run_results(self, run_id: str) -> SerializedAsset:
117
+ endpoint = CoalesceEndpointFactory.run_results(run_id)
118
+ result = self._get(endpoint=endpoint)
119
+ return result["data"]
120
+
121
+ def _run_results_by_run(
122
+ self,
123
+ run_id: str,
124
+ test_names: NodeIDToNamesMapping,
125
+ column_names: NodeIDToNamesMapping,
126
+ ) -> SerializedAsset:
127
+ run_results: list[dict] = []
128
+ for result in self._fetch_run_results(run_id):
129
+ node_id = result["nodeID"]
130
+ for query_result in result["queryResults"]:
131
+ _is_test = is_test(
132
+ query_result,
133
+ node_id,
134
+ test_names,
135
+ column_names,
136
+ )
137
+ if not _is_test:
138
+ continue
139
+ run_result = _run_result_payload(result, query_result)
140
+ run_results.append(run_result)
141
+ return run_results
142
+
143
+ def _run_results_by_env(
144
+ self, environment_id: int, starting_from: str
145
+ ) -> SerializedAsset:
146
+ run_results: list[dict] = []
147
+ nodes = self._fetch_env_nodes(environment_id)
148
+ test_names = test_names_per_node(nodes)
149
+ column_names = column_names_per_node(nodes)
150
+ runs = self._fetch_recent_runs_per_env(environment_id, starting_from)
151
+
152
+ for run in runs:
153
+ run_id = run["id"]
154
+ _results = self._run_results_by_run(
155
+ run_id, test_names, column_names
156
+ )
157
+ run_results.extend(_results)
158
+ return run_results
159
+
160
+ def _fetch_all_run_results(self, starting_from: str) -> SerializedAsset:
161
+ run_results: list[dict] = []
162
+
163
+ for environment in self._fetch_environments():
164
+ environment_id = environment["id"]
165
+ _results = self._run_results_by_env(environment_id, starting_from)
166
+ run_results.extend(_results)
167
+
168
+ return run_results
169
+
170
+ def fetch(
171
+ self, asset: CoalesceAsset, starting_from=None
172
+ ) -> SerializedAsset:
173
+ """Extract the given Coalesce Asset"""
174
+ if asset in (CoalesceAsset.NODES, CoalesceQualityAsset.NODES):
175
+ return self._fetch_all_nodes()
176
+ elif asset == CoalesceQualityAsset.RUN_RESULTS:
177
+ return self._fetch_all_run_results(starting_from=starting_from)
178
+ raise AssertionError(
179
+ f"Asset {asset} is not supported by CoalesceClient"
180
+ )
@@ -0,0 +1,23 @@
1
+ from pydantic import Field
2
+ from pydantic_settings import BaseSettings, SettingsConfigDict
3
+
4
+ CASTOR_ENV_PREFIX = "CASTOR_COALESCE_"
5
+
6
+
7
+ class CoalesceCredentials(BaseSettings):
8
+ """Class to handle Coalesce rest API permissions"""
9
+
10
+ model_config = SettingsConfigDict(
11
+ env_prefix=CASTOR_ENV_PREFIX,
12
+ extra="ignore",
13
+ populate_by_name=True,
14
+ )
15
+
16
+ host: str
17
+ token: str = Field(repr=False)
18
+
19
+ @property
20
+ def token_payload(self) -> dict[str, str]:
21
+ return {
22
+ "client_secret": self.token,
23
+ }
@@ -0,0 +1,42 @@
1
+ from typing import Optional
2
+
3
+
4
+ class CoalesceEndpointFactory:
5
+ """Provide endpoints to hit Coalesce API"""
6
+
7
+ @classmethod
8
+ def environments(cls, environment_id: Optional[int] = None) -> str:
9
+ """
10
+ When specified, concatenate environment_id at the end to fetch details.
11
+ Otherwise, list existing environments.
12
+ """
13
+ base = "api/v1/environments"
14
+ if environment_id:
15
+ return base + f"/{environment_id}"
16
+ return base
17
+
18
+ @classmethod
19
+ def nodes(cls, environment_id: int, node_id: Optional[str] = None) -> str:
20
+ """
21
+ When specified, concatenate node_id at the end to fetch details.
22
+ Otherwise, list existing nodes in the given environment.
23
+ """
24
+ base = f"api/v1/environments/{environment_id}/nodes"
25
+ if node_id:
26
+ return base + f"/{node_id}"
27
+ return base
28
+
29
+ @classmethod
30
+ def runs(cls) -> str:
31
+ """
32
+ Get runs (additional filtering can be done in the body)
33
+ """
34
+ base = "api/v1/runs"
35
+ return base
36
+
37
+ @classmethod
38
+ def run_results(cls, run_id: str) -> str:
39
+ """
40
+ get run results (including success/fail for tests), given a run id
41
+ """
42
+ return f"api/v1/runs/{run_id}/results"
@@ -0,0 +1 @@
1
+ NodeIDToNamesMapping = dict[str, set[str]]
@@ -0,0 +1,52 @@
1
+ from ....utils import SerializedAsset
2
+ from .type import NodeIDToNamesMapping
3
+
4
+ _NULL_SUFFIX = ": Null"
5
+ _UNIQUE_SUFFIX = ": Unique"
6
+
7
+
8
+ def is_test(
9
+ query_result: dict,
10
+ node_id: str,
11
+ test_names: NodeIDToNamesMapping,
12
+ column_names: NodeIDToNamesMapping,
13
+ ) -> bool:
14
+ """
15
+ checks whether a query result is a test result or not.
16
+
17
+ all this implementation can soon be replaced by checking whether
18
+ query_result['type'] == 'sqlTest', which should be GA Apr 28th 2025
19
+ """
20
+ # test scoped on the node (table)
21
+ result_name = query_result["name"]
22
+ if result_name in test_names.get(node_id, {}):
23
+ return True
24
+
25
+ # test scoped on the column
26
+ if result_name.endswith(_NULL_SUFFIX) or result_name.endswith(
27
+ _UNIQUE_SUFFIX
28
+ ):
29
+ column_name = result_name.split(":")[0]
30
+ if column_name in column_names.get(node_id, {}):
31
+ return True
32
+ return False
33
+
34
+
35
+ def test_names_per_node(nodes: SerializedAsset) -> NodeIDToNamesMapping:
36
+ """mapping nodeID: set(testName)"""
37
+ mapping: dict[str, set[str]] = {}
38
+ for node in nodes:
39
+ node_id = node["id"]
40
+ tests = node.get("metadata", {}).get("appliedNodeTests", [])
41
+ mapping[node_id] = {test["name"] for test in tests}
42
+ return mapping
43
+
44
+
45
+ def column_names_per_node(nodes: SerializedAsset) -> NodeIDToNamesMapping:
46
+ """mapping nodeID: set(columnNames)"""
47
+ mapping: dict[str, set[str]] = {}
48
+ for node in nodes:
49
+ node_id = node["id"]
50
+ columns = node.get("metadata", {}).get("columns", [])
51
+ mapping[node_id] = {column["name"] for column in columns}
52
+ return mapping
@@ -0,0 +1,54 @@
1
+ from .utils import is_test
2
+
3
+
4
+ def test_is_test():
5
+ test_names = {"some-uuid": {"check-mirrors", "check-seatbelt"}}
6
+ column_names = {"some-uuid": {"carthago", "delenda", "est"}}
7
+
8
+ happy_node_test = is_test(
9
+ query_result={"name": "check-mirrors"},
10
+ node_id="some-uuid",
11
+ test_names=test_names,
12
+ column_names=column_names,
13
+ )
14
+ assert happy_node_test is True
15
+
16
+ unknown_node_test = is_test(
17
+ query_result={"name": "check-engine"},
18
+ node_id="some-uuid",
19
+ test_names=test_names,
20
+ column_names=column_names,
21
+ )
22
+ assert unknown_node_test is False
23
+
24
+ happy_column_test_unique = is_test(
25
+ query_result={"name": "carthago: Unique"},
26
+ node_id="some-uuid",
27
+ test_names=test_names,
28
+ column_names=column_names,
29
+ )
30
+ assert happy_column_test_unique is True
31
+
32
+ happy_column_test_null = is_test(
33
+ query_result={"name": "carthago: Null"},
34
+ node_id="some-uuid",
35
+ test_names=test_names,
36
+ column_names=column_names,
37
+ )
38
+ assert happy_column_test_null is True
39
+
40
+ unknown_column_test = is_test(
41
+ query_result={"name": "rome: Unique"},
42
+ node_id="some-uuid",
43
+ test_names=test_names,
44
+ column_names=column_names,
45
+ )
46
+ assert unknown_column_test is False
47
+
48
+ unknown_node_id_test = is_test(
49
+ query_result={"name": "whatever: Unique"},
50
+ node_id="unknown-uuid",
51
+ test_names=test_names,
52
+ column_names=column_names,
53
+ )
54
+ assert unknown_node_id_test is False
@@ -29,24 +29,62 @@ _RETRY_BASE_MS = 10_000
29
29
  _RETRY_COUNT = 4
30
30
 
31
31
 
32
+ def _is_timeout(error: dict) -> bool:
33
+ error_message = error.get("message")
34
+ return error_message == _TIMEOUT_MESSAGE
35
+
36
+
37
+ def _is_warning(error: dict) -> bool:
38
+ extensions = error.get("extensions")
39
+ if not extensions:
40
+ return False
41
+
42
+ severity = extensions.get("severity")
43
+ if not severity:
44
+ return False
45
+
46
+ return severity.lower() == "warning"
47
+
48
+
32
49
  def _check_errors(answer: dict) -> None:
33
50
  """
34
- handle errors in graphql response:
51
+ Handle errors in graphql response:
35
52
  - return None when there's no errors in the answer
36
- - TableauApiTimeout if any of the errors is a timeout
37
- - TableauApiError (generic) otherwise
53
+ - raise TableauApiTimeout if any of the errors is a timeout
54
+ - else raise TableauApiError if any of the errors is critical
55
+ - return None otherwise
56
+ More info about Tableau errors:
57
+ https://help.tableau.com/current/api/metadata_api/en-us/docs/meta_api_errors.html#other-errors
38
58
  """
39
59
  if "errors" not in answer:
40
60
  return
41
61
 
42
62
  errors = answer["errors"]
43
63
 
64
+ has_timeout_errors = False
65
+ has_critical_errors = False
66
+
44
67
  for error in errors:
45
- if error.get("message") == _TIMEOUT_MESSAGE:
46
- # we need specific handling for timeout issues (retry strategy)
47
- raise TableauApiTimeout(errors)
68
+ if _is_timeout(error):
69
+ has_timeout_errors = True
70
+ continue
48
71
 
49
- raise TableauApiError(answer["errors"])
72
+ if _is_warning(error):
73
+ # in this case, the answer contains the data anyway
74
+ # just display the warning
75
+ logger.warning(error)
76
+ continue
77
+
78
+ # at this point, it's not a timeout error
79
+ # besides, it's not a warning (severity is either "error" or Unknown)
80
+ has_critical_errors = True
81
+
82
+ if has_timeout_errors:
83
+ raise TableauApiTimeout(errors)
84
+ if has_critical_errors:
85
+ raise TableauApiError(errors)
86
+
87
+ return None
50
88
 
51
89
 
52
90
  def gql_query_scroll(
@@ -92,6 +130,34 @@ def gql_query_scroll(
92
130
  break
93
131
 
94
132
 
133
+ def _deduplicate(result_pages: Iterator[SerializedAsset]) -> SerializedAsset:
134
+ """
135
+ Sometimes assets are duplicated, which triggers UniqueViolation errors
136
+ during store_all down the line.
137
+
138
+ We suspect the offset pagination to be the root cause, because we had no
139
+ problem until recently, when we switched from cursor pagination to offset
140
+ pagination (for performance reasons)
141
+ https://help.tableau.com/current/api/metadata_api/en-us/docs/meta_api_examples.html#pagination
142
+
143
+ This is a straightforward solution to remove these duplicates directly at
144
+ extraction.
145
+ We don't show warnings because duplicates are expected, and we keep only
146
+ the first occurrence since those duplicates are probably identical.
147
+ """
148
+ deduplicated: SerializedAsset = []
149
+ seen_ids: set[str] = set()
150
+ for page in result_pages:
151
+ for asset in page:
152
+ asset_id = asset["id"]
153
+ if asset_id in seen_ids:
154
+ # skip duplicate
155
+ continue
156
+ deduplicated.append(asset)
157
+ seen_ids.add(asset_id)
158
+ return deduplicated
159
+
160
+
95
161
  class TableauClientMetadataApi:
96
162
  """
97
163
  Calls the MetadataAPI, using graphQL
@@ -118,7 +184,7 @@ class TableauClientMetadataApi:
118
184
  fields=fields,
119
185
  page_size=page_size,
120
186
  )
121
- return [asset for page in result_pages for asset in page]
187
+ return _deduplicate(result_pages)
122
188
 
123
189
  def _page_size(self, asset: TableauAsset) -> int:
124
190
  return (
@@ -0,0 +1,111 @@
1
+ import pytest
2
+
3
+ from .client_metadata_api import _TIMEOUT_MESSAGE, _check_errors, _deduplicate
4
+ from .errors import TableauApiError, TableauApiTimeout
5
+
6
+
7
+ def test__deduplicate():
8
+ result_pages = iter(
9
+ [
10
+ [
11
+ {"id": 1, "name": "workbook_1"},
12
+ {"id": 2, "name": "workbook_2"},
13
+ ],
14
+ [
15
+ {"id": 1, "name": "workbook_1"},
16
+ {"id": 3, "name": "workbook_3"},
17
+ {"id": 4, "name": "workbook_4"},
18
+ ],
19
+ [
20
+ {"id": 4, "name": "workbook_4"},
21
+ {"id": 5, "name": "workbook_5"},
22
+ {"id": 5, "name": "workbook_5"},
23
+ {"id": 5, "name": "workbook_5"},
24
+ ],
25
+ [
26
+ {"id": 1, "name": "workbook_1"},
27
+ {"id": 3, "name": "workbook_3"},
28
+ ],
29
+ ]
30
+ )
31
+ deduplicated = _deduplicate(result_pages)
32
+ assert len(deduplicated) == 5
33
+ deduplicated_keys = {item["id"] for item in deduplicated}
34
+ assert deduplicated_keys == {1, 2, 3, 4, 5}
35
+
36
+
37
+ def _timeout_error():
38
+ return {"message": _TIMEOUT_MESSAGE}
39
+
40
+
41
+ def _critical_error():
42
+ return {
43
+ "message": "critical error",
44
+ "extensions": {"severity": "Error"},
45
+ }
46
+
47
+
48
+ def _unknown_severity_error():
49
+ return {
50
+ "message": "unknown severity error",
51
+ "extensions": {"severity": None},
52
+ }
53
+
54
+
55
+ def _warning():
56
+ return {
57
+ "message": "critical error",
58
+ "extensions": {"severity": "Warning"},
59
+ }
60
+
61
+
62
+ def test__check_errors():
63
+ answer = {"data": []} # no errors
64
+ assert _check_errors(answer) is None
65
+
66
+ # only warnings
67
+ answer = {
68
+ "data": [],
69
+ "errors": [
70
+ _warning(),
71
+ _warning(),
72
+ _warning(),
73
+ ],
74
+ }
75
+ assert _check_errors(answer) is None
76
+
77
+ # timeout issues should be prio (because they can be retried)
78
+ answer = {
79
+ "data": [],
80
+ "errors": [
81
+ _warning(),
82
+ _critical_error(),
83
+ _timeout_error(),
84
+ _warning(),
85
+ ],
86
+ }
87
+ with pytest.raises(TableauApiTimeout):
88
+ _check_errors(answer)
89
+
90
+ # expect critical error
91
+ answer = {
92
+ "data": [],
93
+ "errors": [
94
+ _warning(),
95
+ _warning(),
96
+ _critical_error(),
97
+ ],
98
+ }
99
+ with pytest.raises(TableauApiError):
100
+ _check_errors(answer)
101
+
102
+ # unknown severity is considered as critical
103
+ answer = {
104
+ "data": [],
105
+ "errors": [
106
+ _unknown_severity_error(),
107
+ _warning(),
108
+ ],
109
+ }
110
+ with pytest.raises(TableauApiError):
111
+ _check_errors(answer)
@@ -168,7 +168,7 @@ class DatabricksFormatter:
168
168
  "schema_name": None,
169
169
  "query_text": q["query_text"],
170
170
  "user_id": q["user_id"],
171
- "user_name": q["user_name"],
171
+ "user_name": q.get("user_name"),
172
172
  "start_time": start_time,
173
173
  "end_time": end_time,
174
174
  }
@@ -3,7 +3,7 @@ requires = ["setuptools>=61.2"]
3
3
 
4
4
  [tool.poetry]
5
5
  name = "castor-extractor"
6
- version = "0.24.7"
6
+ version = "0.24.10"
7
7
  description = "Extract your metadata assets."
8
8
  authors = ["Castor <support@castordoc.com>"]
9
9
  license = "EULA"