castor-extractor 0.16.11__tar.gz → 0.16.15__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 (376) hide show
  1. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/CHANGELOG.md +16 -0
  2. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/PKG-INFO +1 -1
  3. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/client/api.py +8 -3
  4. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/retry.py +3 -1
  5. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/client/client.py +5 -2
  6. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/client/gql_queries.py +10 -1
  7. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/abstract/__init__.py +2 -0
  8. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/abstract/asset.py +13 -0
  9. castor_extractor-0.16.15/castor_extractor/warehouse/databricks/client.py +465 -0
  10. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/databricks/client_test.py +61 -1
  11. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/databricks/extract.py +36 -0
  12. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/databricks/format.py +13 -0
  13. castor_extractor-0.16.15/castor_extractor/warehouse/databricks/test_constants.py +79 -0
  14. castor_extractor-0.16.15/castor_extractor/warehouse/databricks/types.py +8 -0
  15. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/extract.py +2 -0
  16. castor_extractor-0.16.15/castor_extractor/warehouse/snowflake/queries/function.sql +10 -0
  17. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/pyproject.toml +1 -1
  18. castor_extractor-0.16.11/castor_extractor/warehouse/databricks/client.py +0 -229
  19. castor_extractor-0.16.11/castor_extractor/warehouse/databricks/types.py +0 -3
  20. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/Dockerfile +0 -0
  21. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/LICENCE +0 -0
  22. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/README.md +0 -0
  23. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/__init__.py +0 -0
  24. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/__init__.py +0 -0
  25. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_bigquery.py +0 -0
  26. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_databricks.py +0 -0
  27. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_domo.py +0 -0
  28. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_looker.py +0 -0
  29. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_metabase_api.py +0 -0
  30. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_metabase_db.py +0 -0
  31. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_mode.py +0 -0
  32. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_mysql.py +0 -0
  33. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_postgres.py +0 -0
  34. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_powerbi.py +0 -0
  35. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_qlik.py +0 -0
  36. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_redshift.py +0 -0
  37. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_salesforce.py +0 -0
  38. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_salesforce_reporting.py +0 -0
  39. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_sigma.py +0 -0
  40. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_snowflake.py +0 -0
  41. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_sqlserver.py +0 -0
  42. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/extract_tableau.py +0 -0
  43. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/file_check.py +0 -0
  44. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/commands/upload.py +0 -0
  45. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/file_checker/__init__.py +0 -0
  46. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/file_checker/column.py +0 -0
  47. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/file_checker/column_test.py +0 -0
  48. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/file_checker/constants.py +0 -0
  49. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/file_checker/enums.py +0 -0
  50. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/file_checker/file.py +0 -0
  51. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/file_checker/file_test.py +0 -0
  52. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/file_checker/file_test_users.csv +0 -0
  53. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/file_checker/file_test_users_valid.csv +0 -0
  54. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/file_checker/templates/__init__.py +0 -0
  55. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/file_checker/templates/generic_warehouse.py +0 -0
  56. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/logger.py +0 -0
  57. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/types.py +0 -0
  58. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/uploader/__init__.py +0 -0
  59. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/uploader/constant.py +0 -0
  60. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/uploader/env.py +0 -0
  61. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/uploader/env_test.py +0 -0
  62. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/uploader/upload.py +0 -0
  63. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/uploader/upload_test.py +0 -0
  64. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/uploader/utils.py +0 -0
  65. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/__init__.py +0 -0
  66. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/client/__init__.py +0 -0
  67. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/client/abstract.py +0 -0
  68. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/client/api_test.py +0 -0
  69. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/client/postgres.py +0 -0
  70. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/client/query.py +0 -0
  71. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/client/uri.py +0 -0
  72. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/client/uri_test.py +0 -0
  73. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/collection.py +0 -0
  74. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/collection_test.py +0 -0
  75. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/constants.py +0 -0
  76. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/dbt/__init__.py +0 -0
  77. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/dbt/assets.py +0 -0
  78. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/dbt/client.py +0 -0
  79. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/dbt/client_test.py +0 -0
  80. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/dbt/credentials.py +0 -0
  81. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/deprecate.py +0 -0
  82. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/env.py +0 -0
  83. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/files.py +0 -0
  84. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/files_test.py +0 -0
  85. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/formatter.py +0 -0
  86. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/formatter_test.csv +0 -0
  87. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/formatter_test.json +0 -0
  88. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/formatter_test.py +0 -0
  89. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/json_stream_write.py +0 -0
  90. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/load.py +0 -0
  91. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/object.py +0 -0
  92. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/object_test.py +0 -0
  93. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/pager/__init__.py +0 -0
  94. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/pager/pager.py +0 -0
  95. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/pager/pager_on_id.py +0 -0
  96. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/pager/pager_on_id_test.py +0 -0
  97. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/pager/pager_on_token.py +0 -0
  98. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/pager/pager_on_token_test.py +0 -0
  99. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/pager/pager_test.py +0 -0
  100. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/retry_test.py +0 -0
  101. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/safe.py +0 -0
  102. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/safe_test.py +0 -0
  103. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/salesforce/__init__.py +0 -0
  104. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/salesforce/client.py +0 -0
  105. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/salesforce/client_test.py +0 -0
  106. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/salesforce/constants.py +0 -0
  107. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/salesforce/credentials.py +0 -0
  108. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/salesforce/credentials_test.py +0 -0
  109. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/store.py +0 -0
  110. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/string.py +0 -0
  111. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/string_test.py +0 -0
  112. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/time.py +0 -0
  113. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/time_test.py +0 -0
  114. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/type.py +0 -0
  115. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/validation.py +0 -0
  116. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/validation_test.py +0 -0
  117. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/utils/write.py +0 -0
  118. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/__init__.py +0 -0
  119. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/__init__.py +0 -0
  120. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/assets.py +0 -0
  121. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/client/__init__.py +0 -0
  122. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/client/client.py +0 -0
  123. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/client/client_test.py +0 -0
  124. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/client/credentials.py +0 -0
  125. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/client/endpoints.py +0 -0
  126. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/client/pagination.py +0 -0
  127. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/client/pagination_test.py +0 -0
  128. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/constants.py +0 -0
  129. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/extract.py +0 -0
  130. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/__init__.py +0 -0
  131. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/api/__init__.py +0 -0
  132. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/api/client.py +0 -0
  133. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/api/client_test.py +0 -0
  134. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/api/constants.py +0 -0
  135. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/api/sdk.py +0 -0
  136. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/api/sdk_test.py +0 -0
  137. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/api/utils.py +0 -0
  138. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/assets.py +0 -0
  139. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/constant.py +0 -0
  140. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/env.py +0 -0
  141. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/extract.py +0 -0
  142. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/fields.py +0 -0
  143. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/fields_test.py +0 -0
  144. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/multithreading.py +0 -0
  145. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/parameters.py +0 -0
  146. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/__init__.py +0 -0
  147. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/assets.py +0 -0
  148. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/__init__.py +0 -0
  149. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/api/__init__.py +0 -0
  150. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/api/client.py +0 -0
  151. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/api/client_test.py +0 -0
  152. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/api/credentials.py +0 -0
  153. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/__init__.py +0 -0
  154. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/client.py +0 -0
  155. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/credentials.py +0 -0
  156. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/.sqlfluff +0 -0
  157. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/base_url.sql +0 -0
  158. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/card.sql +0 -0
  159. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/collection.sql +0 -0
  160. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/dashboard.sql +0 -0
  161. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/dashboard_cards.sql +0 -0
  162. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/database.sql +0 -0
  163. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/table.sql +0 -0
  164. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/user.sql +0 -0
  165. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/decryption.py +0 -0
  166. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/decryption_test.py +0 -0
  167. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/shared.py +0 -0
  168. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/errors.py +0 -0
  169. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/extract.py +0 -0
  170. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/types.py +0 -0
  171. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/__init__.py +0 -0
  172. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/assets.py +0 -0
  173. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/client/__init__.py +0 -0
  174. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/client/client.py +0 -0
  175. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/client/client_test.json +0 -0
  176. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/client/client_test.py +0 -0
  177. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/client/constants.py +0 -0
  178. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/client/credentials.py +0 -0
  179. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/errors.py +0 -0
  180. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/extract.py +0 -0
  181. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/__init__.py +0 -0
  182. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/assets.py +0 -0
  183. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/client/__init__.py +0 -0
  184. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/client/constants.py +0 -0
  185. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/client/credentials.py +0 -0
  186. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/client/credentials_test.py +0 -0
  187. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/client/rest.py +0 -0
  188. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/client/rest_test.py +0 -0
  189. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/client/utils.py +0 -0
  190. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/client/utils_test.py +0 -0
  191. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/extract.py +0 -0
  192. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/__init__.py +0 -0
  193. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/assets.py +0 -0
  194. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/__init__.py +0 -0
  195. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/constants.py +0 -0
  196. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/engine/__init__.py +0 -0
  197. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/engine/client.py +0 -0
  198. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/engine/constants.py +0 -0
  199. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/engine/error.py +0 -0
  200. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/engine/error_test.py +0 -0
  201. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/engine/json_rpc.py +0 -0
  202. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/engine/json_rpc_test.py +0 -0
  203. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/engine/websocket.py +0 -0
  204. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/master.py +0 -0
  205. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/rest.py +0 -0
  206. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/rest_test.py +0 -0
  207. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/constants.py +0 -0
  208. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/extract.py +0 -0
  209. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/salesforce_reporting/__init__.py +0 -0
  210. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/salesforce_reporting/assets.py +0 -0
  211. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/salesforce_reporting/client/__init__.py +0 -0
  212. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/salesforce_reporting/client/rest.py +0 -0
  213. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/salesforce_reporting/client/soql.py +0 -0
  214. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/salesforce_reporting/extract.py +0 -0
  215. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/__init__.py +0 -0
  216. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/assets.py +0 -0
  217. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/client/__init__.py +0 -0
  218. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/client/client.py +0 -0
  219. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/client/client_test.py +0 -0
  220. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/client/credentials.py +0 -0
  221. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/client/endpoints.py +0 -0
  222. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/client/pagination.py +0 -0
  223. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/constants.py +0 -0
  224. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/extract.py +0 -0
  225. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/__init__.py +0 -0
  226. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/assets.py +0 -0
  227. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/client/__init__.py +0 -0
  228. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/client/client.py +0 -0
  229. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/client/client_utils.py +0 -0
  230. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/client/credentials.py +0 -0
  231. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/client/project.py +0 -0
  232. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/client/safe_mode.py +0 -0
  233. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/constants.py +0 -0
  234. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/errors.py +0 -0
  235. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/extract.py +0 -0
  236. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/gql_fields.py +0 -0
  237. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/__init__.py +0 -0
  238. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/__init__.py +0 -0
  239. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/assets/graphql/metadata/metadata_1_get.json +0 -0
  240. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/assets/graphql/metadata/metadata_2_get.json +0 -0
  241. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/assets/rest_api/auth.xml +0 -0
  242. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/assets/rest_api/project_get.xml +0 -0
  243. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/assets/rest_api/user_get.xml +0 -0
  244. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/assets/rest_api/view_get_usage.xml +0 -0
  245. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/assets/rest_api/workbook_get.xml +0 -0
  246. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/graphql/__init__.py +0 -0
  247. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/graphql/paginated_object_test.py +0 -0
  248. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/rest_api/__init__.py +0 -0
  249. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/rest_api/auth_test.py +0 -0
  250. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/rest_api/credentials_test.py +0 -0
  251. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/rest_api/projects_test.py +0 -0
  252. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/rest_api/usages_test.py +0 -0
  253. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/rest_api/users_test.py +0 -0
  254. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/rest_api/workbooks_test.py +0 -0
  255. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/utils/__init__.py +0 -0
  256. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/utils/env_key.py +0 -0
  257. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tsc_fields.py +0 -0
  258. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/types.py +0 -0
  259. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/usage.py +0 -0
  260. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/__init__.py +0 -0
  261. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/assets.py +0 -0
  262. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/client/__init__.py +0 -0
  263. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/client/credentials.py +0 -0
  264. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/client/errors.py +0 -0
  265. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/client/tsc_fields.py +0 -0
  266. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/constants.py +0 -0
  267. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/extract.py +0 -0
  268. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/__init__.py +0 -0
  269. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/abstract/asset_test.py +0 -0
  270. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/abstract/extract.py +0 -0
  271. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/abstract/query.py +0 -0
  272. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/abstract/time_filter.py +0 -0
  273. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/abstract/time_filter_test.py +0 -0
  274. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/__init__.py +0 -0
  275. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/client.py +0 -0
  276. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/client_test.py +0 -0
  277. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/credentials.py +0 -0
  278. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/extract.py +0 -0
  279. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/.sqlfluff +0 -0
  280. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/column.sql +0 -0
  281. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/cte/sharded.sql +0 -0
  282. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/database.sql +0 -0
  283. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/query.sql +0 -0
  284. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/schema.sql +0 -0
  285. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/table.sql +0 -0
  286. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/table_with_tags.sql +0 -0
  287. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/user.sql +0 -0
  288. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/view_ddl.sql +0 -0
  289. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/query.py +0 -0
  290. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/types.py +0 -0
  291. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/databricks/__init__.py +0 -0
  292. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/databricks/credentials.py +0 -0
  293. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/databricks/format_test.py +0 -0
  294. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/__init__.py +0 -0
  295. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/client.py +0 -0
  296. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/client_test.py +0 -0
  297. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/extract.py +0 -0
  298. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/queries/.sqlfluff +0 -0
  299. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/queries/column.sql +0 -0
  300. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/queries/database.sql +0 -0
  301. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/queries/query.sql +0 -0
  302. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/queries/schema.sql +0 -0
  303. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/queries/table.sql +0 -0
  304. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/queries/user.sql +0 -0
  305. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/queries/view_ddl.sql +0 -0
  306. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/query.py +0 -0
  307. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/__init__.py +0 -0
  308. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/extract.py +0 -0
  309. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/queries/.sqlfluff +0 -0
  310. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/queries/column.sql +0 -0
  311. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/queries/database.sql +0 -0
  312. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/queries/group.sql +0 -0
  313. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/queries/schema.sql +0 -0
  314. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/queries/table.sql +0 -0
  315. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/queries/user.sql +0 -0
  316. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/query.py +0 -0
  317. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/__init__.py +0 -0
  318. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/client.py +0 -0
  319. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/client_test.py +0 -0
  320. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/extract.py +0 -0
  321. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/.sqlfluff +0 -0
  322. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/column.sql +0 -0
  323. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/database.sql +0 -0
  324. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/group.sql +0 -0
  325. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/query.sql +0 -0
  326. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/schema.sql +0 -0
  327. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/table.sql +0 -0
  328. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/table_freshness.sql +0 -0
  329. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/user.sql +0 -0
  330. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/view_ddl.sql +0 -0
  331. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/query.py +0 -0
  332. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/salesforce/__init__.py +0 -0
  333. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/salesforce/client.py +0 -0
  334. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/salesforce/constants.py +0 -0
  335. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/salesforce/extract.py +0 -0
  336. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/salesforce/format.py +0 -0
  337. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/salesforce/format_test.py +0 -0
  338. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/salesforce/soql.py +0 -0
  339. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/__init__.py +0 -0
  340. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/client.py +0 -0
  341. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/client_test.py +0 -0
  342. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/credentials.py +0 -0
  343. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/credentials_test.py +0 -0
  344. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/.sqlfluff +0 -0
  345. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/column.sql +0 -0
  346. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/column_lineage.sql +0 -0
  347. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/database.sql +0 -0
  348. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/grant_to_role.sql +0 -0
  349. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/grant_to_user.sql +0 -0
  350. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/query.sql +0 -0
  351. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/role.sql +0 -0
  352. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/schema.sql +0 -0
  353. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/table.sql +0 -0
  354. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/user.sql +0 -0
  355. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/view_ddl.sql +0 -0
  356. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/query.py +0 -0
  357. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/__init__.py +0 -0
  358. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/client.py +0 -0
  359. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/extract.py +0 -0
  360. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/queries/.sqlfluff +0 -0
  361. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/queries/column.sql +0 -0
  362. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/queries/database.sql +0 -0
  363. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/queries/schema.sql +0 -0
  364. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/queries/table.sql +0 -0
  365. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/queries/user.sql +0 -0
  366. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/query.py +0 -0
  367. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/__init__.py +0 -0
  368. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/extract.py +0 -0
  369. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/queries/.sqlfluff +0 -0
  370. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/queries/column.sql +0 -0
  371. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/queries/database.sql +0 -0
  372. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/queries/query.sql +0 -0
  373. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/queries/schema.sql +0 -0
  374. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/queries/table.sql +0 -0
  375. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/queries/user.sql +0 -0
  376. {castor_extractor-0.16.11 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/queries/view_ddl.sql +0 -0
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.16.15 - 2024-06-07
4
+
5
+ * Tableau: extract database_name for CustomSQLTables
6
+
7
+ ## 0.16.14 - 2024-06-06
8
+
9
+ * Snowflake: Extract SQL user defined function
10
+
11
+ ## 0.16.13 - 2024-06-05
12
+
13
+ * Tableau: extract database_name for tables
14
+
15
+ ## 0.16.12 - 2024-06-04
16
+
17
+ * Databricks: Extract lineage
18
+
3
19
  ## 0.16.11 - 2024-06-03
4
20
 
5
21
  * Tableau: add extra fields to optimise storage
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: castor-extractor
3
- Version: 0.16.11
3
+ Version: 0.16.15
4
4
  Summary: Extract your metadata assets.
5
5
  Home-page: https://www.castordoc.com/
6
6
  License: EULA
@@ -5,7 +5,7 @@ import requests
5
5
 
6
6
  logger = logging.getLogger(__name__)
7
7
 
8
- DEFAULT_TIMEOUT_MS = 30_000
8
+ DEFAULT_TIMEOUT_S = 30
9
9
 
10
10
  # https://requests.readthedocs.io/en/latest/api/#requests.request
11
11
  HttpMethod = Literal["GET", "OPTIONS", "HEAD", "POST", "PUT", "PATCH", "DELETE"]
@@ -20,7 +20,7 @@ class APIClient:
20
20
  def __init__(self, host: str, token: Optional[str] = None):
21
21
  self._host = host
22
22
  self._token = token or ""
23
- self._timeout = DEFAULT_TIMEOUT_MS
23
+ self._timeout = DEFAULT_TIMEOUT_S
24
24
 
25
25
  @staticmethod
26
26
  def build_url(host: str, path: str):
@@ -44,7 +44,12 @@ class APIClient:
44
44
  ) -> Any:
45
45
  logger.debug(f"Calling {method} on {url}")
46
46
  result = requests.request(
47
- method, url, headers=self._headers(), params=params, json=data
47
+ method,
48
+ url,
49
+ headers=self._headers(),
50
+ params=params,
51
+ json=data,
52
+ timeout=self._timeout,
48
53
  )
49
54
  result.raise_for_status()
50
55
 
@@ -68,7 +68,8 @@ class Retry(BaseModel):
68
68
  self._retry_attempts += 1
69
69
  wait_ms = self.base() + self.jitter()
70
70
  wait_s = float(wait_ms) / MS_IN_SEC
71
- logger.warning(f"Attempting a new call in {wait_s} seconds")
71
+ msg = f"Attempting a new call in {wait_s} seconds, {self._retry_attempts} attempt(s) / {self.max_retries} max retries"
72
+ logger.warning(msg)
72
73
  time.sleep(wait_s)
73
74
  return True
74
75
 
@@ -93,6 +94,7 @@ def retry(
93
94
  try:
94
95
  return None, callable(*args, **kwargs)
95
96
  except exceptions_ as err:
97
+ logger.warning(f"Exception within {callable.__name__}")
96
98
  return err, None
97
99
 
98
100
  def _func(*args, **kwargs) -> Any:
@@ -31,8 +31,11 @@ _TSC_ASSETS = (
31
31
  # increase the value when extraction is too slow
32
32
  # decrease the value when timeouts arise
33
33
  _CUSTOM_PAGE_SIZE: Dict[TableauRevampAsset, int] = {
34
- # fields and columns are light but volumes are bigger
35
- TableauRevampAsset.COLUMN: 200,
34
+ # for some clients, extraction of columns tend to hit the node limit
35
+ # https://community.tableau.com/s/question/0D54T00000YuK60SAF/metadata-query-nodelimitexceeded-error
36
+ # the workaround is to reduce pagination
37
+ TableauRevampAsset.COLUMN: 50,
38
+ # fields are light but volumes are bigger
36
39
  TableauRevampAsset.FIELD: 1000,
37
40
  TableauRevampAsset.TABLE: 50,
38
41
  }
@@ -63,12 +63,21 @@ downstreamWorkbooks { id }
63
63
  id
64
64
  name
65
65
  ... on DatabaseTable {
66
- connectionType
67
66
  fullName
68
67
  schema
68
+ database {
69
+ connectionType
70
+ id
71
+ name
72
+ }
69
73
  }
70
74
  ... on CustomSQLTable {
71
75
  query
76
+ database {
77
+ connectionType
78
+ id
79
+ name
80
+ }
72
81
  }
73
82
  """
74
83
 
@@ -1,6 +1,8 @@
1
1
  from .asset import (
2
+ ADDITIONAL_LINEAGE_ASSETS,
2
3
  CATALOG_ASSETS,
3
4
  EXTERNAL_LINEAGE_ASSETS,
5
+ FUNCTIONS_ASSETS,
4
6
  QUERIES_ASSETS,
5
7
  VIEWS_ASSETS,
6
8
  SupportedAssets,
@@ -7,6 +7,8 @@ from ...types import ExternalAsset, classproperty
7
7
  class WarehouseAsset(ExternalAsset):
8
8
  """Assets that can be extracted from warehouses"""
9
9
 
10
+ ADDITIONAL_COLUMN_LINEAGE = "additional_column_lineage"
11
+ ADDITIONAL_TABLE_LINEAGE = "additional_table_lineage"
10
12
  COLUMN = "column"
11
13
  COLUMN_LINEAGE = "column_lineage" # specific to snowflake
12
14
  DATABASE = "database"
@@ -19,12 +21,15 @@ class WarehouseAsset(ExternalAsset):
19
21
  ROLE = "role"
20
22
  SCHEMA = "schema"
21
23
  TABLE = "table"
24
+ FUNCTION = "function"
22
25
  USER = "user"
23
26
  VIEW_DDL = "view_ddl"
24
27
 
25
28
  @classproperty
26
29
  def optional(cls) -> Set["WarehouseAsset"]:
27
30
  return {
31
+ WarehouseAsset.ADDITIONAL_COLUMN_LINEAGE,
32
+ WarehouseAsset.ADDITIONAL_TABLE_LINEAGE,
28
33
  WarehouseAsset.EXTERNAL_COLUMN_LINEAGE,
29
34
  WarehouseAsset.EXTERNAL_TABLE_LINEAGE,
30
35
  }
@@ -33,8 +38,10 @@ class WarehouseAsset(ExternalAsset):
33
38
  class WarehouseAssetGroup(Enum):
34
39
  """Groups of assets that can be extracted together"""
35
40
 
41
+ ADDITIONAL_LINEAGE = "additional_lineage"
36
42
  CATALOG = "catalog"
37
43
  EXTERNAL_LINEAGE = "external_lineage"
44
+ FUNCTION = "function"
38
45
  QUERY = "query"
39
46
  ROLE = "role"
40
47
  SNOWFLAKE_LINEAGE = "snowflake_lineage"
@@ -53,6 +60,7 @@ CATALOG_ASSETS = (
53
60
  )
54
61
 
55
62
  # shared by technologies supporting queries
63
+ FUNCTIONS_ASSETS = (WarehouseAsset.FUNCTION,)
56
64
  QUERIES_ASSETS = (WarehouseAsset.QUERY,)
57
65
  VIEWS_ASSETS = (WarehouseAsset.VIEW_DDL,)
58
66
 
@@ -61,6 +69,11 @@ EXTERNAL_LINEAGE_ASSETS = (
61
69
  WarehouseAsset.EXTERNAL_TABLE_LINEAGE,
62
70
  )
63
71
 
72
+ ADDITIONAL_LINEAGE_ASSETS = (
73
+ WarehouseAsset.ADDITIONAL_COLUMN_LINEAGE,
74
+ WarehouseAsset.ADDITIONAL_TABLE_LINEAGE,
75
+ )
76
+
64
77
  NON_EXTRACTABLE_ASSETS = {WarehouseAssetGroup.EXTERNAL_LINEAGE}
65
78
 
66
79
 
@@ -0,0 +1,465 @@
1
+ import logging
2
+ from concurrent.futures import ThreadPoolExecutor
3
+ from datetime import date
4
+ from functools import partial
5
+ from typing import Any, Dict, List, Optional, Set, Tuple, cast
6
+
7
+ import requests
8
+
9
+ from ...utils import (
10
+ SafeMode,
11
+ at_midnight,
12
+ date_after,
13
+ mapping_from_rows,
14
+ retry,
15
+ safe_mode,
16
+ )
17
+ from ...utils.client.api import APIClient
18
+ from ...utils.pager import PagerOnToken
19
+ from ..abstract.time_filter import TimeFilter
20
+ from .credentials import DatabricksCredentials
21
+ from .format import DatabricksFormatter
22
+ from .types import Link, Ostr, OTimestampedLink, TablesColumns, TimestampedLink
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ _MAX_NUMBER_OF_LINEAGE_ERRORS = 1000
27
+ _MAX_THREADS = 10
28
+ _RETRY_ATTEMPTS = 3
29
+ _RETRY_BASE_MS = 1000
30
+ _RETRY_EXCEPTIONS = [
31
+ requests.exceptions.ConnectTimeout,
32
+ ]
33
+
34
+ safe_params = SafeMode((BaseException,), _MAX_NUMBER_OF_LINEAGE_ERRORS)
35
+
36
+
37
+ def _day_to_epoch_ms(day: date) -> int:
38
+ return int(at_midnight(day).timestamp() * 1000)
39
+
40
+
41
+ def _day_hour_to_epoch_ms(day: date, hour: int) -> int:
42
+ return int(at_midnight(day).timestamp() * 1000) + (hour * 3600 * 1000)
43
+
44
+
45
+ class LineageLinks:
46
+ """
47
+ helper class that handles lineage deduplication and filtering
48
+ """
49
+
50
+ def __init__(self):
51
+ self.lineage: Dict[Link, Ostr] = dict()
52
+
53
+ def add(self, timestamped_link: TimestampedLink) -> None:
54
+ """
55
+ keep the most recent lineage link, adding to `self.lineage`
56
+ """
57
+ parent, child, timestamp = timestamped_link
58
+ link = (parent, child)
59
+ if not self.lineage.get(link):
60
+ self.lineage[link] = timestamp
61
+ else:
62
+ if not timestamp:
63
+ return
64
+ # keep most recent link; cast for mypy
65
+ recent = max(cast(str, self.lineage[link]), cast(str, timestamp))
66
+ self.lineage[link] = recent
67
+
68
+
69
+ class DatabricksClient(APIClient):
70
+ """Databricks Client"""
71
+
72
+ def __init__(
73
+ self,
74
+ credentials: DatabricksCredentials,
75
+ db_allowed: Optional[Set[str]] = None,
76
+ db_blocked: Optional[Set[str]] = None,
77
+ ):
78
+ super().__init__(host=credentials.host, token=credentials.token)
79
+ self._db_allowed = db_allowed
80
+ self._db_blocked = db_blocked
81
+ self.formatter = DatabricksFormatter()
82
+
83
+ @staticmethod
84
+ def name() -> str:
85
+ return "Databricks"
86
+
87
+ def _keep_catalog(self, catalog: str) -> bool:
88
+ """
89
+ Helper function to determine if we should keep the Databricks catalog
90
+ which is a CastorDoc database
91
+ """
92
+ if self._db_allowed and catalog not in self._db_allowed:
93
+ return False
94
+ if self._db_blocked and catalog in self._db_blocked:
95
+ return False
96
+ return True
97
+
98
+ def databases(self) -> List[dict]:
99
+ path = "api/2.1/unity-catalog/catalogs"
100
+ content = self.get(path=path)
101
+ _databases = self.formatter.format_database(content.get("catalogs", []))
102
+ return [d for d in _databases if self._keep_catalog(d["database_name"])]
103
+
104
+ def _schemas_of_database(self, database: dict) -> List[dict]:
105
+ path = "api/2.1/unity-catalog/schemas"
106
+ payload = {"catalog_name": database["database_name"]}
107
+ content = self.get(path=path, payload=payload)
108
+ schemas = content.get("schemas", [])
109
+ return self.formatter.format_schema(schemas, database)
110
+
111
+ def schemas(self, databases: List[dict]) -> List[dict]:
112
+ """
113
+ Get the databricks schemas (also sometimes called databases)
114
+ (which correspond to the schemas in Castor)
115
+ leveraging the unity catalog API
116
+ """
117
+ return [
118
+ schema
119
+ for database in databases
120
+ for schema in self._schemas_of_database(database)
121
+ ]
122
+
123
+ def _tables_columns_of_schema(self, schema: dict) -> TablesColumns:
124
+ path = "api/2.1/unity-catalog/tables"
125
+ payload = {
126
+ "catalog_name": schema["database_id"],
127
+ "schema_name": schema["schema_name"],
128
+ }
129
+ content = self.get(path=path, payload=payload)
130
+ return self.formatter.format_table_column(
131
+ content.get("tables", []), schema
132
+ )
133
+
134
+ @staticmethod
135
+ def _match_table_with_user(table: dict, user_mapping: dict) -> dict:
136
+ table_owner_email = table.get("owner_email")
137
+ if not table_owner_email:
138
+ return table
139
+ owner_external_id = user_mapping.get(table_owner_email)
140
+ if not owner_external_id:
141
+ return table
142
+ return {**table, "owner_external_id": owner_external_id}
143
+
144
+ @staticmethod
145
+ def _get_user_mapping(users: List[dict]) -> dict:
146
+ return {
147
+ **mapping_from_rows(users, "email", "id"),
148
+ **mapping_from_rows(users, "user_name", "id"),
149
+ }
150
+
151
+ def tables_and_columns(
152
+ self, schemas: List[dict], users: List[dict]
153
+ ) -> TablesColumns:
154
+ """
155
+ Get the databricks tables & columns leveraging the unity catalog API
156
+ """
157
+ tables: List[dict] = []
158
+ columns: List[dict] = []
159
+ user_mapping = self._get_user_mapping(users)
160
+ for schema in schemas:
161
+ t_to_add, c_to_add = self._tables_columns_of_schema(schema)
162
+ t_with_owner = [
163
+ self._match_table_with_user(table, user_mapping)
164
+ for table in t_to_add
165
+ ]
166
+ tables.extend(t_with_owner)
167
+ columns.extend(c_to_add)
168
+ return tables, columns
169
+
170
+ @staticmethod
171
+ def _to_table_path(table: dict) -> Ostr:
172
+ if table.get("name"):
173
+ return f"{table['catalog_name']}.{table['schema_name']}.{table['name']}"
174
+ return None
175
+
176
+ @staticmethod
177
+ def _to_column_path(column: dict) -> Ostr:
178
+ if column.get("name"):
179
+ return f"{column['catalog_name']}.{column['schema_name']}.{column['table_name']}.{column['name']}"
180
+ return None
181
+
182
+ def _link(
183
+ self, path_from: Ostr, path_to: Ostr, timestamp: Ostr
184
+ ) -> OTimestampedLink:
185
+ """exclude missing path and self-lineage"""
186
+ if (not path_from) or (not path_to):
187
+ return None
188
+ is_self_lineage = path_from.lower() == path_to.lower()
189
+ if is_self_lineage:
190
+ return None
191
+ return (path_from, path_to, timestamp)
192
+
193
+ def _single_table_lineage_links(
194
+ self, table_path: str, single_table_lineage: dict
195
+ ) -> List[TimestampedLink]:
196
+ """
197
+ process databricks lineage API response for a given table
198
+ returns a list of (parent, child, timestamp)
199
+
200
+ Note: in `upstreams` or `downstreams` we only care about `tableInfo`,
201
+ we could also have `notebookInfos` or `fileInfo`
202
+ """
203
+ links: List[OTimestampedLink] = []
204
+ # add parent:
205
+ for link in single_table_lineage.get("upstreams", []):
206
+ parent = link.get("tableInfo", {})
207
+ parent_path = self._to_table_path(parent)
208
+ timestamp: Ostr = parent.get("lineage_timestamp")
209
+ links.append(self._link(parent_path, table_path, timestamp))
210
+
211
+ # add children:
212
+ for link in single_table_lineage.get("downstreams", []):
213
+ child = link.get("tableInfo", {})
214
+ child_path = self._to_table_path(child)
215
+ timestamp = child.get("lineage_timestamp")
216
+ links.append(self._link(table_path, child_path, timestamp))
217
+
218
+ return list(filter(None, links))
219
+
220
+ @safe_mode(safe_params, lambda: [])
221
+ @retry(
222
+ exceptions=_RETRY_EXCEPTIONS,
223
+ max_retries=_RETRY_ATTEMPTS,
224
+ base_ms=_RETRY_BASE_MS,
225
+ )
226
+ def get_single_table_lineage(
227
+ self, table_path: str
228
+ ) -> List[TimestampedLink]:
229
+ """
230
+ Helper function used in get_lineage_links.
231
+ Call data lineage API and return the content of the result
232
+ eg table_path: broward_prd.bronze.account_adjustments
233
+ FYI: Maximum rate of 50 requests per SECOND
234
+ """
235
+ path = "api/2.0/lineage-tracking/table-lineage"
236
+ payload = {"table_name": table_path, "include_entity_lineage": True}
237
+ content = self.get(path=path, payload=payload)
238
+ return self._single_table_lineage_links(table_path, content)
239
+
240
+ def _deduplicate_lineage(self, lineages: List[TimestampedLink]) -> dict:
241
+ deduplicated_lineage = LineageLinks()
242
+ for timestamped_link in lineages:
243
+ deduplicated_lineage.add(timestamped_link)
244
+ return deduplicated_lineage.lineage
245
+
246
+ def table_lineage(self, tables: List[dict]) -> List[dict]:
247
+ """
248
+ Wrapper function that retrieves all table lineage
249
+ """
250
+ # retrieve table lineage
251
+ with ThreadPoolExecutor(max_workers=_MAX_THREADS) as executor:
252
+ table_paths = [
253
+ ".".join([table["schema_id"], table["table_name"]])
254
+ for table in tables
255
+ ]
256
+ results = executor.map(self.get_single_table_lineage, table_paths)
257
+ lineages = [link for links in results for link in links]
258
+ deduplicated = self._deduplicate_lineage(lineages)
259
+ return self.formatter.format_lineage(deduplicated)
260
+
261
+ @staticmethod
262
+ def _paths_for_column_lineage(
263
+ tables: List[dict], columns: List[dict], table_lineage: List[dict]
264
+ ) -> List[Tuple[str, str]]:
265
+ """
266
+ helper providing a list of candidate columns to look lineage for:
267
+ we only look for column lineage where there is table lineage
268
+ """
269
+ # mapping between table id and its path db.schema.table
270
+ # table["schema_id"] follows the pattern `db.schema`
271
+ mapping = {
272
+ table["id"]: ".".join([table["schema_id"], table["table_name"]])
273
+ for table in tables
274
+ }
275
+
276
+ tables_with_lineage: Set[str] = set()
277
+ for t in table_lineage:
278
+ tables_with_lineage.add(t["parent_path"])
279
+ tables_with_lineage.add(t["child_path"])
280
+
281
+ paths_to_return: List[Tuple[str, str]] = []
282
+ for column in columns:
283
+ table_path = mapping[column["table_id"]]
284
+ if table_path not in tables_with_lineage:
285
+ continue
286
+ column_ = (table_path, column["column_name"])
287
+ paths_to_return.append(column_)
288
+
289
+ return paths_to_return
290
+
291
+ def _single_column_lineage_links(
292
+ self, column_path: str, single_column_lineage: dict
293
+ ) -> List[TimestampedLink]:
294
+ """
295
+ process databricks lineage API response for a given table
296
+ returns a list of (parent, child, timestamp)
297
+
298
+ Note: in `upstreams` or `downstreams` we only care about `tableInfo`,
299
+ we could also have `notebookInfos` or `fileInfo`
300
+ """
301
+ links: List[OTimestampedLink] = []
302
+ # add parent:
303
+ for link in single_column_lineage.get("upstream_cols", []):
304
+ parent_path = self._to_column_path(link)
305
+ timestamp: Ostr = link.get("lineage_timestamp")
306
+ links.append(self._link(parent_path, column_path, timestamp))
307
+
308
+ # add children:
309
+ for link in single_column_lineage.get("downstream_cols", []):
310
+ child_path = self._to_column_path(link)
311
+ timestamp = link.get("lineage_timestamp")
312
+ links.append(self._link(column_path, child_path, timestamp))
313
+
314
+ return list(filter(None, links))
315
+
316
+ @safe_mode(safe_params, lambda: [])
317
+ @retry(
318
+ exceptions=_RETRY_EXCEPTIONS,
319
+ max_retries=_RETRY_ATTEMPTS,
320
+ base_ms=_RETRY_BASE_MS,
321
+ )
322
+ def get_single_column_lineage(
323
+ self,
324
+ names: Tuple[str, str],
325
+ ) -> List[TimestampedLink]:
326
+ """
327
+ Helper function used in get_lineage_links.
328
+ Call data lineage API and return the content of the result
329
+
330
+ eg table_path: broward_prd.bronze.account_adjustments
331
+ FYI: Maximum rate of 10 requests per SECOND
332
+ """
333
+ table_path, column_name = names
334
+ api_path = "api/2.0/lineage-tracking/column-lineage"
335
+ payload = {
336
+ "table_name": table_path,
337
+ "column_name": column_name,
338
+ "include_entity_lineage": True,
339
+ }
340
+ content = self.get(path=api_path, payload=payload)
341
+ column_path = f"{table_path}.{column_name}"
342
+ return self._single_column_lineage_links(column_path, content)
343
+
344
+ def column_lineage(
345
+ self, tables: List[dict], columns: List[dict], table_lineage: List[dict]
346
+ ) -> List[dict]:
347
+ """
348
+ Wrapper function that retrieves all column lineage
349
+ we only try to retrieve column lineage if we found table lineage
350
+ """
351
+ candidate_paths = self._paths_for_column_lineage(
352
+ tables, columns, table_lineage
353
+ )
354
+ lineages: List[TimestampedLink] = [
355
+ link
356
+ for paths in candidate_paths
357
+ for link in self.get_single_column_lineage(paths)
358
+ ]
359
+ deduplicated = self._deduplicate_lineage(lineages)
360
+ return self.formatter.format_lineage(deduplicated)
361
+
362
+ @staticmethod
363
+ def _time_filter(time_filter: Optional[TimeFilter]) -> dict:
364
+ """time filter to retrieve Databricks' queries"""
365
+ # define an explicit time window
366
+ if not time_filter:
367
+ time_filter = TimeFilter.default()
368
+
369
+ assert time_filter # for mypy
370
+
371
+ hour_min = time_filter.hour_min
372
+ hour_max = time_filter.hour_max
373
+ day = time_filter.day
374
+ if hour_min is not None and hour_max is not None: # specific window
375
+ start_time_ms = _day_hour_to_epoch_ms(day, hour_min)
376
+ # note: in practice, hour_min == hour_max (hourly query ingestion)
377
+ end_time_ms = _day_hour_to_epoch_ms(day, hour_max + 1)
378
+ else: # fallback to an extraction of the entire day
379
+ start_time_ms = _day_to_epoch_ms(day)
380
+ end_time_ms = _day_to_epoch_ms(date_after(day, 1))
381
+
382
+ return {
383
+ "filter_by": {
384
+ "query_start_time_range": {
385
+ "end_time_ms": end_time_ms,
386
+ "start_time_ms": start_time_ms,
387
+ }
388
+ }
389
+ }
390
+
391
+ def query_payload(
392
+ self,
393
+ page_token: Optional[str] = None,
394
+ max_results: Optional[int] = None,
395
+ time_range_filter: Optional[dict] = None,
396
+ ) -> dict:
397
+ """helper method to build the payload used to retrieve queries"""
398
+ # in payload: You can provide only one of 'page_token' or 'filter_by'
399
+ if page_token:
400
+ payload: Dict[str, Any] = {"page_token": page_token}
401
+ else:
402
+ if time_range_filter:
403
+ payload = {**time_range_filter}
404
+ else:
405
+ payload = self._time_filter(None) # default to yesterday
406
+ if max_results:
407
+ payload["max_results"] = max_results
408
+ return payload
409
+
410
+ def _scroll_queries(
411
+ self,
412
+ page_token: Optional[str] = None,
413
+ max_results: Optional[int] = None,
414
+ time_range_filter: Optional[dict] = None,
415
+ ) -> dict:
416
+ """
417
+ Callback to scroll the queries api
418
+ https://docs.databricks.com/api/workspace/queryhistory/list
419
+ max_results: Limit the number of results returned in one page.
420
+ The default is 100. (both on our side and Databricks')
421
+ """
422
+ path = "api/2.0/sql/history/queries"
423
+ payload = self.query_payload(page_token, max_results, time_range_filter)
424
+ content = self.get(path=path, payload=payload)
425
+ return content if content else {}
426
+
427
+ def queries(self, time_filter: Optional[TimeFilter] = None) -> List[dict]:
428
+ """get all queries"""
429
+ # add a time filter (by default: yesterday)
430
+ time_range_filter = self._time_filter(time_filter)
431
+ _time_filtered_scroll_queries = partial(
432
+ self._scroll_queries,
433
+ time_range_filter=time_range_filter,
434
+ )
435
+
436
+ # retrieve all queries using pagination
437
+ raw_queries = PagerOnToken(_time_filtered_scroll_queries).all()
438
+
439
+ return self.formatter.format_query(raw_queries)
440
+
441
+ def users(self) -> List[dict]:
442
+ """
443
+ retrieve user from api
444
+ """
445
+ path = "api/2.0/preview/scim/v2/Users"
446
+ content = self.get(path=path)
447
+ return self.formatter.format_user(content.get("Resources", []))
448
+
449
+ def _view_ddl(self, schema: dict) -> List[dict]:
450
+ path = "api/2.1/unity-catalog/tables"
451
+ payload = {
452
+ "catalog_name": schema["database_id"],
453
+ "schema_name": schema["schema_name"],
454
+ "omit_columns": True,
455
+ }
456
+ content = self.get(path=path, payload=payload)
457
+ return self.formatter.format_view_ddl(content.get("tables", []), schema)
458
+
459
+ def view_ddl(self, schemas: List[dict]) -> List[dict]:
460
+ """retrieve view ddl"""
461
+ view_ddl: List[dict] = []
462
+ for schema in schemas:
463
+ v_to_add = self._view_ddl(schema)
464
+ view_ddl.extend(v_to_add)
465
+ return view_ddl