castor-extractor 0.16.9__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.
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/CHANGELOG.md +24 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/PKG-INFO +1 -1
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/client/api.py +8 -3
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/retry.py +3 -1
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/client/client.py +8 -2
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/client/gql_queries.py +15 -2
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/abstract/__init__.py +2 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/abstract/asset.py +13 -0
- castor_extractor-0.16.15/castor_extractor/warehouse/databricks/client.py +465 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/databricks/client_test.py +61 -1
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/databricks/extract.py +36 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/databricks/format.py +13 -0
- castor_extractor-0.16.15/castor_extractor/warehouse/databricks/test_constants.py +79 -0
- castor_extractor-0.16.15/castor_extractor/warehouse/databricks/types.py +8 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/salesforce/client.py +8 -6
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/salesforce/extract.py +2 -2
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/salesforce/format.py +34 -7
- castor_extractor-0.16.15/castor_extractor/warehouse/salesforce/format_test.py +80 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/extract.py +2 -0
- castor_extractor-0.16.15/castor_extractor/warehouse/snowflake/queries/function.sql +10 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/pyproject.toml +1 -1
- castor_extractor-0.16.9/castor_extractor/warehouse/databricks/client.py +0 -229
- castor_extractor-0.16.9/castor_extractor/warehouse/databricks/types.py +0 -3
- castor_extractor-0.16.9/castor_extractor/warehouse/salesforce/format_test.py +0 -32
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/Dockerfile +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/LICENCE +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/README.md +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_bigquery.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_databricks.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_domo.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_looker.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_metabase_api.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_metabase_db.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_mode.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_mysql.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_postgres.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_powerbi.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_qlik.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_redshift.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_salesforce.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_salesforce_reporting.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_sigma.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_snowflake.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_sqlserver.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/extract_tableau.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/file_check.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/commands/upload.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/file_checker/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/file_checker/column.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/file_checker/column_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/file_checker/constants.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/file_checker/enums.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/file_checker/file.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/file_checker/file_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/file_checker/file_test_users.csv +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/file_checker/file_test_users_valid.csv +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/file_checker/templates/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/file_checker/templates/generic_warehouse.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/logger.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/types.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/uploader/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/uploader/constant.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/uploader/env.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/uploader/env_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/uploader/upload.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/uploader/upload_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/uploader/utils.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/client/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/client/abstract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/client/api_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/client/postgres.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/client/query.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/client/uri.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/client/uri_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/collection.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/collection_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/constants.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/dbt/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/dbt/assets.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/dbt/client.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/dbt/client_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/dbt/credentials.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/deprecate.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/env.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/files.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/files_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/formatter.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/formatter_test.csv +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/formatter_test.json +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/formatter_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/json_stream_write.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/load.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/object.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/object_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/pager/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/pager/pager.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/pager/pager_on_id.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/pager/pager_on_id_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/pager/pager_on_token.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/pager/pager_on_token_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/pager/pager_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/retry_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/safe.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/safe_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/salesforce/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/salesforce/client.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/salesforce/client_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/salesforce/constants.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/salesforce/credentials.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/salesforce/credentials_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/store.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/string.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/string_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/time.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/time_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/type.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/validation.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/validation_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/utils/write.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/assets.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/client/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/client/client.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/client/client_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/client/credentials.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/client/endpoints.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/client/pagination.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/client/pagination_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/constants.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/domo/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/api/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/api/client.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/api/client_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/api/constants.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/api/sdk.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/api/sdk_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/api/utils.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/assets.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/constant.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/env.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/fields.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/fields_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/multithreading.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/looker/parameters.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/assets.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/api/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/api/client.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/api/client_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/api/credentials.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/client.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/credentials.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/.sqlfluff +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/base_url.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/card.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/collection.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/dashboard.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/dashboard_cards.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/database.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/table.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/db/queries/user.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/decryption.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/decryption_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/client/shared.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/errors.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/metabase/types.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/assets.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/client/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/client/client.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/client/client_test.json +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/client/client_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/client/constants.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/client/credentials.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/errors.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/mode/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/assets.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/client/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/client/constants.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/client/credentials.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/client/credentials_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/client/rest.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/client/rest_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/client/utils.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/client/utils_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/powerbi/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/assets.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/constants.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/engine/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/engine/client.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/engine/constants.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/engine/error.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/engine/error_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/engine/json_rpc.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/engine/json_rpc_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/engine/websocket.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/master.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/rest.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/client/rest_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/constants.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/qlik/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/salesforce_reporting/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/salesforce_reporting/assets.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/salesforce_reporting/client/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/salesforce_reporting/client/rest.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/salesforce_reporting/client/soql.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/salesforce_reporting/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/assets.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/client/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/client/client.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/client/client_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/client/credentials.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/client/endpoints.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/client/pagination.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/constants.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/sigma/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/assets.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/client/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/client/client.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/client/client_utils.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/client/credentials.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/client/project.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/client/safe_mode.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/constants.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/errors.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/gql_fields.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/assets/graphql/metadata/metadata_1_get.json +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/assets/graphql/metadata/metadata_2_get.json +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/assets/rest_api/auth.xml +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/assets/rest_api/project_get.xml +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/assets/rest_api/user_get.xml +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/assets/rest_api/view_get_usage.xml +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/assets/rest_api/workbook_get.xml +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/graphql/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/graphql/paginated_object_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/rest_api/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/rest_api/auth_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/rest_api/credentials_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/rest_api/projects_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/rest_api/usages_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/rest_api/users_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/rest_api/workbooks_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/utils/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tests/unit/utils/env_key.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/tsc_fields.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/types.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau/usage.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/assets.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/client/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/client/credentials.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/client/errors.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/client/tsc_fields.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/constants.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/visualization/tableau_revamp/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/abstract/asset_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/abstract/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/abstract/query.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/abstract/time_filter.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/abstract/time_filter_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/client.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/client_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/credentials.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/.sqlfluff +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/column.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/cte/sharded.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/database.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/query.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/schema.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/table.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/table_with_tags.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/user.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/queries/view_ddl.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/query.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/bigquery/types.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/databricks/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/databricks/credentials.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/databricks/format_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/client.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/client_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/queries/.sqlfluff +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/queries/column.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/queries/database.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/queries/query.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/queries/schema.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/queries/table.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/queries/user.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/queries/view_ddl.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/mysql/query.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/queries/.sqlfluff +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/queries/column.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/queries/database.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/queries/group.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/queries/schema.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/queries/table.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/queries/user.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/postgres/query.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/client.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/client_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/.sqlfluff +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/column.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/database.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/group.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/query.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/schema.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/table.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/table_freshness.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/user.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/queries/view_ddl.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/redshift/query.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/salesforce/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/salesforce/constants.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/salesforce/soql.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/client.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/client_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/credentials.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/credentials_test.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/.sqlfluff +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/column.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/column_lineage.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/database.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/grant_to_role.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/grant_to_user.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/query.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/role.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/schema.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/table.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/user.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/queries/view_ddl.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/snowflake/query.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/client.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/queries/.sqlfluff +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/queries/column.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/queries/database.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/queries/schema.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/queries/table.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/queries/user.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/sqlserver/query.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/__init__.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/extract.py +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/queries/.sqlfluff +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/queries/column.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/queries/database.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/queries/query.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/queries/schema.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/queries/table.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/queries/user.sql +0 -0
- {castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/synapse/queries/view_ddl.sql +0 -0
|
@@ -1,5 +1,29 @@
|
|
|
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
|
+
|
|
19
|
+
## 0.16.11 - 2024-06-03
|
|
20
|
+
|
|
21
|
+
* Tableau: add extra fields to optimise storage
|
|
22
|
+
|
|
23
|
+
## 0.16.10 - 2024-05-30
|
|
24
|
+
|
|
25
|
+
* Salesforce: extract sobjects Label as table name
|
|
26
|
+
|
|
3
27
|
## 0.16.9 - 2024-05-28
|
|
4
28
|
|
|
5
29
|
* Tableau: extract only fields that are necessary
|
|
@@ -5,7 +5,7 @@ import requests
|
|
|
5
5
|
|
|
6
6
|
logger = logging.getLogger(__name__)
|
|
7
7
|
|
|
8
|
-
|
|
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 =
|
|
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,
|
|
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
|
-
|
|
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:
|
|
@@ -28,10 +28,16 @@ _TSC_ASSETS = (
|
|
|
28
28
|
TableauRevampAsset.USAGE,
|
|
29
29
|
)
|
|
30
30
|
|
|
31
|
-
#
|
|
31
|
+
# increase the value when extraction is too slow
|
|
32
|
+
# decrease the value when timeouts arise
|
|
32
33
|
_CUSTOM_PAGE_SIZE: Dict[TableauRevampAsset, int] = {
|
|
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
|
|
33
39
|
TableauRevampAsset.FIELD: 1000,
|
|
34
|
-
TableauRevampAsset.
|
|
40
|
+
TableauRevampAsset.TABLE: 50,
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
|
|
@@ -18,7 +18,11 @@ QUERY_TEMPLATE = """
|
|
|
18
18
|
|
|
19
19
|
_COLUMNS_QUERY = """
|
|
20
20
|
downstreamDashboards { id }
|
|
21
|
-
downstreamFields {
|
|
21
|
+
downstreamFields {
|
|
22
|
+
id
|
|
23
|
+
__typename
|
|
24
|
+
datasource { id }
|
|
25
|
+
}
|
|
22
26
|
downstreamWorkbooks { id }
|
|
23
27
|
id
|
|
24
28
|
name
|
|
@@ -59,12 +63,21 @@ downstreamWorkbooks { id }
|
|
|
59
63
|
id
|
|
60
64
|
name
|
|
61
65
|
... on DatabaseTable {
|
|
62
|
-
connectionType
|
|
63
66
|
fullName
|
|
64
67
|
schema
|
|
68
|
+
database {
|
|
69
|
+
connectionType
|
|
70
|
+
id
|
|
71
|
+
name
|
|
72
|
+
}
|
|
65
73
|
}
|
|
66
74
|
... on CustomSQLTable {
|
|
67
75
|
query
|
|
76
|
+
database {
|
|
77
|
+
connectionType
|
|
78
|
+
id
|
|
79
|
+
name
|
|
80
|
+
}
|
|
68
81
|
}
|
|
69
82
|
"""
|
|
70
83
|
|
{castor_extractor-0.16.9 → castor_extractor-0.16.15}/castor_extractor/warehouse/abstract/asset.py
RENAMED
|
@@ -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
|