datacontract-cli 1.2.8__tar.gz → 1.2.9__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.
- {datacontract_cli-1.2.8/datacontract_cli.egg-info → datacontract_cli-1.2.9}/PKG-INFO +8 -7
- datacontract_cli-1.2.9/datacontract/__init__.py +30 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/api.py +57 -13
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/cli.py +1 -2
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/config/settings.py +16 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/checks/check_spec.py +2 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/checks/create_checks.py +14 -6
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/checks/custom_quality_check.py +118 -36
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/data_contract_test.py +4 -1
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/hana/check_hana_execute.py +2 -1
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/hana/hana_quality_check.py +4 -3
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/connections/connect.py +23 -1
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/ibis_check_execute.py +61 -21
- datacontract_cli-1.2.9/datacontract/engines/ibis/snowflake_nested_values.py +149 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/sql_exporter.py +82 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9/datacontract_cli.egg-info}/PKG-INFO +8 -7
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract_cli.egg-info/SOURCES.txt +1 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract_cli.egg-info/requires.txt +7 -6
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/pyproject.toml +10 -8
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_api.py +213 -3
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_connect_athena.py +1 -1
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_connect_bigquery.py +54 -2
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_connect_snowflake.py +71 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_create_checks_sql_placeholders.py +10 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_custom_quality_checks.py +160 -3
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_docs_ordering.py +2 -2
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_sql.py +147 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_hana_quality_check.py +15 -3
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_snowflake.py +2 -1
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_nested_path_checks.py +44 -2
- datacontract_cli-1.2.8/datacontract/__init__.py +0 -17
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/LICENSE +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/MANIFEST.in +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/README.md +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/breaking/__init__.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/breaking/detector.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/breaking/rules.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/catalog/catalog.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/changelog/__init__.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/changelog/changelog.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/changelog/normalize.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/command_api.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/command_breaking.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/command_catalog.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/command_changelog.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/command_ci.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/command_dbt.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/command_edit.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/command_export.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/command_import.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/command_init.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/command_lint.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/command_publish.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/command_test.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/config/__init__.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/config/cli_context.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/config/variables.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/data_contract.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/editor_assets/VERSION +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/editor_assets/assets/editor.worker-4.7.0.js +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/editor_assets/assets/yaml.worker-4.7.0.js +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/editor_assets/datacontract-editor-version.json +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/editor_assets/datacontract-editor.css +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/editor_assets/datacontract-editor.es.js +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/editor_assets/index.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/editor_assets/logo_fuchsia_v2.svg +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/editor_assets/monaco-4.7.0.js +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/editor_assets/odcs.svg +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/editor_assets/robots.txt +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/editor_assets/rolldown-runtime-CEFd7nDs.js +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/editor_assets/sitemap.xml +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/__init__.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/checks/__init__.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/checks/check_filter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/checks/dimensions.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/checks/physical_type_match.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/checks/severity.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/checks/sql_guard.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/checks/type_normalize.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/datacontract/check_azure_blob_file.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/datacontract/check_that_datacontract_contains_valid_servers_configuration.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/datacontract/check_that_datacontract_file_exists.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/fastjsonschema/check_jsonschema.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/fastjsonschema/s3/s3_read_files.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/hana/__init__.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/hana/hana_check_selection.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/hana/hana_connection.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/hana/hana_schema_check.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/hana/hana_type_mapping.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/http_api.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/__init__.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/connections/__init__.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/connections/athena_patch.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/connections/aws_credentials.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/connections/databricks_patch.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/connections/duckdb_connection.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/connections/exasol_patch.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/connections/hive_patch.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/connections/iceberg.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/connections/kafka.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/connections/oracle_patch.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/connections/redshift_credentials.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/connections/redshift_patch.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/dtype_category.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/native_type.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/ibis/snowflake_structured_types.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/__init__.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/avro_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/avro_idl_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/bigquery_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/custom_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/data_caterer_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/dbml_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/dbt_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/dcs_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/dqx_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/duckdb_type_converter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/excel_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/exporter_factory.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/go_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/great_expectations_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/html_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/iceberg_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/jsonschema_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/markdown_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/mermaid_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/odcs_v3_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/pandas_type_converter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/protobuf_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/pydantic_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/rdf_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/sodacl_check_builder.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/sodacl_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/spark_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/sql_type_converter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/sqlalchemy_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/export/xsd_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/athena_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/avro_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/bigquery_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/clickhouse_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/csv_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/dbml_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/dbt_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/dcs_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/excel_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/glue_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/hive_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/iceberg_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/importer_factory.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/json_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/jsonschema_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/mysql_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/object_storage_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/odata_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/odcs_helper.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/odcs_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/openapi_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/oracle_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/parquet_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/postgres_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/powerbi_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/protobuf_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/pydantic_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/redshift_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/snowflake_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/spark_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/spark_type_json.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/sql_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/sqlserver_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/trino_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/unity_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/imports/xsd_importer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/init/init_template.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/integration/dbt_sync.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/integration/dbt_test_mapping.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/integration/entropy_data.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/lint/constraints.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/lint/files.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/lint/resolve.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/lint/resources.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/lint/s3.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/lint/schema.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/lint/urls.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/model/breaking.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/model/changelog.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/model/enum_values.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/model/exceptions.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/model/map_type.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/model/natural_keys.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/model/odcs.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/model/run.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/model/server.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/model/vector_type.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/model/workbook.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/output/__init__.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/output/ci_output.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/output/json_test_results.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/output/junit_test_results.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/output/output_format.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/output/test_results_writer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/output/text_breaking_results.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/output/text_changelog_results.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/py.typed +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/schemas/datacontract-1.1.0.init.yaml +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/schemas/datacontract-1.1.0.schema.json +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/schemas/datacontract-1.2.0.init.yaml +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/schemas/datacontract-1.2.0.schema.json +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/schemas/datacontract-1.2.1.init.yaml +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/schemas/datacontract-1.2.1.schema.json +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/schemas/odcs-3.0.2.schema.json +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/schemas/odcs-3.1.0.schema.json +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/schemas/odcs-3.2.0.init.yaml +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/schemas/odcs-3.2.0.schema.json +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/datacontract.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/datacontract_odcs.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/excel/odcs-template-v3.0.xlsx +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/excel/odcs-template-v3.1.xlsx +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/excel/odcs-template-v3.2.xlsx +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/index.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/macros/authoritative_definitions.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/macros/context.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/macros/schema.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/macros/server.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/macros/support.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials/datacontract_information.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials/datacontract_servicelevels.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials/datacontract_terms.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials/definition.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials/example.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials/model_field.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials/quality.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials/server.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials_odcs/context.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials_odcs/custom_properties.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials_odcs/diagram.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials_odcs/dialog_yaml.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials_odcs/footer.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials_odcs/fundamentals.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials_odcs/header.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials_odcs/pricing.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials_odcs/roles.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials_odcs/schema.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials_odcs/servers.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials_odcs/sla.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials_odcs/support.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials_odcs/team.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials_odcs/term_of_use.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/partials_odcs/title.html +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/templates/style/output.css +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract_cli.egg-info/dependency_links.txt +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract_cli.egg-info/entry_points.txt +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract_cli.egg-info/top_level.txt +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/setup.cfg +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_api_concurrency.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_api_openapi.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_breaking.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_breaking_odcs_3_2.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_catalog.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_changelog.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_changelog_engine.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_changelog_normalize.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_changelog_output_text.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_check_quality_provenance.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_ci_output.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_cli.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_command_api.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_command_api_options.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_config.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_config_file.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_config_require.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_config_threading.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_connect_clickhouse.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_connect_databricks.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_connect_exasol.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_connect_hive.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_connect_impala.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_connect_mysql.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_connect_oracle.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_connect_postgres.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_connect_redshift.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_connect_s3_credentials.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_connect_sqlserver.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_connect_trino.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_create_checks_array_items.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_create_checks_nested.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_create_checks_nested_type.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_create_checks_physical_type.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_create_checks_primary_key.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_create_checks_retention_period.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_create_checks_servicelevel.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_data_contract.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_data_contract_checks.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_data_contract_specification.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_dbt_sync.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_dbt_test_mapping.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_description_linter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_docs_commands.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_docs_config_options.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_download_datacontract_file.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_dtype_category.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_duckdb_json.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_edit.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_enum_values.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_avro.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_avro_idl.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_bigquery.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_complex_data_contract.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_custom.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_custom_exporter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_custom_model.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_data_caterer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_dbml.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_dbt_models.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_dbt_sources.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_dbt_staging_sql.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_dcs.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_dqx.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_excel.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_go.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_great_expectations.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_html.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_iceberg.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_jsonschema.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_large_contract.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_markdown.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_mermaid.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_odcs_v3.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_protobuf.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_pydantic.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_rdf.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_sodacl.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_spark.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_sql_clickhouse.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_sql_query.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_sqlalchemy.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_export_xsd.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_hana_connection.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_hana_schema_check.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_hana_type_mapping.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_ibis_check_execute.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_ibis_mssql_pattern.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_ibis_mssql_schema.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_ibis_oracle_schema.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_athena.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_avro.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_bigquery.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_clickhouse.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_csv.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_dbml.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_dbt.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_excel.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_glue.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_hive.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_iceberg.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_json.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_jsonschema.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_mysql.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_object_storage.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_odata.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_odcs_v3.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_openapi.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_oracle.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_parquet.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_postgres.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_powerbi.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_protobuf.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_pydantic_model.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_redshift.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_spark_type_json.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_sql_clickhouse.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_sql_hive.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_sql_oracle.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_sql_postgres.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_sql_snowflake.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_sql_sqlserver.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_sql_struct.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_sqlserver.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_trino.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_unity_file.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_unmapped_types.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_import_xsd.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_install_extras.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_integration_entropydata.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_lint.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_lint_files.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_lint_odcs_3_2_constraints.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_lint_schema.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_lint_urls.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_map_type.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_metadata_fields.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_native_type_schema_scope.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_oracle_patch.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_physical_type_match.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_publish_test_results.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_publish_test_results_payload.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_quality_sql_dialects.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_quality_unrunnable_rules.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_redshift_credentials.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_redshift_patch.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_resolve.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_resolve_definitions.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_resolve_local_definitions.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_roundtrip_jsonschema.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_roundtrip_pydantic_model.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_roundtrip_xsd.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_row_filter_duplicate_count.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_run_check_deprecated_fields.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_run_check_engine.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_server_overrides.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_server_type_mssql.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_server_type_synonyms.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_servers_3_2_0.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_snowflake_structured_types.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_sql_type_converter_physicaltype.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_api.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_api_endpoints.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_api_openapi_import.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_array_items.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_athena_iceberg.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_azure_blob_file.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_azure_remote.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_bigquery.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_checks_filter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_clickhouse.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_databricks.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_delta.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_diagnostics.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_dimension_filter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_dry_run.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_duckdb.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_failed_samples.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_gcs_csv_remote.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_gcs_json_remote.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_hana.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_hive.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_iceberg.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_iceberg_rest.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_iceberg_s3tables.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_kafka.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_kafka_decoding.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_kafka_remote.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_local_json.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_local_json_nd.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_local_xlsx.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_local_xml.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_metadata_only.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_mysql.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_nested_checks.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_oracle.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_oracle_xe.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_output_json.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_output_junit.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_parquet.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_postgres.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_quality.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_quality_id_and_tag_filter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_quality_local_fallback.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_quality_percent_severity.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_redshift.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_relationships.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_results_writer.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_row_filter.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_s3_csv.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_s3_delta.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_s3_json.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_s3_json_complex.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_s3_json_multiple_files.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_s3_json_multiple_models.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_s3_xlsx.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_schema_evolution.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_server_not_found.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_snowflake.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_soda_connection_error.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_sqlserver.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_trino.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_test_variables.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_type_normalize.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_untrusted_contract.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_untrusted_contract_variables.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_variables.py +0 -0
- {datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/tests/test_vector_type.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datacontract-cli
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.9
|
|
4
4
|
Summary: The datacontract CLI is an open source command-line tool for working with Data Contracts. It uses data contract YAML files to lint the data contract, connect to data sources and execute schema and quality tests, detect breaking changes, and export to different formats. The tool is written in Python. It can be used as a standalone CLI tool, in a CI/CD pipeline, or directly as a Python library.
|
|
5
5
|
Author-email: Jochen Christ <jochen.christ@entropy-data.com>, Stefan Negele <stefan.negele@innoq.com>, Simon Harrer <simon.harrer@entropy-data.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -73,7 +73,7 @@ Requires-Dist: rich>=12.4.4; extra == "dataframe"
|
|
|
73
73
|
Provides-Extra: databricks
|
|
74
74
|
Requires-Dist: ibis-framework[databricks]<13.0.0,>=10.0.0; extra == "databricks"
|
|
75
75
|
Requires-Dist: databricks-sql-connector<4.7.0,>=3.7.0; extra == "databricks"
|
|
76
|
-
Requires-Dist: databricks-sdk<0.
|
|
76
|
+
Requires-Dist: databricks-sdk<0.157.0; extra == "databricks"
|
|
77
77
|
Requires-Dist: packaging<27.0,>=21.3; extra == "databricks"
|
|
78
78
|
Provides-Extra: databricks-runtime
|
|
79
79
|
Requires-Dist: datacontract-cli[databricks]; extra == "databricks-runtime"
|
|
@@ -107,8 +107,9 @@ Requires-Dist: duckdb-extension-azure<1.6.0,>=1.5.0; extra == "azure"
|
|
|
107
107
|
Requires-Dist: azure-identity<1.27,>=1.17.1; extra == "azure"
|
|
108
108
|
Requires-Dist: azure-storage-blob<12.32,>=12.24; extra == "azure"
|
|
109
109
|
Provides-Extra: snowflake
|
|
110
|
-
Requires-Dist: snowflake-connector-python[pandas]<
|
|
110
|
+
Requires-Dist: snowflake-connector-python[pandas]<5.2,>=3.17; extra == "snowflake"
|
|
111
111
|
Requires-Dist: ibis-framework[snowflake]<13.0.0,>=10.0.0; extra == "snowflake"
|
|
112
|
+
Requires-Dist: packaging<27.0,>=21.3; extra == "snowflake"
|
|
112
113
|
Provides-Extra: sqlserver
|
|
113
114
|
Requires-Dist: ibis-framework[mssql]<13.0.0,>=10.0.0; extra == "sqlserver"
|
|
114
115
|
Requires-Dist: packaging<27.0,>=21.3; extra == "sqlserver"
|
|
@@ -144,7 +145,7 @@ Requires-Dist: pyarrow>=18.1.0; extra == "parquet"
|
|
|
144
145
|
Provides-Extra: rdf
|
|
145
146
|
Requires-Dist: rdflib==7.6.0; extra == "rdf"
|
|
146
147
|
Provides-Extra: api
|
|
147
|
-
Requires-Dist: fastapi<0.
|
|
148
|
+
Requires-Dist: fastapi<0.162.0,>=0.115.0; extra == "api"
|
|
148
149
|
Requires-Dist: uvicorn<0.55.0,>=0.44.0; extra == "api"
|
|
149
150
|
Provides-Extra: protobuf
|
|
150
151
|
Requires-Dist: proto-schema-parser<3.0,>=1.0; extra == "protobuf"
|
|
@@ -164,10 +165,10 @@ Requires-Dist: pre-commit<4.7.0,>=3.7.1; extra == "dev"
|
|
|
164
165
|
Requires-Dist: pytest; extra == "dev"
|
|
165
166
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
166
167
|
Requires-Dist: pytest-xdist; extra == "dev"
|
|
167
|
-
Requires-Dist: pymssql==2.4.
|
|
168
|
+
Requires-Dist: pymssql==2.4.5; extra == "dev"
|
|
168
169
|
Requires-Dist: psycopg2-binary<3.0,>=2.9; extra == "dev"
|
|
169
|
-
Requires-Dist: mysql-connector-python<26.
|
|
170
|
-
Requires-Dist: ruff==0.17.
|
|
170
|
+
Requires-Dist: mysql-connector-python<26.11.0,>=8.0.30; extra == "dev"
|
|
171
|
+
Requires-Dist: ruff==0.17.2; extra == "dev"
|
|
171
172
|
Requires-Dist: testcontainers[kafka,minio,mssql,mysql,postgres]==4.15.0; extra == "dev"
|
|
172
173
|
Requires-Dist: trino==0.340.0; extra == "dev"
|
|
173
174
|
Dynamic: license-file
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Configuration so that yaml.safe_dump dumps strings with line breaks with yaml literal |
|
|
2
|
+
import warnings
|
|
3
|
+
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
yaml.SafeDumper.org_represent_str = yaml.SafeDumper.represent_str
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def repr_str(dumper, data):
|
|
10
|
+
if "\n" in data:
|
|
11
|
+
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
|
|
12
|
+
return dumper.org_represent_str(data)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
yaml.add_representer(str, repr_str, Dumper=yaml.SafeDumper)
|
|
16
|
+
|
|
17
|
+
# snowflake-connector-python 5.x warns on every connection about a changing logout default, and about the 4.x
|
|
18
|
+
# parameter names we keep passing because Python 3.10 installs still get 4.x.
|
|
19
|
+
warnings.filterwarnings(
|
|
20
|
+
"ignore", message="enable_server_session_keep_alive_auto_detection was not set", category=FutureWarning
|
|
21
|
+
)
|
|
22
|
+
warnings.filterwarnings(
|
|
23
|
+
"ignore",
|
|
24
|
+
message="'(private_key_file_pwd|network_timeout|socket_timeout)' is deprecated",
|
|
25
|
+
category=DeprecationWarning,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
from datacontract.config import Config # noqa: E402
|
|
29
|
+
|
|
30
|
+
__all__ = ["Config"]
|
|
@@ -12,7 +12,7 @@ from fastapi.security.api_key import APIKeyHeader
|
|
|
12
12
|
from pydantic import BaseModel, Field, ValidationError
|
|
13
13
|
|
|
14
14
|
from datacontract.config import Config, known_env_names
|
|
15
|
-
from datacontract.config.variables import CONTRACT_VARIABLES_ENV, allowed_environment,
|
|
15
|
+
from datacontract.config.variables import CONTRACT_VARIABLES_ENV, allowed_environment, resolve_server_variables
|
|
16
16
|
from datacontract.data_contract import DataContract, ExportFormat
|
|
17
17
|
from datacontract.model.breaking import BreakingChangeEntry, Perspective
|
|
18
18
|
from datacontract.model.changelog import ChangelogEntry
|
|
@@ -443,6 +443,22 @@ AUTHENTICATION_RESPONSES: dict[int | str, dict] = {
|
|
|
443
443
|
|
|
444
444
|
_CONFIG_HEADER_PREFIX = "datacontract-"
|
|
445
445
|
|
|
446
|
+
# Options naming a file or folder on the server's disk, or the server's own workload identity: the operator's
|
|
447
|
+
# to choose, never the caller's, or a caller could have the server log in as the operator to a host of their choosing.
|
|
448
|
+
_SERVER_ONLY_OPTIONS = frozenset(
|
|
449
|
+
{
|
|
450
|
+
"DATACONTRACT_CUSTOM_QUALITY_CHECKS",
|
|
451
|
+
"DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_FILE",
|
|
452
|
+
"DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_PATH",
|
|
453
|
+
"DATACONTRACT_SNOWFLAKE_TOKEN_FILE_PATH",
|
|
454
|
+
"DATACONTRACT_SNOWFLAKE_WORKLOAD_IDENTITY_PROVIDER",
|
|
455
|
+
"DATACONTRACT_BIGQUERY_ACCOUNT_INFO_JSON_PATH",
|
|
456
|
+
"DATACONTRACT_DATABRICKS_PROFILE",
|
|
457
|
+
"DATACONTRACT_ORACLE_CLIENT_DIR",
|
|
458
|
+
"DATACONTRACT_DUCKDB_DATABASE",
|
|
459
|
+
}
|
|
460
|
+
)
|
|
461
|
+
|
|
446
462
|
|
|
447
463
|
def config_from_headers(headers) -> "Config | None":
|
|
448
464
|
"""Build a per-request Config from configuration option headers.
|
|
@@ -452,8 +468,8 @@ def config_from_headers(headers) -> "Config | None":
|
|
|
452
468
|
→ ``DATACONTRACT_SNOWFLAKE_PASSWORD``, ``entropy-data-api-key`` →
|
|
453
469
|
``ENTROPY_DATA_API_KEY``. Returns None when no config headers are present,
|
|
454
470
|
so env-var-configured deployments behave exactly as before. Unknown
|
|
455
|
-
``datacontract-*`` option names are rejected with a 400, and so
|
|
456
|
-
|
|
471
|
+
``datacontract-*`` option names are rejected with a 400, and so are options
|
|
472
|
+
only the operator may set.
|
|
457
473
|
"""
|
|
458
474
|
known = known_env_names()
|
|
459
475
|
values = {}
|
|
@@ -461,11 +477,10 @@ def config_from_headers(headers) -> "Config | None":
|
|
|
461
477
|
lowered = name.lower()
|
|
462
478
|
env = lowered.upper().replace("-", "_")
|
|
463
479
|
if lowered.startswith(_CONFIG_HEADER_PREFIX) or env in known:
|
|
464
|
-
if env
|
|
465
|
-
# A folder on the server's disk is the operator's to choose, never the caller's.
|
|
480
|
+
if env in _SERVER_ONLY_OPTIONS:
|
|
466
481
|
raise HTTPException(
|
|
467
482
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
468
|
-
detail="
|
|
483
|
+
detail=f"{env} is set on the API server. It is not possible to override it per request.",
|
|
469
484
|
)
|
|
470
485
|
values[env] = value
|
|
471
486
|
if not values:
|
|
@@ -516,12 +531,25 @@ _ENVIRONMENT_CREDENTIAL_TARGETS: dict[str, tuple[str, str | None, tuple[str, ...
|
|
|
516
531
|
"hive": ("host", "hive_host", ("hive_password",)),
|
|
517
532
|
"redshift": ("host", "redshift_host", ("redshift_password",)),
|
|
518
533
|
"sqlserver": ("host", "sqlserver_host", ("sqlserver_password",)),
|
|
534
|
+
"mssql": ("host", "sqlserver_host", ("sqlserver_password",)),
|
|
519
535
|
"snowflake": (
|
|
520
536
|
"account",
|
|
521
537
|
"snowflake_account",
|
|
522
|
-
(
|
|
538
|
+
(
|
|
539
|
+
"snowflake_password",
|
|
540
|
+
"snowflake_token",
|
|
541
|
+
"snowflake_private_key",
|
|
542
|
+
"snowflake_private_key_file",
|
|
543
|
+
"snowflake_private_key_path",
|
|
544
|
+
"snowflake_token_file_path",
|
|
545
|
+
"snowflake_workload_identity_provider",
|
|
546
|
+
),
|
|
547
|
+
),
|
|
548
|
+
"databricks": (
|
|
549
|
+
"host",
|
|
550
|
+
"databricks_server_hostname",
|
|
551
|
+
("databricks_token", "databricks_client_secret", "databricks_profile"),
|
|
523
552
|
),
|
|
524
|
-
"databricks": ("host", "databricks_server_hostname", ("databricks_token", "databricks_client_secret")),
|
|
525
553
|
# Kafka's broker host is always the contract's `server.host` (no environment
|
|
526
554
|
# option pins it), so an environment-held SASL password can never be paired
|
|
527
555
|
# with a posted contract safely -- host_field is None, so it is always rejected.
|
|
@@ -539,13 +567,17 @@ def _selected_server(body: str, server_name: str | None, config):
|
|
|
539
567
|
"""
|
|
540
568
|
from datacontract.engines.data_contract_test import get_server
|
|
541
569
|
from datacontract.lint import resolve
|
|
570
|
+
from datacontract.model.server import get_server_type
|
|
542
571
|
|
|
543
572
|
try:
|
|
544
573
|
data_contract = resolve.resolve_data_contract(data_contract_str=body, config=config)
|
|
545
574
|
server = get_server(data_contract, server_name)
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
575
|
+
if server is None:
|
|
576
|
+
return None
|
|
577
|
+
# Guard the server type the test will connect to: references like ${VAR:-local} resolved, and
|
|
578
|
+
# `custom` + customType or an ODCS synonym such as `postgresql` mapped to the type the CLI dispatches on.
|
|
579
|
+
server = resolve_server_variables(server, allowed_environment(_allowed_contract_variables()))
|
|
580
|
+
return server.model_copy(update={"type": get_server_type(server)})
|
|
549
581
|
except Exception:
|
|
550
582
|
return None
|
|
551
583
|
|
|
@@ -630,11 +662,23 @@ def _reject_environment_credentials_to_untrusted_host(body: str, server_name: st
|
|
|
630
662
|
target_attr, host_field, secret_fields = guard
|
|
631
663
|
if not any(resolved.option_source(field) == "env" for field in secret_fields):
|
|
632
664
|
return # no environment-held secret for this server type; nothing to protect
|
|
633
|
-
if host_field is not None and resolved.option_source(host_field) == "env":
|
|
634
|
-
return # the operator pinned the target host; the contract's host is ignored
|
|
635
665
|
held = [
|
|
636
666
|
env_name(field, Config.model_fields[field]) for field in secret_fields if resolved.option_source(field) == "env"
|
|
637
667
|
]
|
|
668
|
+
if host_field is not None and resolved.option_source(host_field) == "env":
|
|
669
|
+
# Snowflake's host and port override the address the pinned account resolves to.
|
|
670
|
+
redirects = [
|
|
671
|
+
env_name(field, Config.model_fields[field])
|
|
672
|
+
for field in ("snowflake_host", "snowflake_port")
|
|
673
|
+
if server.type == "snowflake" and resolved.option_source(field) == "request"
|
|
674
|
+
]
|
|
675
|
+
if not redirects:
|
|
676
|
+
return # the operator pinned the target host; the contract's host is ignored
|
|
677
|
+
raise HTTPException(
|
|
678
|
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
679
|
+
detail=f"{', '.join(redirects)} cannot be set per request while the API server uses its own Snowflake "
|
|
680
|
+
f"credential ({', '.join(held)}). Remove it, or send all credentials with the request.",
|
|
681
|
+
)
|
|
638
682
|
if host_field is not None:
|
|
639
683
|
env_var = env_name(host_field, Config.model_fields[host_field])
|
|
640
684
|
detail = (
|
|
@@ -35,8 +35,7 @@ custom_quality_checks_option = Annotated[
|
|
|
35
35
|
Optional[Path],
|
|
36
36
|
typer.Option(
|
|
37
37
|
metavar="DIR",
|
|
38
|
-
help="Folder with
|
|
39
|
-
"Defaults to DATACONTRACT_CUSTOM_QUALITY_CHECKS.",
|
|
38
|
+
help="Folder with custom quality checks. Defaults to DATACONTRACT_CUSTOM_QUALITY_CHECKS.",
|
|
40
39
|
),
|
|
41
40
|
]
|
|
42
41
|
|
|
@@ -136,6 +136,8 @@ class Config(BaseSettings):
|
|
|
136
136
|
bigquery_account_info_json_path: str | None = None
|
|
137
137
|
bigquery_billing_project: str | None = None
|
|
138
138
|
bigquery_impersonation_account: str | None = None
|
|
139
|
+
bigquery_location: str | None = None
|
|
140
|
+
bigquery_kms_key_name: str | None = None
|
|
139
141
|
# overrides for the contract's servers block
|
|
140
142
|
bigquery_project: str | None = None
|
|
141
143
|
bigquery_dataset: str | None = None
|
|
@@ -279,8 +281,10 @@ class Config(BaseSettings):
|
|
|
279
281
|
snowflake_username: str | None = None
|
|
280
282
|
snowflake_password: SecretStr | None = None
|
|
281
283
|
snowflake_authenticator: str | None = None
|
|
284
|
+
snowflake_workload_identity_provider: str | None = None
|
|
282
285
|
snowflake_role: str | None = None
|
|
283
286
|
snowflake_token: SecretStr | None = None
|
|
287
|
+
snowflake_token_file_path: str | None = None
|
|
284
288
|
snowflake_passcode: SecretStr | None = None
|
|
285
289
|
snowflake_private_key: SecretStr | None = None
|
|
286
290
|
snowflake_private_key_file: str | None = None
|
|
@@ -582,6 +586,12 @@ class Config(BaseSettings):
|
|
|
582
586
|
def get_bigquery_impersonation_account(self, required: bool = False) -> str | None:
|
|
583
587
|
return self._str_option("bigquery_impersonation_account", required)
|
|
584
588
|
|
|
589
|
+
def get_bigquery_location(self, required: bool = False) -> str | None:
|
|
590
|
+
return self._str_option("bigquery_location", required)
|
|
591
|
+
|
|
592
|
+
def get_bigquery_kms_key_name(self, required: bool = False) -> str | None:
|
|
593
|
+
return self._str_option("bigquery_kms_key_name", required)
|
|
594
|
+
|
|
585
595
|
def get_bigquery_project(self, required: bool = False) -> str | None:
|
|
586
596
|
return self._str_option("bigquery_project", required)
|
|
587
597
|
|
|
@@ -902,12 +912,18 @@ class Config(BaseSettings):
|
|
|
902
912
|
def get_snowflake_authenticator(self, required: bool = False) -> str | None:
|
|
903
913
|
return self._str_option("snowflake_authenticator", required)
|
|
904
914
|
|
|
915
|
+
def get_snowflake_workload_identity_provider(self, required: bool = False) -> str | None:
|
|
916
|
+
return self._str_option("snowflake_workload_identity_provider", required)
|
|
917
|
+
|
|
905
918
|
def get_snowflake_role(self, required: bool = False) -> str | None:
|
|
906
919
|
return self._str_option("snowflake_role", required)
|
|
907
920
|
|
|
908
921
|
def get_snowflake_token(self, required: bool = False) -> str | None:
|
|
909
922
|
return self._str_option("snowflake_token", required)
|
|
910
923
|
|
|
924
|
+
def get_snowflake_token_file_path(self, required: bool = False) -> str | None:
|
|
925
|
+
return self._str_option("snowflake_token_file_path", required)
|
|
926
|
+
|
|
911
927
|
def get_snowflake_passcode(self, required: bool = False) -> str | None:
|
|
912
928
|
return self._str_option("snowflake_passcode", required)
|
|
913
929
|
|
|
@@ -151,6 +151,7 @@ class CheckSpec:
|
|
|
151
151
|
valid_min_items: Optional[int] = None # INVALID_COUNT, array columns
|
|
152
152
|
valid_max_items: Optional[int] = None # INVALID_COUNT, array columns
|
|
153
153
|
valid_unique_items: Optional[bool] = None # INVALID_COUNT, array columns
|
|
154
|
+
value_type: Optional[str] = None # the declared type a value stored untyped is cast to
|
|
154
155
|
|
|
155
156
|
expected_category: Optional[str] = None # FIELD_TYPE: human-readable label (display only)
|
|
156
157
|
expected_type_label: Optional[str] = None # FIELD_TYPE: human-readable expected type
|
|
@@ -166,6 +167,7 @@ class CheckSpec:
|
|
|
166
167
|
query: Optional[str] = None # CUSTOM_SQL (placeholders already substituted)
|
|
167
168
|
dialect: Optional[str] = None # CUSTOM_SQL input SQL dialect
|
|
168
169
|
diagnostics: Optional[dict] = None # CUSTOM_SQL: reported alongside the measured value
|
|
170
|
+
custom_quality_check: Optional[str] = None # CUSTOM_SQL: the check's ID and arguments, as between(min=1, max=100)
|
|
169
171
|
warnings: Optional[List[str]] = None # for the run's log
|
|
170
172
|
|
|
171
173
|
seconds: Optional[int] = None # FRESHNESS / RETENTION threshold in seconds
|
{datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/checks/create_checks.py
RENAMED
|
@@ -28,6 +28,7 @@ from datacontract.engines.checks.check_spec import METADATA_METRICS, CheckSpec,
|
|
|
28
28
|
from datacontract.engines.checks.custom_quality_check import (
|
|
29
29
|
COLUMN_PLACEHOLDERS,
|
|
30
30
|
ENGINE,
|
|
31
|
+
CustomQualityChecks,
|
|
31
32
|
CustomQualityCheckUnusable,
|
|
32
33
|
Instance,
|
|
33
34
|
instantiate,
|
|
@@ -54,6 +55,7 @@ _NESTED_CHECK_SERVER_TYPES = {
|
|
|
54
55
|
"kafka",
|
|
55
56
|
"trino",
|
|
56
57
|
"athena",
|
|
58
|
+
"snowflake",
|
|
57
59
|
} | _FILE_SERVER_TYPES
|
|
58
60
|
|
|
59
61
|
|
|
@@ -212,10 +214,14 @@ def prepare_query(
|
|
|
212
214
|
names |= dict.fromkeys(COLUMN_PLACEHOLDERS, field_name)
|
|
213
215
|
|
|
214
216
|
def identifier(name: str, forced: bool) -> str:
|
|
215
|
-
|
|
217
|
+
parts = [
|
|
216
218
|
exp.to_identifier(part, quoted=forced or not bare.fullmatch(part)).sql(dialect=dialect)
|
|
217
219
|
for part in name.split(".")
|
|
218
|
-
|
|
220
|
+
]
|
|
221
|
+
if dialect == "snowflake" and name == field_name:
|
|
222
|
+
# Snowflake reaches into a column's value with `:`; a dot would name a table's column
|
|
223
|
+
return parts[0] + "".join(f":{part}" for part in parts[1:])
|
|
224
|
+
return ".".join(parts)
|
|
219
225
|
|
|
220
226
|
if instance is not None:
|
|
221
227
|
return instance.render(dialect, names, identifier)
|
|
@@ -234,7 +240,7 @@ def create_checks(
|
|
|
234
240
|
server: Optional[Server],
|
|
235
241
|
schema_name: str = "all",
|
|
236
242
|
variables: Optional[Mapping[str, str]] = None,
|
|
237
|
-
custom_quality_checks: Optional[
|
|
243
|
+
custom_quality_checks: Optional[CustomQualityChecks] = None,
|
|
238
244
|
) -> List[CheckSpec]:
|
|
239
245
|
checks: List[CheckSpec] = []
|
|
240
246
|
if data_contract.schema_ is None:
|
|
@@ -265,7 +271,7 @@ def _to_schema_checks(
|
|
|
265
271
|
schema_object: SchemaObject,
|
|
266
272
|
server: Optional[Server],
|
|
267
273
|
variables: Optional[Mapping[str, str]] = None,
|
|
268
|
-
custom_quality_checks: Optional[
|
|
274
|
+
custom_quality_checks: Optional[CustomQualityChecks] = None,
|
|
269
275
|
) -> List[CheckSpec]:
|
|
270
276
|
checks: List[CheckSpec] = []
|
|
271
277
|
server_type = get_server_type(server) if server is not None else None
|
|
@@ -598,6 +604,7 @@ def _to_schema_checks(
|
|
|
598
604
|
|
|
599
605
|
# A check the server cannot run is reported, not dropped.
|
|
600
606
|
for check in checks[first_check:]:
|
|
607
|
+
check.value_type = prop_type
|
|
601
608
|
if check.preset_result == "warning":
|
|
602
609
|
# An unrunnable rule keeps its own, more specific reason.
|
|
603
610
|
continue
|
|
@@ -831,7 +838,7 @@ def _quality_checks(
|
|
|
831
838
|
quality_list: List[DataQuality],
|
|
832
839
|
server: Optional[Server],
|
|
833
840
|
variables: Optional[Mapping[str, str]] = None,
|
|
834
|
-
custom_quality_checks: Optional[
|
|
841
|
+
custom_quality_checks: Optional[CustomQualityChecks] = None,
|
|
835
842
|
) -> List[CheckSpec]:
|
|
836
843
|
checks: List[CheckSpec] = []
|
|
837
844
|
for count, quality in enumerate(quality_list):
|
|
@@ -933,7 +940,7 @@ def _quality_rule_checks(
|
|
|
933
940
|
count: int,
|
|
934
941
|
server: Optional[Server],
|
|
935
942
|
variables: Optional[Mapping[str, str]] = None,
|
|
936
|
-
custom_quality_checks: Optional[
|
|
943
|
+
custom_quality_checks: Optional[CustomQualityChecks] = None,
|
|
937
944
|
) -> List[CheckSpec]:
|
|
938
945
|
"""The checks of a single ODCS quality rule (``count`` is its index in the list)."""
|
|
939
946
|
if quality.type == "custom" and quality.engine == "soda" and quality.implementation:
|
|
@@ -1000,6 +1007,7 @@ def _quality_rule_checks(
|
|
|
1000
1007
|
severity=rule.severity,
|
|
1001
1008
|
dimension=rule.dimension,
|
|
1002
1009
|
diagnostics=instance.diagnostics,
|
|
1010
|
+
custom_quality_check=instance.call,
|
|
1003
1011
|
warnings=instance.warnings,
|
|
1004
1012
|
)
|
|
1005
1013
|
]
|
|
@@ -8,7 +8,9 @@ import re
|
|
|
8
8
|
from dataclasses import dataclass, field
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from typing import Any, Callable, Mapping, Optional
|
|
11
|
+
from urllib.parse import quote, urlparse
|
|
11
12
|
|
|
13
|
+
import requests
|
|
12
14
|
import sqlglot
|
|
13
15
|
import yaml
|
|
14
16
|
from open_data_contract_standard.model import DataQuality
|
|
@@ -17,9 +19,11 @@ from sqlglot import exp
|
|
|
17
19
|
from sqlglot.dialects.dialect import Dialect
|
|
18
20
|
from sqlglot.tokens import TokenType
|
|
19
21
|
|
|
22
|
+
from datacontract.config import Config
|
|
20
23
|
from datacontract.config.variables import _ENUM_VALUES
|
|
21
24
|
from datacontract.engines.checks.sql_guard import _DIALECT_BY_SERVER_TYPE
|
|
22
|
-
from datacontract.
|
|
25
|
+
from datacontract.integration.entropy_data import _get_api_key_or_none, _get_host
|
|
26
|
+
from datacontract.lint.resolve import _api_key_for, _SafeLoaderNoTimestamp
|
|
23
27
|
|
|
24
28
|
ENGINE = "datacontract-cli"
|
|
25
29
|
|
|
@@ -91,41 +95,104 @@ class CustomQualityCheck:
|
|
|
91
95
|
warnings: list[str] = field(default_factory=list)
|
|
92
96
|
|
|
93
97
|
|
|
94
|
-
|
|
95
|
-
folder
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
f"The custom quality check '{check_id}' is defined twice: {', '.join(path.name for path in paths)}."
|
|
98
|
+
class CustomQualityChecks:
|
|
99
|
+
"""Where a test run finds its custom quality checks: the folder first, then Entropy Data."""
|
|
100
|
+
|
|
101
|
+
def __init__(self, config: Config, untrusted_contract: bool = False):
|
|
102
|
+
self.directory = config.get_custom_quality_checks()
|
|
103
|
+
# An untrusted caller may set the host per request (API header); checks still come from the platform
|
|
104
|
+
# as the environment sees it, or the request would choose where the server sends its GET.
|
|
105
|
+
self.host = _get_host(None if untrusted_contract else config).rstrip("/")
|
|
106
|
+
self.api_key = _api_key_for(self.host, config, untrusted_contract)
|
|
107
|
+
# A key the request brought for its own host is not sent to the environment's host.
|
|
108
|
+
self.ignored_request_host = (
|
|
109
|
+
_get_host(config)
|
|
110
|
+
if untrusted_contract and self.api_key is None and _get_api_key_or_none(config) is not None
|
|
111
|
+
else None
|
|
109
112
|
)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
113
|
+
# lowercased ID -> the check's YAML, or why Entropy Data could not provide it
|
|
114
|
+
self._fetched: dict[str, str | CustomQualityCheckError] = {}
|
|
115
|
+
|
|
116
|
+
def load(self, check_id: str) -> CustomQualityCheck:
|
|
117
|
+
"""The check with ``check_id``, regardless of case."""
|
|
118
|
+
if self.directory is not None:
|
|
119
|
+
folder = Path(self.directory)
|
|
120
|
+
if not folder.is_dir():
|
|
121
|
+
problem = "is a file, not a folder" if folder.exists() else "does not exist"
|
|
122
|
+
raise CustomQualityCheckError(f"The custom quality checks folder {self.directory} {problem}.")
|
|
123
|
+
# The ID comes from the contract, so it is looked up among the files, never joined into a path.
|
|
124
|
+
files: dict[str, list[Path]] = {}
|
|
125
|
+
for path in sorted(folder.iterdir()):
|
|
126
|
+
if path.is_file() and path.suffix in (".yaml", ".yml"):
|
|
127
|
+
files.setdefault(path.stem.lower(), []).append(path)
|
|
128
|
+
paths = files.get(check_id.lower(), [])
|
|
129
|
+
if len(paths) > 1:
|
|
130
|
+
raise CustomQualityCheckError(
|
|
131
|
+
f"The custom quality check '{check_id}' is defined twice: {', '.join(path.name for path in paths)}."
|
|
132
|
+
)
|
|
133
|
+
if paths:
|
|
134
|
+
return _parse(check_id, paths[0].read_text(encoding="utf-8"))
|
|
135
|
+
if self.api_key is None:
|
|
136
|
+
ids = sorted(path.stem for group in files.values() for path in group)
|
|
137
|
+
if len(ids) <= 7:
|
|
138
|
+
hint = f" Available: {', '.join(ids) or 'none'}."
|
|
139
|
+
else:
|
|
140
|
+
similar = difflib.get_close_matches(check_id, ids, n=3)
|
|
141
|
+
hint = f" Did you mean {', '.join(similar)}?" if similar else ""
|
|
142
|
+
raise CustomQualityCheckError(f"There is no custom quality check with the ID '{check_id}'.{hint}")
|
|
143
|
+
|
|
144
|
+
key = check_id.lower()
|
|
145
|
+
if key not in self._fetched:
|
|
146
|
+
reason = None
|
|
147
|
+
try:
|
|
148
|
+
# Not following redirects keeps the API key from travelling to another host.
|
|
149
|
+
response = requests.get(
|
|
150
|
+
f"{self.host}/api/custom-quality-checks/{quote(check_id, safe='')}/check.yaml",
|
|
151
|
+
headers={"x-api-key": self.api_key, "Accept": "application/yaml"},
|
|
152
|
+
timeout=10,
|
|
153
|
+
allow_redirects=False,
|
|
154
|
+
)
|
|
155
|
+
except requests.RequestException as e:
|
|
156
|
+
reason = str(e)
|
|
157
|
+
else:
|
|
158
|
+
if response.status_code == 200:
|
|
159
|
+
self._fetched[key] = response.content.decode("utf-8")
|
|
160
|
+
elif response.status_code == 404:
|
|
161
|
+
self._fetched[key] = CustomQualityCheckError(
|
|
162
|
+
f"Neither '{self.directory}' nor Entropy Data has the custom quality check '{check_id}'."
|
|
163
|
+
if self.directory is not None
|
|
164
|
+
else f"Entropy Data has no custom quality check '{check_id}'."
|
|
165
|
+
)
|
|
166
|
+
elif response.is_redirect:
|
|
167
|
+
reason = (
|
|
168
|
+
f"HTTP {response.status_code} redirect to '{response.headers.get('Location')}' is not followed"
|
|
169
|
+
)
|
|
170
|
+
elif response.status_code in (401, 403):
|
|
171
|
+
reason = f"the API key was rejected (HTTP {response.status_code})"
|
|
172
|
+
else:
|
|
173
|
+
reason = f"HTTP {response.status_code} {response.reason}"
|
|
174
|
+
if reason is not None:
|
|
175
|
+
source = urlparse(self.host).netloc or self.host
|
|
176
|
+
self._fetched[key] = CustomQualityCheckError(
|
|
177
|
+
f"The custom quality check '{check_id}' is not in '{self.directory}' and could not be loaded "
|
|
178
|
+
f"from {source}: {reason}."
|
|
179
|
+
if self.directory is not None
|
|
180
|
+
else f"Could not load the custom quality check '{check_id}' from {source}: {reason}."
|
|
181
|
+
)
|
|
182
|
+
fetched = self._fetched[key]
|
|
183
|
+
if isinstance(fetched, CustomQualityCheckError):
|
|
184
|
+
raise fetched
|
|
185
|
+
return _parse(check_id, fetched)
|
|
123
186
|
|
|
124
187
|
|
|
125
|
-
def _parse(check_id: str,
|
|
188
|
+
def _parse(check_id: str, text: str) -> CustomQualityCheck:
|
|
126
189
|
def invalid(detail: str) -> CustomQualityCheckError:
|
|
127
190
|
return CustomQualityCheckError(f"The custom quality check '{check_id}' is invalid: {detail}")
|
|
128
191
|
|
|
192
|
+
try:
|
|
193
|
+
document = yaml.load(text, Loader=_SafeLoaderNoTimestamp)
|
|
194
|
+
except yaml.YAMLError as e:
|
|
195
|
+
raise CustomQualityCheckError(f"The custom quality check '{check_id}' is not valid YAML: {e}")
|
|
129
196
|
if not isinstance(document, dict):
|
|
130
197
|
raise invalid("it must be a YAML mapping.")
|
|
131
198
|
# Unknown keys only warn, so a file written for a newer version still runs.
|
|
@@ -232,6 +299,8 @@ class Instance:
|
|
|
232
299
|
# The rule with the check's defaults filled in: expected result and dimension.
|
|
233
300
|
quality: DataQuality
|
|
234
301
|
name: str
|
|
302
|
+
# The check's ID with the rule's arguments, as between(min=1, max=100)
|
|
303
|
+
call: str
|
|
235
304
|
diagnostics: dict
|
|
236
305
|
warnings: list[str]
|
|
237
306
|
|
|
@@ -365,7 +434,7 @@ def instantiate(
|
|
|
365
434
|
model: str,
|
|
366
435
|
field_name: Optional[str],
|
|
367
436
|
dialect: Optional[str],
|
|
368
|
-
|
|
437
|
+
custom_quality_checks: Optional[CustomQualityChecks],
|
|
369
438
|
) -> Instance:
|
|
370
439
|
"""The rule's custom quality check for a server whose rules are written in ``dialect``."""
|
|
371
440
|
implementation = quality.implementation if isinstance(quality.implementation, dict) else {}
|
|
@@ -386,6 +455,8 @@ def instantiate(
|
|
|
386
455
|
f"The custom quality check ID '{check_id}' must not contain spaces, slashes, "
|
|
387
456
|
"or any of the characters \\ % ; # ?"
|
|
388
457
|
)
|
|
458
|
+
if check_id in (".", ".."):
|
|
459
|
+
raise unusable(f"'{check_id}' is no valid custom quality check ID.")
|
|
389
460
|
location = model if field_name is None else f"{model}.{field_name}"
|
|
390
461
|
rule_warnings += [
|
|
391
462
|
f"Custom quality check '{check_id}' on {location}: Unknown key 'implementation.{key}' is ignored."
|
|
@@ -414,13 +485,22 @@ def instantiate(
|
|
|
414
485
|
or f"{check_id}({', '.join(f'{argument}={value_text(value)}' for argument, value in given.items())})"
|
|
415
486
|
)
|
|
416
487
|
|
|
417
|
-
if
|
|
488
|
+
if custom_quality_checks is None or (
|
|
489
|
+
custom_quality_checks.directory is None and custom_quality_checks.api_key is None
|
|
490
|
+
):
|
|
491
|
+
if custom_quality_checks is not None and custom_quality_checks.ignored_request_host is not None:
|
|
492
|
+
raise unusable(
|
|
493
|
+
f"The rule uses the custom quality check '{check_id}', but the request's API key belongs to its "
|
|
494
|
+
f"entropy-data-host '{custom_quality_checks.ignored_request_host}', so set ENTROPY_DATA_HOST to that "
|
|
495
|
+
"host on the server running the API."
|
|
496
|
+
)
|
|
418
497
|
raise unusable(
|
|
419
|
-
f"The rule uses the custom quality check '{check_id}', but
|
|
420
|
-
f"configured. Pass --custom-quality-checks or set
|
|
498
|
+
f"The rule uses the custom quality check '{check_id}', but neither a custom quality checks folder nor "
|
|
499
|
+
f"an Entropy Data API key is configured. Pass --custom-quality-checks, or set "
|
|
500
|
+
f"DATACONTRACT_CUSTOM_QUALITY_CHECKS or ENTROPY_DATA_API_KEY.",
|
|
421
501
|
)
|
|
422
502
|
try:
|
|
423
|
-
check = load(
|
|
503
|
+
check = custom_quality_checks.load(check_id)
|
|
424
504
|
except CustomQualityCheckError as e:
|
|
425
505
|
raise unusable(str(e))
|
|
426
506
|
|
|
@@ -453,6 +533,7 @@ def instantiate(
|
|
|
453
533
|
for item in (value if isinstance(value, list) else [value])
|
|
454
534
|
]
|
|
455
535
|
arguments[argument] = (False, numbers if isinstance(value, list) else numbers[0])
|
|
536
|
+
call = f"{check.id}({', '.join(f'{name}={value_text(value)}' for name, (_, value) in arguments.items())})"
|
|
456
537
|
if quality.description:
|
|
457
538
|
name = quality.description
|
|
458
539
|
elif check.description:
|
|
@@ -472,7 +553,7 @@ def instantiate(
|
|
|
472
553
|
|
|
473
554
|
name = reference_pattern(TABLE_PLACEHOLDERS + COLUMN_PLACEHOLDERS).sub(fill, check.description)
|
|
474
555
|
else:
|
|
475
|
-
name =
|
|
556
|
+
name = call
|
|
476
557
|
|
|
477
558
|
for argument, (identifier, value) in arguments.items():
|
|
478
559
|
if identifier and (not isinstance(value, str) or not value):
|
|
@@ -534,6 +615,7 @@ def instantiate(
|
|
|
534
615
|
arguments=arguments,
|
|
535
616
|
quality=quality.model_copy(update=updates),
|
|
536
617
|
name=name,
|
|
618
|
+
call=call,
|
|
537
619
|
diagnostics={
|
|
538
620
|
"custom_quality_check": check.id,
|
|
539
621
|
"query": query_dialect,
|
{datacontract_cli-1.2.8 → datacontract_cli-1.2.9}/datacontract/engines/data_contract_test.py
RENAMED
|
@@ -12,6 +12,7 @@ from open_data_contract_standard.model import OpenDataContractStandard, Server
|
|
|
12
12
|
from datacontract.config import Config
|
|
13
13
|
from datacontract.engines.checks.check_spec import MetricType
|
|
14
14
|
from datacontract.engines.checks.create_checks import create_checks, to_schema_name
|
|
15
|
+
from datacontract.engines.checks.custom_quality_check import CustomQualityChecks
|
|
15
16
|
from datacontract.engines.checks.dimensions import default_dimension
|
|
16
17
|
|
|
17
18
|
if typing.TYPE_CHECKING:
|
|
@@ -123,6 +124,7 @@ def execute_data_contract_test(
|
|
|
123
124
|
)
|
|
124
125
|
|
|
125
126
|
model_filters = resolve_row_filters(data_contract, server, run, filter, filters, schema_name)
|
|
127
|
+
custom_quality_checks = CustomQualityChecks(config, untrusted_contract)
|
|
126
128
|
|
|
127
129
|
if server.type == "hana":
|
|
128
130
|
from datacontract.engines.hana.check_hana_execute import check_hana_execute
|
|
@@ -140,6 +142,7 @@ def execute_data_contract_test(
|
|
|
140
142
|
quality_ids=quality_ids,
|
|
141
143
|
tags=tags,
|
|
142
144
|
config=config,
|
|
145
|
+
custom_quality_checks=custom_quality_checks,
|
|
143
146
|
)
|
|
144
147
|
return
|
|
145
148
|
|
|
@@ -148,7 +151,7 @@ def execute_data_contract_test(
|
|
|
148
151
|
server,
|
|
149
152
|
schema_name=schema_name,
|
|
150
153
|
variables=variables,
|
|
151
|
-
custom_quality_checks=
|
|
154
|
+
custom_quality_checks=custom_quality_checks,
|
|
152
155
|
)
|
|
153
156
|
if check_categories is not None:
|
|
154
157
|
specs = [s for s in specs if s.category in check_categories]
|