datacontract-cli 1.2.1__tar.gz → 1.2.2__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.1/datacontract_cli.egg-info → datacontract_cli-1.2.2}/PKG-INFO +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/api.py +12 -2
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/command_api.py +25 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/command_import.py +49 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/command_init.py +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/config/settings.py +1 -1
- datacontract_cli-1.2.2/datacontract/config/variables.py +256 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/data_contract.py +53 -0
- datacontract_cli-1.2.2/datacontract/editor_assets/VERSION +1 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/editor_assets/datacontract-editor.css +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/editor_assets/datacontract-editor.es.js +27133 -25620
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/editor_assets/index.html +9 -5
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/checks/check_spec.py +2 -2
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/checks/create_checks.py +164 -92
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/checks/sql_guard.py +42 -17
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/data_contract_test.py +29 -15
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/hana/hana_quality_check.py +47 -12
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/ibis/connections/connect.py +17 -7
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/ibis/ibis_check_execute.py +6 -2
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/sodacl_exporter.py +24 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/sql_type_converter.py +6 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/avro_importer.py +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/bigquery_importer.py +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/dbml_importer.py +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/dcs_importer.py +3 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/iceberg_importer.py +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/importer.py +1 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/importer_factory.py +5 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/jsonschema_importer.py +61 -14
- datacontract_cli-1.2.2/datacontract/imports/odata_importer.py +506 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/protobuf_importer.py +2 -2
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/spark_importer.py +43 -38
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/sql_importer.py +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/unity_importer.py +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/init/init_template.py +2 -2
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/lint/files.py +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/lint/resolve.py +48 -28
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/lint/schema.py +2 -2
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/model/run.py +2 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/model/vector_type.py +5 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/output/test_results_writer.py +24 -20
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2/datacontract_cli.egg-info}/PKG-INFO +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract_cli.egg-info/SOURCES.txt +9 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/pyproject.toml +1 -1
- datacontract_cli-1.2.2/tests/test_command_api_options.py +49 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_config.py +1 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_create_checks_nested.py +20 -3
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_create_checks_physical_type.py +1 -1
- datacontract_cli-1.2.2/tests/test_create_checks_sql_placeholders.py +160 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_sodacl.py +37 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_hana_quality_check.py +47 -5
- datacontract_cli-1.2.2/tests/test_import_jsonschema.py +151 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_object_storage.py +1 -1
- datacontract_cli-1.2.2/tests/test_import_odata.py +873 -0
- datacontract_cli-1.2.2/tests/test_lint_files.py +10 -0
- datacontract_cli-1.2.2/tests/test_lint_schema.py +20 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_quality_sql_dialects.py +3 -3
- datacontract_cli-1.2.2/tests/test_quality_unrunnable_rules.py +240 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_checks_filter.py +2 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_dry_run.py +31 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_iceberg_rest.py +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_kafka.py +17 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_metadata_only.py +11 -11
- datacontract_cli-1.2.2/tests/test_test_nested_checks.py +154 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_parquet.py +22 -32
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_quality_percent_severity.py +12 -8
- datacontract_cli-1.2.2/tests/test_test_results_writer.py +44 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_s3_csv.py +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_s3_delta.py +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_s3_json.py +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_s3_json_complex.py +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_s3_json_multiple_models.py +1 -1
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_schema_evolution.py +6 -4
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_variables.py +60 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_untrusted_contract.py +64 -12
- datacontract_cli-1.2.2/tests/test_untrusted_contract_variables.py +209 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_vector_type.py +3 -0
- datacontract_cli-1.2.1/datacontract/config/variables.py +0 -133
- datacontract_cli-1.2.1/datacontract/editor_assets/VERSION +0 -1
- datacontract_cli-1.2.1/tests/test_create_checks_sql_placeholders.py +0 -44
- datacontract_cli-1.2.1/tests/test_import_jsonschema.py +0 -78
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/LICENSE +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/MANIFEST.in +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/README.md +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/__init__.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/breaking/__init__.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/breaking/detector.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/breaking/rules.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/catalog/catalog.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/changelog/__init__.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/changelog/changelog.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/changelog/normalize.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/cli.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/command_breaking.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/command_catalog.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/command_changelog.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/command_ci.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/command_dbt.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/command_edit.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/command_export.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/command_lint.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/command_publish.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/command_test.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/config/__init__.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/config/cli_context.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/editor_assets/assets/editor.worker-4.7.0.js +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/editor_assets/assets/yaml.worker-4.7.0.js +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/editor_assets/logo_fuchsia_v2.svg +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/editor_assets/monaco-4.7.0.js +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/editor_assets/odcs.svg +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/editor_assets/robots.txt +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/editor_assets/rolldown-runtime-DY7j01NX.js +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/editor_assets/sitemap.xml +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/__init__.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/checks/__init__.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/checks/check_filter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/checks/dimensions.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/checks/physical_type_match.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/checks/severity.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/checks/type_normalize.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/datacontract/check_azure_blob_file.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/datacontract/check_that_datacontract_contains_valid_servers_configuration.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/datacontract/check_that_datacontract_file_exists.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/fastjsonschema/check_jsonschema.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/fastjsonschema/s3/s3_read_files.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/hana/__init__.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/hana/check_hana_execute.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/hana/hana_check_selection.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/hana/hana_connection.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/hana/hana_schema_check.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/hana/hana_type_mapping.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/ibis/__init__.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/ibis/connections/__init__.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/ibis/connections/aws_credentials.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/ibis/connections/databricks_patch.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/ibis/connections/duckdb_connection.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/ibis/connections/exasol_patch.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/ibis/connections/iceberg.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/ibis/connections/kafka.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/ibis/connections/oracle_patch.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/ibis/connections/redshift_credentials.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/ibis/connections/redshift_patch.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/ibis/dtype_category.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/ibis/native_type.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/engines/ibis/snowflake_structured_types.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/__init__.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/avro_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/avro_idl_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/bigquery_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/custom_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/data_caterer_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/dbml_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/dbt_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/dcs_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/dqx_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/duckdb_type_converter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/excel_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/exporter_factory.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/go_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/great_expectations_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/html_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/iceberg_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/jsonschema_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/markdown_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/mermaid_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/odcs_v3_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/pandas_type_converter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/protobuf_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/pydantic_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/rdf_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/sodacl_check_builder.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/spark_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/sql_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/export/sqlalchemy_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/athena_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/csv_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/dbt_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/excel_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/glue_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/json_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/mysql_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/object_storage_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/odcs_helper.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/odcs_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/oracle_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/parquet_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/postgres_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/powerbi_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/pydantic_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/redshift_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/snowflake_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/spark_type_json.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/sqlserver_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/imports/trino_importer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/integration/dbt_sync.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/integration/dbt_test_mapping.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/integration/entropy_data.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/lint/constraints.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/lint/resources.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/lint/s3.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/lint/urls.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/model/breaking.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/model/changelog.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/model/enum_values.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/model/exceptions.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/model/map_type.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/model/natural_keys.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/model/odcs.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/model/server.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/model/workbook.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/output/__init__.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/output/ci_output.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/output/json_test_results.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/output/junit_test_results.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/output/output_format.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/output/text_breaking_results.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/output/text_changelog_results.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/py.typed +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/schemas/datacontract-1.1.0.init.yaml +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/schemas/datacontract-1.1.0.schema.json +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/schemas/datacontract-1.2.0.init.yaml +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/schemas/datacontract-1.2.0.schema.json +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/schemas/datacontract-1.2.1.init.yaml +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/schemas/datacontract-1.2.1.schema.json +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/schemas/odcs-3.0.2.schema.json +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/schemas/odcs-3.1.0.schema.json +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/schemas/odcs-3.2.0.init.yaml +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/schemas/odcs-3.2.0.schema.json +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/datacontract.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/datacontract_odcs.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/excel/odcs-template-v3.0.xlsx +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/excel/odcs-template-v3.1.xlsx +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/excel/odcs-template-v3.2.xlsx +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/index.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/macros/authoritative_definitions.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/macros/context.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/macros/schema.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/macros/server.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/macros/support.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials/datacontract_information.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials/datacontract_servicelevels.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials/datacontract_terms.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials/definition.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials/example.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials/model_field.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials/quality.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials/server.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials_odcs/context.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials_odcs/custom_properties.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials_odcs/diagram.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials_odcs/dialog_yaml.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials_odcs/footer.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials_odcs/fundamentals.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials_odcs/header.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials_odcs/pricing.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials_odcs/roles.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials_odcs/schema.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials_odcs/servers.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials_odcs/sla.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials_odcs/support.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials_odcs/team.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials_odcs/term_of_use.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/partials_odcs/title.html +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract/templates/style/output.css +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract_cli.egg-info/dependency_links.txt +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract_cli.egg-info/entry_points.txt +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract_cli.egg-info/requires.txt +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/datacontract_cli.egg-info/top_level.txt +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/setup.cfg +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_api.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_api_openapi.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_breaking.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_breaking_odcs_3_2.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_catalog.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_changelog.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_changelog_engine.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_changelog_normalize.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_changelog_output_text.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_check_quality_provenance.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_ci_output.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_cli.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_command_api.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_config_file.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_config_require.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_config_threading.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_connect_athena.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_connect_bigquery.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_connect_databricks.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_connect_exasol.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_connect_impala.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_connect_mysql.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_connect_oracle.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_connect_postgres.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_connect_redshift.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_connect_s3_credentials.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_connect_snowflake.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_connect_sqlserver.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_connect_trino.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_create_checks_array_items.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_create_checks_nested_type.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_create_checks_primary_key.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_create_checks_retention_period.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_create_checks_servicelevel.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_data_contract.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_data_contract_checks.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_data_contract_specification.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_dbt_sync.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_dbt_test_mapping.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_description_linter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_docs_commands.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_docs_config_options.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_docs_ordering.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_download_datacontract_file.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_dtype_category.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_duckdb_json.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_edit.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_enum_values.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_avro.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_avro_idl.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_bigquery.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_complex_data_contract.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_custom.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_custom_exporter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_custom_model.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_data_caterer.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_dbml.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_dbt_models.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_dbt_sources.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_dbt_staging_sql.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_dcs.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_dqx.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_excel.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_go.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_great_expectations.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_html.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_iceberg.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_jsonschema.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_large_contract.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_markdown.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_mermaid.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_odcs_v3.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_protobuf.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_pydantic.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_rdf.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_spark.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_sql.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_sql_clickhouse.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_sql_query.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_export_sqlalchemy.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_hana_connection.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_hana_schema_check.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_hana_type_mapping.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_ibis_check_execute.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_ibis_mssql_pattern.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_ibis_mssql_schema.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_ibis_oracle_schema.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_athena.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_avro.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_bigquery.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_csv.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_dbml.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_dbt.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_excel.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_glue.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_iceberg.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_json.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_mysql.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_odcs_v3.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_oracle.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_parquet.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_postgres.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_powerbi.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_protobuf.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_pydantic_model.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_redshift.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_snowflake.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_spark_type_json.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_sql_oracle.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_sql_postgres.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_sql_snowflake.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_sql_sqlserver.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_sqlserver.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_trino.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_unity_file.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_import_unmapped_types.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_install_extras.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_integration_entropydata.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_lint.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_lint_odcs_3_2_constraints.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_lint_urls.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_map_type.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_metadata_fields.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_native_type_schema_scope.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_nested_path_checks.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_oracle_patch.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_physical_type_match.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_publish_test_results.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_publish_test_results_payload.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_redshift_credentials.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_redshift_patch.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_resolve.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_resolve_definitions.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_resolve_local_definitions.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_roundtrip_jsonschema.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_roundtrip_pydantic_model.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_row_filter_duplicate_count.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_run_check_deprecated_fields.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_run_check_engine.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_server_overrides.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_server_type_mssql.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_server_type_synonyms.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_servers_3_2_0.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_snowflake_structured_types.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_sql_type_converter_physicaltype.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_api.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_array_items.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_athena_iceberg.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_azure_blob_file.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_azure_remote.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_bigquery.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_databricks.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_delta.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_diagnostics.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_dimension_filter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_duckdb.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_failed_samples.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_gcs_csv_remote.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_gcs_json_remote.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_hana.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_iceberg.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_iceberg_s3tables.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_kafka_decoding.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_kafka_remote.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_local_json.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_local_json_nd.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_mysql.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_oracle.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_oracle_xe.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_output_json.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_output_junit.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_postgres.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_quality.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_quality_id_and_tag_filter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_quality_local_fallback.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_redshift.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_row_filter.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_s3_json_remote.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_server_not_found.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_snowflake.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_soda_connection_error.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_sqlserver.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_test_trino.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_type_normalize.py +0 -0
- {datacontract_cli-1.2.1 → datacontract_cli-1.2.2}/tests/test_variables.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.2
|
|
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
|
|
@@ -12,6 +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, resolve_variables
|
|
15
16
|
from datacontract.data_contract import DataContract, ExportFormat
|
|
16
17
|
from datacontract.model.breaking import BreakingChangeEntry
|
|
17
18
|
from datacontract.model.changelog import ChangelogEntry
|
|
@@ -527,7 +528,10 @@ def _selected_server(body: str, server_name: str | None, config):
|
|
|
527
528
|
|
|
528
529
|
try:
|
|
529
530
|
data_contract = resolve.resolve_data_contract(data_contract_str=body, config=config)
|
|
530
|
-
|
|
531
|
+
server = get_server(data_contract, server_name)
|
|
532
|
+
# Guard the server type the test will run against, not a ${VAR:-local} reference to it.
|
|
533
|
+
variables = allowed_environment(_allowed_contract_variables())
|
|
534
|
+
return server and server.model_copy(update={"type": resolve_variables(server.type, variables=variables)})
|
|
531
535
|
except Exception:
|
|
532
536
|
return None
|
|
533
537
|
|
|
@@ -540,6 +544,11 @@ ALLOW_LOCAL_FILES_ENV = "DATACONTRACT_CLI_API_ALLOW_LOCAL_FILES"
|
|
|
540
544
|
_TRUTHY = frozenset({"1", "true", "yes", "on"})
|
|
541
545
|
|
|
542
546
|
|
|
547
|
+
def _allowed_contract_variables() -> list[str]:
|
|
548
|
+
"""The variable-name globs a posted contract may resolve; none by default."""
|
|
549
|
+
return (os.getenv(CONTRACT_VARIABLES_ENV) or "").split(",")
|
|
550
|
+
|
|
551
|
+
|
|
543
552
|
def _local_files_allowed() -> bool:
|
|
544
553
|
"""Whether the operator opted in to serving data from the server's own disk.
|
|
545
554
|
|
|
@@ -569,7 +578,7 @@ def _reject_local_server_type(body: str, server_name: str | None, config) -> Non
|
|
|
569
578
|
detail=(
|
|
570
579
|
f"Server type '{server.type}' reads from the file system of the server running this API, "
|
|
571
580
|
f"so it is refused. Use a server type that names a data source, such as s3, gcs, azure, "
|
|
572
|
-
f"postgres, or snowflake — or
|
|
581
|
+
f"postgres, or snowflake — or start the API server with --allow-local-files if this deployment serves "
|
|
573
582
|
f"its own files on purpose."
|
|
574
583
|
),
|
|
575
584
|
)
|
|
@@ -814,6 +823,7 @@ async def test(
|
|
|
814
823
|
filter=filter,
|
|
815
824
|
filters=parsed_filters,
|
|
816
825
|
untrusted_contract=untrusted_contract,
|
|
826
|
+
allowed_variables=_allowed_contract_variables(),
|
|
817
827
|
).test()
|
|
818
828
|
|
|
819
829
|
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
1
3
|
import typer
|
|
2
4
|
from typing_extensions import Annotated
|
|
3
5
|
|
|
4
6
|
from datacontract.cli import app, debug_option, enable_debug_logging
|
|
7
|
+
from datacontract.config.variables import CONTRACT_VARIABLES_ENV
|
|
5
8
|
|
|
6
9
|
|
|
7
10
|
def _get_uvicorn_arguments(port: int, host: str, reload: bool, context: typer.Context) -> dict:
|
|
@@ -40,6 +43,20 @@ def api(
|
|
|
40
43
|
help="Watch the source files and restart the server on changes. For development only; off by default.",
|
|
41
44
|
),
|
|
42
45
|
] = False,
|
|
46
|
+
contract_variables: Annotated[
|
|
47
|
+
str | None,
|
|
48
|
+
typer.Option(
|
|
49
|
+
help="Environment variables a posted data contract may read through ${VAR} references, "
|
|
50
|
+
"as comma-separated fnmatch globs (e.g. 'TABLE_*,CUTOFF_DATE', or '*' to allow all). Empty by default.",
|
|
51
|
+
),
|
|
52
|
+
] = None,
|
|
53
|
+
allow_local_files: Annotated[
|
|
54
|
+
bool | None,
|
|
55
|
+
typer.Option(
|
|
56
|
+
"--allow-local-files/--no-allow-local-files",
|
|
57
|
+
help="Let a posted data contract read the server's own disk through servers[].type: local. Off by default.",
|
|
58
|
+
),
|
|
59
|
+
] = None,
|
|
43
60
|
debug: debug_option = None,
|
|
44
61
|
):
|
|
45
62
|
"""
|
|
@@ -60,6 +77,14 @@ def api(
|
|
|
60
77
|
"""
|
|
61
78
|
enable_debug_logging(debug)
|
|
62
79
|
|
|
80
|
+
# Passed through the environment, which survives uvicorn's --reload respawn.
|
|
81
|
+
from datacontract.api import ALLOW_LOCAL_FILES_ENV
|
|
82
|
+
|
|
83
|
+
if contract_variables is not None:
|
|
84
|
+
os.environ[CONTRACT_VARIABLES_ENV] = contract_variables
|
|
85
|
+
if allow_local_files is not None:
|
|
86
|
+
os.environ[ALLOW_LOCAL_FILES_ENV] = "true" if allow_local_files else "false"
|
|
87
|
+
|
|
63
88
|
import uvicorn
|
|
64
89
|
from uvicorn.config import LOGGING_CONFIG
|
|
65
90
|
|
|
@@ -909,3 +909,52 @@ def import_athena(
|
|
|
909
909
|
id=id,
|
|
910
910
|
)
|
|
911
911
|
_write_result(result, output)
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
@import_app.command(
|
|
915
|
+
name="odata",
|
|
916
|
+
epilog="Example: datacontract import odata --service-root-url https://example.com/odata/ --entity-set Products --output datacontract.yaml",
|
|
917
|
+
)
|
|
918
|
+
def import_odata(
|
|
919
|
+
service_root_url: Annotated[str, typer.Option(help="HTTP(S) root URL of the OData service.")],
|
|
920
|
+
service_document_file: Annotated[
|
|
921
|
+
Optional[Path],
|
|
922
|
+
typer.Option(help="Local JSON service document. Ignored when --entity-set is supplied."),
|
|
923
|
+
] = None,
|
|
924
|
+
entity_set: Annotated[
|
|
925
|
+
Optional[List[str]],
|
|
926
|
+
typer.Option(
|
|
927
|
+
help="EntitySet to import (repeat for multiple sets). If omitted, will import all sets from the service document."
|
|
928
|
+
),
|
|
929
|
+
] = None,
|
|
930
|
+
metadata_url: Annotated[
|
|
931
|
+
Optional[str],
|
|
932
|
+
typer.Option(
|
|
933
|
+
help="CSDL XML or JSON URL. Defaults to SERVICE_ROOT_URL/$metadata.",
|
|
934
|
+
),
|
|
935
|
+
] = None,
|
|
936
|
+
metadata_file: Annotated[
|
|
937
|
+
Optional[Path],
|
|
938
|
+
typer.Option(
|
|
939
|
+
help="Path to a local OData CSDL XML or JSON file. Use either --metadata-file or --metadata-url.",
|
|
940
|
+
),
|
|
941
|
+
] = None,
|
|
942
|
+
output: output_option = None,
|
|
943
|
+
owner: owner_option = None,
|
|
944
|
+
id: id_option = None,
|
|
945
|
+
debug: debug_option = None,
|
|
946
|
+
):
|
|
947
|
+
"""Import a data contract from OData 4.x CSDL XML or JSON metadata, using a URL or local file."""
|
|
948
|
+
enable_debug_logging(debug)
|
|
949
|
+
result = DataContract.import_from_source(
|
|
950
|
+
config=cli_config(),
|
|
951
|
+
format="odata",
|
|
952
|
+
source=service_root_url,
|
|
953
|
+
odata_service_document_file=service_document_file,
|
|
954
|
+
odata_entity_set=entity_set,
|
|
955
|
+
odata_metadata_url=metadata_url,
|
|
956
|
+
odata_metadata_file=metadata_file,
|
|
957
|
+
owner=owner,
|
|
958
|
+
id=id,
|
|
959
|
+
)
|
|
960
|
+
_write_result(result, output)
|
|
@@ -28,6 +28,6 @@ def init(
|
|
|
28
28
|
console.print("File already exists, use --overwrite to overwrite")
|
|
29
29
|
raise typer.Exit(code=1)
|
|
30
30
|
template_str = get_init_template(template)
|
|
31
|
-
with open(location, "w") as f:
|
|
31
|
+
with open(location, "w", encoding="utf-8") as f:
|
|
32
32
|
f.write(template_str)
|
|
33
33
|
console.print("📄 data contract written to " + location)
|
|
@@ -356,7 +356,7 @@ class Config(BaseSettings):
|
|
|
356
356
|
|
|
357
357
|
path = Path(path)
|
|
358
358
|
try:
|
|
359
|
-
data = yaml.safe_load(path.read_text())
|
|
359
|
+
data = yaml.safe_load(path.read_text(encoding="utf-8"))
|
|
360
360
|
except yaml.YAMLError as e:
|
|
361
361
|
raise ValueError(f"Config file {path} is not valid YAML: {e}")
|
|
362
362
|
if data is None:
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""Variable references in string values, per ODCS v3.2.0 (RFC 0050).
|
|
2
|
+
|
|
3
|
+
Any string value in a data contract may contain ``${VAR_NAME}`` or, with an
|
|
4
|
+
inline default, ``${VAR_NAME:-default}``. References are resolved from the
|
|
5
|
+
process environment (which includes a loaded ``.env`` file) at the moment a
|
|
6
|
+
value is *used*: when a connection is opened or a quality query is executed.
|
|
7
|
+
The loaded contract itself is never mutated, so ``export`` and ``publish``
|
|
8
|
+
write the references back verbatim.
|
|
9
|
+
|
|
10
|
+
A reference to an unset or empty variable without a default is an error;
|
|
11
|
+
an empty string is never substituted silently.
|
|
12
|
+
An untrusted contract resolves only the allow-listed part of the environment.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import fnmatch
|
|
16
|
+
import os
|
|
17
|
+
import re
|
|
18
|
+
from typing import Iterable, Mapping, Optional
|
|
19
|
+
|
|
20
|
+
from open_data_contract_standard.model import Server
|
|
21
|
+
from pydantic import BaseModel
|
|
22
|
+
|
|
23
|
+
_VARIABLE = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}")
|
|
24
|
+
|
|
25
|
+
CONTRACT_VARIABLES_ENV = "DATACONTRACT_CLI_API_CONTRACT_VARIABLES"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class VariableError(ValueError):
|
|
29
|
+
"""A ``${VAR}`` reference could not be turned into a usable value."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class UnresolvedVariableError(VariableError):
|
|
33
|
+
"""A ``${VAR}`` reference names a variable that is unset and has no default."""
|
|
34
|
+
|
|
35
|
+
def __init__(self, name: str, source: str = "", restricted: bool = False):
|
|
36
|
+
self.name = name
|
|
37
|
+
where = f" in {source}" if source else ""
|
|
38
|
+
# "Not listed" must read like "not set", or the message enumerates the host's environment.
|
|
39
|
+
if restricted:
|
|
40
|
+
reason = "is not available. Check if the API permits the resolution (--contract-variables needs to be set)."
|
|
41
|
+
else:
|
|
42
|
+
reason = f"is not set. Set it in the environment or a .env file, or use ${{{name}:-default}}."
|
|
43
|
+
super().__init__(f"Variable {name} referenced{where} {reason}")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def allowed_environment(patterns: Iterable[str]) -> dict[str, str]:
|
|
47
|
+
"""The environment entries whose names match one of the ``fnmatch`` ``patterns``."""
|
|
48
|
+
globs = [pattern.strip() for pattern in patterns if pattern and pattern.strip()]
|
|
49
|
+
return {name: value for name, value in os.environ.items() if any(fnmatch.fnmatchcase(name, glob) for glob in globs)}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# The ODCS enums a reference must resolve into, by (model, field); a test keeps them in step with the schema.
|
|
53
|
+
_ENUM_VALUES: dict[tuple[str, str], frozenset[str]] = {
|
|
54
|
+
("Server", "type"): frozenset(
|
|
55
|
+
{
|
|
56
|
+
"api",
|
|
57
|
+
"athena",
|
|
58
|
+
"azure",
|
|
59
|
+
"bigquery",
|
|
60
|
+
"btrieve",
|
|
61
|
+
"clickhouse",
|
|
62
|
+
"databricks",
|
|
63
|
+
"denodo",
|
|
64
|
+
"dremio",
|
|
65
|
+
"duckdb",
|
|
66
|
+
"exasol",
|
|
67
|
+
"fastobjects",
|
|
68
|
+
"glue",
|
|
69
|
+
"hana",
|
|
70
|
+
"cloudsql",
|
|
71
|
+
"db2",
|
|
72
|
+
"hive",
|
|
73
|
+
"iceberg",
|
|
74
|
+
"impala",
|
|
75
|
+
"informix",
|
|
76
|
+
"ingres",
|
|
77
|
+
"kafka",
|
|
78
|
+
"kinesis",
|
|
79
|
+
"local",
|
|
80
|
+
"mysql",
|
|
81
|
+
"oracle",
|
|
82
|
+
"poet",
|
|
83
|
+
"postgresql",
|
|
84
|
+
"postgres",
|
|
85
|
+
"presto",
|
|
86
|
+
"pubsub",
|
|
87
|
+
"redshift",
|
|
88
|
+
"s3",
|
|
89
|
+
"sftp",
|
|
90
|
+
"snowflake",
|
|
91
|
+
"sqlserver",
|
|
92
|
+
"synapse",
|
|
93
|
+
"teradata",
|
|
94
|
+
"trino",
|
|
95
|
+
"vectorwise",
|
|
96
|
+
"versant",
|
|
97
|
+
"vertica",
|
|
98
|
+
"zen",
|
|
99
|
+
"custom",
|
|
100
|
+
}
|
|
101
|
+
),
|
|
102
|
+
("DataQuality", "metric"): frozenset(
|
|
103
|
+
{"nullValues", "missingValues", "invalidValues", "duplicateValues", "rowCount"}
|
|
104
|
+
),
|
|
105
|
+
("DataQuality", "type"): frozenset({"text", "library", "sql", "custom"}),
|
|
106
|
+
("DataQuality", "dimension"): frozenset(
|
|
107
|
+
{"accuracy", "completeness", "conformity", "consistency", "coverage", "timeliness", "uniqueness"}
|
|
108
|
+
),
|
|
109
|
+
("SchemaObject", "logicalType"): frozenset({"object", "blob"}),
|
|
110
|
+
("SchemaProperty", "logicalType"): frozenset(
|
|
111
|
+
{
|
|
112
|
+
"string",
|
|
113
|
+
"date",
|
|
114
|
+
"timestamp",
|
|
115
|
+
"time",
|
|
116
|
+
"number",
|
|
117
|
+
"integer",
|
|
118
|
+
"boolean",
|
|
119
|
+
"object",
|
|
120
|
+
"array",
|
|
121
|
+
"map",
|
|
122
|
+
"vector",
|
|
123
|
+
}
|
|
124
|
+
),
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class InvalidVariableValueError(VariableError):
|
|
129
|
+
"""A reference resolved outside the field's enum. Never quotes the value: it may be a secret."""
|
|
130
|
+
|
|
131
|
+
def __init__(self, reference: str, source: str, allowed: frozenset[str]):
|
|
132
|
+
super().__init__(
|
|
133
|
+
f"{source} resolved {reference} into an invalid value. Expected one of: {', '.join(sorted(allowed))}."
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _checked(original, resolved, owner: str, field: str, source: str):
|
|
138
|
+
"""Reject a resolved value the field's ODCS enum does not accept."""
|
|
139
|
+
allowed = _ENUM_VALUES.get((owner, field))
|
|
140
|
+
if allowed is None or not contains_variables(original) or resolved in allowed:
|
|
141
|
+
return resolved
|
|
142
|
+
raise InvalidVariableValueError(original, source, allowed)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def contains_variables(value) -> bool:
|
|
146
|
+
"""True if ``value`` is a string holding at least one ``${VAR}`` reference."""
|
|
147
|
+
return isinstance(value, str) and _VARIABLE.search(value) is not None
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def resolve_variables(value, source: str = "", variables: Optional[Mapping[str, str]] = None):
|
|
151
|
+
"""Return ``value`` with every ``${VAR}`` reference replaced.
|
|
152
|
+
|
|
153
|
+
Non-string values pass through unchanged. ``source`` names the value in
|
|
154
|
+
the error message (``"server 'prod' host"``, ``"config file x.yaml"``).
|
|
155
|
+
``variables`` replaces the process environment for an untrusted contract.
|
|
156
|
+
"""
|
|
157
|
+
if not isinstance(value, str):
|
|
158
|
+
return value
|
|
159
|
+
environment = os.environ if variables is None else variables
|
|
160
|
+
|
|
161
|
+
def replace(match: re.Match) -> str:
|
|
162
|
+
name, default = match.group(1), match.group(2)
|
|
163
|
+
env_value = environment.get(name)
|
|
164
|
+
if env_value:
|
|
165
|
+
return env_value
|
|
166
|
+
if default is not None:
|
|
167
|
+
return default
|
|
168
|
+
raise UnresolvedVariableError(name, source, restricted=variables is not None)
|
|
169
|
+
|
|
170
|
+
return _VARIABLE.sub(replace, value)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def resolve_server_variables(server: Server, variables: Optional[Mapping[str, str]] = None) -> Server:
|
|
174
|
+
"""Return a copy of ``server`` with the references in its string fields resolved.
|
|
175
|
+
|
|
176
|
+
``port`` may hold a reference (the schema allows a string there for that
|
|
177
|
+
reason); a resolved all-digit port becomes an int again. Custom property
|
|
178
|
+
values are resolved too. The contract's server is not mutated.
|
|
179
|
+
"""
|
|
180
|
+
updates = {}
|
|
181
|
+
for field in Server.model_fields:
|
|
182
|
+
value = getattr(server, field)
|
|
183
|
+
if contains_variables(value):
|
|
184
|
+
source = f"server '{server.server}' {_field_name(field)}"
|
|
185
|
+
updates[field] = _checked(
|
|
186
|
+
value, resolve_variables(value, source=source, variables=variables), "Server", field, source
|
|
187
|
+
)
|
|
188
|
+
port = updates.get("port", server.port)
|
|
189
|
+
if isinstance(port, str) and port.isdigit():
|
|
190
|
+
updates["port"] = int(port)
|
|
191
|
+
if server.customProperties:
|
|
192
|
+
custom_properties = []
|
|
193
|
+
changed = False
|
|
194
|
+
for prop in server.customProperties:
|
|
195
|
+
if contains_variables(prop.value):
|
|
196
|
+
changed = True
|
|
197
|
+
prop = prop.model_copy(
|
|
198
|
+
update={
|
|
199
|
+
"value": resolve_variables(
|
|
200
|
+
prop.value,
|
|
201
|
+
source=f"server '{server.server}' custom property '{prop.property}'",
|
|
202
|
+
variables=variables,
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
custom_properties.append(prop)
|
|
207
|
+
if changed:
|
|
208
|
+
updates["customProperties"] = custom_properties
|
|
209
|
+
return server.model_copy(update=updates) if updates else server
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _field_name(field: str) -> str:
|
|
213
|
+
return "schema" if field == "schema_" else field
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# These fields document the contract rather than supply test inputs. SQL queries
|
|
217
|
+
# resolve separately, after the engine substitutes its ${model}/${field} tokens;
|
|
218
|
+
# servers resolve separately, after configuration overrides and server selection.
|
|
219
|
+
_DEFERRED_FIELDS = {
|
|
220
|
+
"authoritativeDefinitions",
|
|
221
|
+
"businessName",
|
|
222
|
+
"context",
|
|
223
|
+
"description",
|
|
224
|
+
"examples",
|
|
225
|
+
"implementation",
|
|
226
|
+
"query",
|
|
227
|
+
"servers",
|
|
228
|
+
"synonyms",
|
|
229
|
+
"transformLogic",
|
|
230
|
+
"transformSourceObjects",
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def resolve_runtime_variables(value, source: str = "contract", variables: Optional[Mapping[str, str]] = None):
|
|
235
|
+
"""Copy test inputs with variables resolved, recursively, without altering the contract.
|
|
236
|
+
|
|
237
|
+
Handles property names/types, enum values, logical type options, library
|
|
238
|
+
quality arguments, SLA values, and nested array/map definitions. Dictionary
|
|
239
|
+
keys are structural identifiers and are never interpolated.
|
|
240
|
+
"""
|
|
241
|
+
if isinstance(value, BaseModel):
|
|
242
|
+
owner = type(value).__name__
|
|
243
|
+
updates = {}
|
|
244
|
+
for field in type(value).model_fields:
|
|
245
|
+
if field in _DEFERRED_FIELDS:
|
|
246
|
+
continue
|
|
247
|
+
original = getattr(value, field)
|
|
248
|
+
field_source = f"{source}.{_field_name(field)}"
|
|
249
|
+
resolved = resolve_runtime_variables(original, field_source, variables)
|
|
250
|
+
updates[field] = _checked(original, resolved, owner, field, field_source)
|
|
251
|
+
return value.model_copy(update=updates)
|
|
252
|
+
if isinstance(value, dict):
|
|
253
|
+
return {key: resolve_runtime_variables(item, f"{source}.{key}", variables) for key, item in value.items()}
|
|
254
|
+
if isinstance(value, list):
|
|
255
|
+
return [resolve_runtime_variables(item, f"{source}[{index}]", variables) for index, item in enumerate(value)]
|
|
256
|
+
return resolve_variables(value, source=source, variables=variables)
|
|
@@ -11,6 +11,7 @@ if typing.TYPE_CHECKING:
|
|
|
11
11
|
|
|
12
12
|
from datacontract.breaking.detector import BreakingChangeDetector
|
|
13
13
|
from datacontract.config import Config
|
|
14
|
+
from datacontract.engines.checks.create_checks import to_schema_name, unexecuted_check_name, unrunnable_reason
|
|
14
15
|
from datacontract.engines.checks.dimensions import default_dimension
|
|
15
16
|
from datacontract.engines.data_contract_test import execute_data_contract_test
|
|
16
17
|
from datacontract.export.exporter import ExportFormat
|
|
@@ -31,6 +32,53 @@ from datacontract.model.run import Check, ResultEnum, Run
|
|
|
31
32
|
logger = logging.getLogger(__name__)
|
|
32
33
|
|
|
33
34
|
|
|
35
|
+
def _unrunnable_rule_checks(data_contract: OpenDataContractStandard) -> list[Check]:
|
|
36
|
+
"""Warnings for quality rules `datacontract test` would not be able to run."""
|
|
37
|
+
checks = []
|
|
38
|
+
|
|
39
|
+
def visit(properties, schema_name, prefix=None):
|
|
40
|
+
for prop in properties or []:
|
|
41
|
+
name = prop.physicalName or prop.name
|
|
42
|
+
field = f"{prefix}.{name}" if prefix else name
|
|
43
|
+
for quality in prop.quality or []:
|
|
44
|
+
reason = unrunnable_reason(quality, field)
|
|
45
|
+
if reason is not None:
|
|
46
|
+
checks.append(
|
|
47
|
+
Check(
|
|
48
|
+
type="lint",
|
|
49
|
+
result=ResultEnum.warning,
|
|
50
|
+
name=unexecuted_check_name(schema_name, field),
|
|
51
|
+
reason=reason,
|
|
52
|
+
model=schema_name,
|
|
53
|
+
field=field,
|
|
54
|
+
engine="datacontract-cli",
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
visit(prop.properties, schema_name, field)
|
|
58
|
+
# `[]` marks the array hop, as the check keys do.
|
|
59
|
+
if prop.items is not None:
|
|
60
|
+
visit(prop.items.properties, schema_name, f"{field}[]")
|
|
61
|
+
|
|
62
|
+
for schema_obj in data_contract.schema_ or []:
|
|
63
|
+
# Named like the checks `test` reports for the same rules.
|
|
64
|
+
schema_name = to_schema_name(schema_obj, None)
|
|
65
|
+
for quality in schema_obj.quality or []:
|
|
66
|
+
reason = unrunnable_reason(quality, None)
|
|
67
|
+
if reason is not None:
|
|
68
|
+
checks.append(
|
|
69
|
+
Check(
|
|
70
|
+
type="lint",
|
|
71
|
+
result=ResultEnum.warning,
|
|
72
|
+
name=unexecuted_check_name(schema_name, None),
|
|
73
|
+
reason=reason,
|
|
74
|
+
model=schema_name,
|
|
75
|
+
engine="datacontract-cli",
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
visit(schema_obj.properties, schema_name)
|
|
79
|
+
return checks
|
|
80
|
+
|
|
81
|
+
|
|
34
82
|
class DataContract:
|
|
35
83
|
def __init__(
|
|
36
84
|
self,
|
|
@@ -58,6 +106,7 @@ class DataContract:
|
|
|
58
106
|
metadata_only: bool = False,
|
|
59
107
|
dry_run: bool = False,
|
|
60
108
|
untrusted_contract: bool = False,
|
|
109
|
+
allowed_variables: list[str] | None = None,
|
|
61
110
|
config: "Config | dict[str, str] | None" = None,
|
|
62
111
|
):
|
|
63
112
|
self._data_contract_file = data_contract_file
|
|
@@ -87,6 +136,8 @@ class DataContract:
|
|
|
87
136
|
# server): the SQL it carries must not reach the host running it, and its
|
|
88
137
|
# authoritativeDefinitions are resolved against the configured host only.
|
|
89
138
|
self._untrusted_contract = untrusted_contract
|
|
139
|
+
# fnmatch globs of the environment variables an untrusted contract may read.
|
|
140
|
+
self._allowed_variables = allowed_variables
|
|
90
141
|
self._config = Config.resolve(config)
|
|
91
142
|
|
|
92
143
|
@classmethod
|
|
@@ -120,6 +171,7 @@ class DataContract:
|
|
|
120
171
|
engine="datacontract-cli",
|
|
121
172
|
)
|
|
122
173
|
)
|
|
174
|
+
run.checks.extend(_unrunnable_rule_checks(data_contract))
|
|
123
175
|
run.dataContractId = data_contract.id
|
|
124
176
|
run.dataContractVersion = data_contract.version
|
|
125
177
|
except DataContractValidationErrors as e:
|
|
@@ -197,6 +249,7 @@ class DataContract:
|
|
|
197
249
|
dry_run=self._dry_run,
|
|
198
250
|
config=self._config,
|
|
199
251
|
untrusted_contract=self._untrusted_contract,
|
|
252
|
+
allowed_variables=self._allowed_variables,
|
|
200
253
|
)
|
|
201
254
|
|
|
202
255
|
except DataContractException as e:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.14
|