datacontract-cli 1.1.1__tar.gz → 1.1.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.1.2/LICENSE +21 -0
- {datacontract_cli-1.1.1/datacontract_cli.egg-info → datacontract_cli-1.1.2}/PKG-INFO +6 -5
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/api.py +241 -13
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/catalog/catalog.py +1 -1
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/cli.py +11 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/command_api.py +10 -3
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/command_ci.py +5 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/command_dbt.py +13 -4
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/command_test.py +9 -1
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/config/settings.py +30 -2
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/data_contract.py +13 -4
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/checks/check_spec.py +4 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/checks/create_checks.py +67 -16
- datacontract_cli-1.1.2/datacontract/engines/checks/sql_guard.py +87 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/data_contract_test.py +11 -8
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/datacontract/check_azure_blob_file.py +10 -5
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/datacontract/check_that_datacontract_contains_valid_servers_configuration.py +3 -3
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/fastjsonschema/check_jsonschema.py +15 -5
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/fastjsonschema/s3/s3_read_files.py +1 -1
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/ibis/connections/connect.py +94 -26
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/ibis/connections/duckdb_connection.py +80 -5
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/ibis/connections/kafka.py +10 -10
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/ibis/connections/redshift_credentials.py +4 -4
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/ibis/ibis_check_execute.py +47 -16
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/dbml_exporter.py +2 -2
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/dcs_exporter.py +26 -1
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/exporter.py +4 -2
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/html_exporter.py +5 -3
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/mermaid_exporter.py +11 -2
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/protobuf_exporter.py +1 -1
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/pydantic_exporter.py +4 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/sodacl_check_builder.py +20 -4
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/sql_type_converter.py +2 -2
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/athena_importer.py +3 -3
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/avro_importer.py +4 -4
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/bigquery_importer.py +5 -5
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/dbml_importer.py +1 -1
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/excel_importer.py +2 -2
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/iceberg_importer.py +1 -1
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/jsonschema_importer.py +3 -3
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/mysql_importer.py +6 -6
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/object_storage_importer.py +4 -4
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/odcs_importer.py +4 -4
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/oracle_importer.py +7 -7
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/parquet_importer.py +1 -1
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/postgres_importer.py +5 -5
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/powerbi_importer.py +7 -7
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/protobuf_importer.py +2 -2
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/pydantic_importer.py +32 -6
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/redshift_importer.py +6 -6
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/snowflake_importer.py +5 -5
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/sql_importer.py +2 -2
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/sqlserver_importer.py +6 -6
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/trino_importer.py +7 -7
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/unity_importer.py +4 -4
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/integration/dbt_sync.py +38 -28
- datacontract_cli-1.1.2/datacontract/integration/entropy_data.py +248 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/lint/files.py +1 -1
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/lint/resolve.py +14 -17
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/lint/s3.py +1 -1
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/lint/schema.py +1 -1
- datacontract_cli-1.1.2/datacontract/lint/urls.py +48 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/model/exceptions.py +16 -1
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/model/run.py +26 -5
- datacontract_cli-1.1.2/datacontract/model/server.py +112 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials_odcs/diagram.html +1 -1
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2/datacontract_cli.egg-info}/PKG-INFO +6 -5
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract_cli.egg-info/SOURCES.txt +17 -2
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract_cli.egg-info/requires.txt +5 -4
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/pyproject.toml +25 -5
- datacontract_cli-1.1.2/tests/test_api.py +655 -0
- datacontract_cli-1.1.2/tests/test_check_quality_provenance.py +77 -0
- datacontract_cli-1.1.2/tests/test_command_api.py +32 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_config.py +4 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_config_file.py +14 -2
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_connect_bigquery.py +52 -11
- datacontract_cli-1.1.2/tests/test_create_checks_servicelevel.py +87 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_data_contract_checks.py +64 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_dbt_sync.py +108 -0
- datacontract_cli-1.1.2/tests/test_export_dcs.py +131 -0
- datacontract_cli-1.1.2/tests/test_export_html.py +155 -0
- datacontract_cli-1.1.2/tests/test_export_large_contract.py +57 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_mermaid.py +36 -0
- datacontract_cli-1.1.2/tests/test_lint_urls.py +85 -0
- datacontract_cli-1.1.2/tests/test_publish_test_results.py +142 -0
- datacontract_cli-1.1.2/tests/test_publish_test_results_payload.py +141 -0
- datacontract_cli-1.1.2/tests/test_quality_sql_dialects.py +132 -0
- datacontract_cli-1.1.2/tests/test_roundtrip_pydantic_model.py +80 -0
- datacontract_cli-1.1.2/tests/test_run_check_engine.py +40 -0
- datacontract_cli-1.1.2/tests/test_server_overrides.py +104 -0
- datacontract_cli-1.1.2/tests/test_server_type_synonyms.py +85 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_azure_blob_file.py +3 -3
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_diagnostics.py +3 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_dimension_filter.py +9 -0
- datacontract_cli-1.1.2/tests/test_test_duckdb.py +194 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_postgres.py +26 -1
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_quality.py +1 -1
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_redshift.py +1 -1
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_soda_connection_error.py +1 -1
- datacontract_cli-1.1.2/tests/test_untrusted_contract.py +324 -0
- datacontract_cli-1.1.1/LICENSE +0 -48
- datacontract_cli-1.1.1/datacontract/export/odcs_export_helper.py +0 -314
- datacontract_cli-1.1.1/datacontract/integration/entropy_data.py +0 -143
- datacontract_cli-1.1.1/datacontract/lint/urls.py +0 -76
- datacontract_cli-1.1.1/datacontract/model/server.py +0 -50
- datacontract_cli-1.1.1/tests/test_api.py +0 -337
- datacontract_cli-1.1.1/tests/test_export_html.py +0 -40
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/MANIFEST.in +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/README.md +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/__init__.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/changelog/__init__.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/changelog/changelog.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/changelog/normalize.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/command_catalog.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/command_changelog.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/command_edit.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/command_export.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/command_import.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/command_init.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/command_lint.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/command_publish.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/config/__init__.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/config/cli_context.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/VERSION +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/abap-Bs8kvjsq.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/apex-Dspwrl5m.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/assets/editor.worker-4.7.0.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/assets/yaml.worker-4.7.0.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/azcli-k1BiOau1.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/bat-BIP72KWc.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/bicep-VhQeYhS3.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/cameligo-DnuAXtHg.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/clojure-BAuDPsal.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/coffee-BV06qSnj.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/cpp-Cz8Jeyrf.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/csharp-F6hhlXFb.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/csp-KzlEHCyI.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/css-e4FKntZ3.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/cssMode-BNkTa2xp.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/cypher-CpvDGKRr.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/dart-DdHeFWR0.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/datacontract-editor.css +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/datacontract-editor.es.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/dockerfile-aoNXDcio.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/ecl-C4Q0DYuA.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/elixir-C9X42b0T.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/flow9-Bff42pfB.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/freemarker2-Dyfzmb5A.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/fsharp-C6vPAsHG.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/go-DEr8zx9f.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/graphql-BiNBBRFh.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/handlebars-BtDNI-uC.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/hcl-2T9lKuFg.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/html-Cn4TGw_o.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/htmlMode-Bf84vbo2.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/index.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/ini-CZGtssYM.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/java-B260NeH3.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/javascript-B86Kac07.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/jsonMode-Ckgg5lAE.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/julia-kuNLM9c5.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/kotlin-Bz6kAefA.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/less-AZUtS89I.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/lexon-BcmgnyRb.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/liquid-D0NPwuu_.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/logo_fuchsia_v2.svg +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/lua-Dz1RbATg.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/m3-CIMooLFn.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/markdown-C1BsCK9P.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/mdx-QFRSBtVQ.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/mips-BovsnfLQ.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/monaco-4.7.0.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/msdax-CiRB2ZB-.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/mysql-BwdpPE19.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/objective-c-D4nfHfso.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/odcs.svg +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/pascal--j5-PYtz.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/pascaligo-C7TcJxNL.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/perl-BzEWqUb5.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/pgsql-B_KZ0mGX.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/php-A19QHzQV.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/pla-COeKY1hA.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/postiats-BfMp7zJL.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/powerquery-B0TS_5Gb.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/powershell-5jSbKdYB.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/protobuf-YgnOutn3.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/pug-DLej6kn5.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/python-C8RW9B6A.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/qsharp-AhjqtSHB.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/r-D-F4P80w.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/razor-CLZqdY_r.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/redis-hwwIlMnV.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/redshift-Bcm_kjEG.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/restructuredtext-CrdzfPP-.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/ruby-QC2UNm1s.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/rust-5aqiQ3L5.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/sb-B1g1ZfLJ.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/scala-C2U4R5Gb.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/scheme-DeFRbslr.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/scss-RSTCiCmI.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/shell-BpF-iNu8.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/solidity-C00r2mbQ.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/sophia-DVW5ZKDB.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/sparql-B0nmpxeX.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/sql-CJwKPERn.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/st-DPlKG5DT.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/swift-YZBAbVHV.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/systemverilog-CDQSaH63.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/tcl-Dlu8sp6Q.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/tsMode-yleMwhPq.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/twig-C5TO7-kw.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/typescript-CJBeebKU.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/typespec-BsmjDEu7.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/vb-ChHvIDrk.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/wgsl-XEp1ju8c.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/xml-vC-NMVX1.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/editor_assets/yaml-Dlf2VF-T.js +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/__init__.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/checks/__init__.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/checks/check_filter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/checks/dimensions.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/checks/physical_type_match.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/checks/type_normalize.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/datacontract/check_that_datacontract_file_exists.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/ibis/__init__.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/ibis/connections/__init__.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/ibis/connections/aws_credentials.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/ibis/connections/databricks_patch.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/ibis/connections/oracle_patch.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/ibis/connections/redshift_patch.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/ibis/dtype_category.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/ibis/native_type.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/engines/ibis/snowflake_structured_types.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/__init__.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/avro_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/avro_idl_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/bigquery_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/custom_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/data_caterer_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/dbt_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/dqx_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/duckdb_type_converter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/excel_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/exporter_factory.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/go_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/great_expectations_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/iceberg_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/jsonschema_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/markdown_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/odcs_v3_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/pandas_type_converter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/rdf_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/sodacl_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/spark_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/sql_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/export/sqlalchemy_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/csv_importer.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/dbt_importer.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/dcs_importer.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/glue_importer.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/importer.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/importer_factory.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/json_importer.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/odcs_helper.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/spark_importer.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/imports/spark_type_json.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/init/init_template.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/integration/dbt_test_mapping.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/lint/resources.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/model/changelog.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/model/odcs.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/output/__init__.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/output/ci_output.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/output/json_test_results.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/output/junit_test_results.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/output/output_format.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/output/test_results_writer.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/output/text_changelog_results.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/py.typed +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/schemas/datacontract-1.1.0.init.yaml +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/schemas/datacontract-1.1.0.schema.json +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/schemas/datacontract-1.2.0.init.yaml +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/schemas/datacontract-1.2.0.schema.json +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/schemas/datacontract-1.2.1.init.yaml +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/schemas/datacontract-1.2.1.schema.json +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/schemas/odcs-3.0.1.schema.json +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/schemas/odcs-3.0.2.schema.json +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/schemas/odcs-3.1.0.init.yaml +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/schemas/odcs-3.1.0.schema.json +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/datacontract.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/datacontract_odcs.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/excel/odcs-template.xlsx +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/index.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/macros/authoritative_definitions.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/macros/schema.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/macros/server.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/macros/support.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials/datacontract_information.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials/datacontract_servicelevels.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials/datacontract_terms.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials/definition.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials/example.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials/model_field.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials/quality.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials/server.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials_odcs/custom_properties.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials_odcs/dialog_yaml.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials_odcs/footer.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials_odcs/fundamentals.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials_odcs/header.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials_odcs/pricing.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials_odcs/roles.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials_odcs/schema.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials_odcs/servers.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials_odcs/sla.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials_odcs/support.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials_odcs/team.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials_odcs/term_of_use.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/partials_odcs/title.html +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract/templates/style/output.css +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract_cli.egg-info/dependency_links.txt +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract_cli.egg-info/entry_points.txt +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/datacontract_cli.egg-info/top_level.txt +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/setup.cfg +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_api_openapi.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_catalog.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_changelog.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_changelog_engine.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_changelog_normalize.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_changelog_output_text.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_ci_output.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_cli.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_config_require.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_config_threading.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_connect_athena.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_connect_databricks.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_connect_impala.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_connect_mysql.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_connect_oracle.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_connect_postgres.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_connect_redshift.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_connect_s3_credentials.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_connect_snowflake.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_connect_sqlserver.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_connect_trino.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_create_checks_nested_type.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_create_checks_physical_type.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_create_checks_primary_key.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_create_checks_sql_placeholders.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_data_contract.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_data_contract_specification.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_dbt_test_mapping.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_description_linter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_docs_commands.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_docs_config_options.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_docs_ordering.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_download_datacontract_file.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_dtype_category.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_duckdb_json.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_edit.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_avro.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_avro_idl.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_bigquery.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_complex_data_contract.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_custom.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_custom_exporter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_custom_model.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_data_caterer.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_dbml.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_dbt_models.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_dbt_sources.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_dbt_staging_sql.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_dqx.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_excel.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_go.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_great_expectations.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_iceberg.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_jsonschema.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_markdown.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_odcs_v3.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_protobuf.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_pydantic.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_rdf.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_sodacl.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_spark.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_sql.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_sql_clickhouse.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_sql_query.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_export_sqlalchemy.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_ibis_mssql_pattern.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_ibis_mssql_schema.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_ibis_oracle_schema.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_athena.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_avro.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_bigquery.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_csv.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_dbml.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_dbt.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_excel.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_glue.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_iceberg.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_json.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_jsonschema.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_mysql.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_object_storage.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_odcs_v3.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_oracle.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_parquet.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_postgres.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_powerbi.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_protobuf.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_pydantic_model.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_redshift.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_snowflake.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_spark_type_json.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_sql_oracle.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_sql_postgres.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_sql_snowflake.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_sql_sqlserver.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_sqlserver.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_trino.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_import_unity_file.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_install_extras.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_integration_entropydata.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_lint.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_native_type_schema_scope.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_oracle_patch.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_physical_type_match.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_redshift_credentials.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_redshift_patch.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_resolve.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_resolve_definitions.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_roundtrip_jsonschema.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_run_check_deprecated_fields.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_server_type_mssql.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_snowflake_structured_types.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_sql_type_converter_physicaltype.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_api.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_athena_iceberg.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_azure_remote.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_bigquery.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_checks_filter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_databricks.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_delta.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_failed_samples.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_gcs_csv_remote.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_gcs_json_remote.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_kafka.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_kafka_decoding.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_kafka_remote.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_local_json.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_local_json_nd.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_metadata_only.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_mysql.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_oracle.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_oracle_xe.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_output_json.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_output_junit.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_parquet.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_quality_id_and_tag_filter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_quality_local_fallback.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_quality_percent_severity.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_row_filter.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_s3_csv.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_s3_delta.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_s3_json.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_s3_json_complex.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_s3_json_multiple_models.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_s3_json_remote.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_schema_evolution.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_server_not_found.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_snowflake.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_sqlserver.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_test_trino.py +0 -0
- {datacontract_cli-1.1.1 → datacontract_cli-1.1.2}/tests/test_type_normalize.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Entropy Data GmbH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datacontract-cli
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.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
|
|
@@ -49,7 +49,7 @@ Requires-Dist: datacontract-specification<2.0.0,>=1.2.3
|
|
|
49
49
|
Requires-Dist: open-data-contract-standard<4.0.0,>=3.1.2
|
|
50
50
|
Requires-Dist: deepdiff<10.0.0,>=6.0.0
|
|
51
51
|
Provides-Extra: avro
|
|
52
|
-
Requires-Dist: avro==1.12.
|
|
52
|
+
Requires-Dist: avro==1.12.2; extra == "avro"
|
|
53
53
|
Provides-Extra: bigquery
|
|
54
54
|
Requires-Dist: ibis-framework[bigquery]<13.0.0,>=10.0.0; extra == "bigquery"
|
|
55
55
|
Provides-Extra: csv
|
|
@@ -67,7 +67,7 @@ Requires-Dist: rich>=12.4.4; extra == "dataframe"
|
|
|
67
67
|
Provides-Extra: databricks
|
|
68
68
|
Requires-Dist: ibis-framework[databricks]<13.0.0,>=10.0.0; extra == "databricks"
|
|
69
69
|
Requires-Dist: databricks-sql-connector<4.5.0,>=3.7.0; extra == "databricks"
|
|
70
|
-
Requires-Dist: databricks-sdk<0.
|
|
70
|
+
Requires-Dist: databricks-sdk<0.134.0; extra == "databricks"
|
|
71
71
|
Requires-Dist: packaging<27.0,>=21.3; extra == "databricks"
|
|
72
72
|
Provides-Extra: databricks-runtime
|
|
73
73
|
Requires-Dist: datacontract-cli[databricks]; extra == "databricks-runtime"
|
|
@@ -143,13 +143,14 @@ Requires-Dist: openapi-spec-validator<0.10,>=0.7; extra == "dev"
|
|
|
143
143
|
Requires-Dist: pandas>=2.1.0; extra == "dev"
|
|
144
144
|
Requires-Dist: pre-commit<4.7.0,>=3.7.1; extra == "dev"
|
|
145
145
|
Requires-Dist: pytest; extra == "dev"
|
|
146
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
146
147
|
Requires-Dist: pytest-xdist; extra == "dev"
|
|
147
148
|
Requires-Dist: pymssql==2.3.13; extra == "dev"
|
|
148
149
|
Requires-Dist: psycopg2-binary<3.0,>=2.9; extra == "dev"
|
|
149
150
|
Requires-Dist: mysql-connector-python<26.8.0,>=8.0.30; extra == "dev"
|
|
150
|
-
Requires-Dist: ruff==0.16.
|
|
151
|
+
Requires-Dist: ruff==0.16.4; extra == "dev"
|
|
151
152
|
Requires-Dist: testcontainers[kafka,minio,mssql,mysql,postgres]==4.15.0; extra == "dev"
|
|
152
|
-
Requires-Dist: trino==0.
|
|
153
|
+
Requires-Dist: trino==0.339.0; extra == "dev"
|
|
153
154
|
Dynamic: license-file
|
|
154
155
|
|
|
155
156
|
# Data Contract CLI
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
|
+
import secrets
|
|
4
5
|
import tempfile
|
|
5
6
|
from importlib import metadata
|
|
6
7
|
from typing import Annotated, Optional
|
|
@@ -8,7 +9,6 @@ from typing import Annotated, Optional
|
|
|
8
9
|
import pydantic
|
|
9
10
|
import yaml
|
|
10
11
|
from fastapi import Body, Depends, FastAPI, HTTPException, Query, Request, status
|
|
11
|
-
from fastapi.middleware.cors import CORSMiddleware
|
|
12
12
|
from fastapi.responses import PlainTextResponse
|
|
13
13
|
from fastapi.security.api_key import APIKeyHeader
|
|
14
14
|
from pydantic import BaseModel, Field, ValidationError
|
|
@@ -16,7 +16,7 @@ from pydantic import BaseModel, Field, ValidationError
|
|
|
16
16
|
from datacontract.config import Config, known_env_names
|
|
17
17
|
from datacontract.data_contract import DataContract, ExportFormat
|
|
18
18
|
from datacontract.model.changelog import ChangelogEntry
|
|
19
|
-
from datacontract.model.exceptions import DataContractException
|
|
19
|
+
from datacontract.model.exceptions import DataContractException, DefinitionResolutionError
|
|
20
20
|
from datacontract.model.run import Check, ResultEnum, Run
|
|
21
21
|
|
|
22
22
|
DATA_CONTRACT_EXAMPLE_PAYLOAD = """apiVersion: v3.1.0
|
|
@@ -251,6 +251,16 @@ that value, and answers `401` when the header is missing and `403` when it is wr
|
|
|
251
251
|
Securing the API is strongly recommended: `POST /test` connects to your data sources, and test
|
|
252
252
|
results may contain sensitive information.
|
|
253
253
|
|
|
254
|
+
## Contracts are untrusted
|
|
255
|
+
|
|
256
|
+
A posted contract carries SQL and names the hosts to connect to, so the server treats it as
|
|
257
|
+
untrusted input: a `quality.type: sql` rule must be a read-only query, a credential held in the
|
|
258
|
+
server's environment is never sent to a host the contract names, a `publish_url` may only point at
|
|
259
|
+
the Entropy Data platform or the host configured via `ENTROPY_DATA_HOST`, and `servers[].type: local`
|
|
260
|
+
is refused so a caller cannot read the server's own files. Set
|
|
261
|
+
`DATACONTRACT_CLI_API_ALLOW_LOCAL_FILES=true` if the deployment serves its own files on purpose;
|
|
262
|
+
the contract is then still confined to the paths it declares.
|
|
263
|
+
|
|
254
264
|
## Connecting to data sources
|
|
255
265
|
|
|
256
266
|
`POST /test` needs credentials for the server described in the contract's `servers` section. Provide
|
|
@@ -334,12 +344,17 @@ def _openapi_with_external_docs() -> dict:
|
|
|
334
344
|
|
|
335
345
|
app.openapi = _openapi_with_external_docs
|
|
336
346
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
347
|
+
# Contracts posted to this server arrive over HTTP from callers we do not trust,
|
|
348
|
+
# so a credential the server holds in its environment must never be sent to a
|
|
349
|
+
# host the contract names. The `edit` server reuses the `test` handler for a
|
|
350
|
+
# local file the user owns, so it does not set this flag and is not guarded.
|
|
351
|
+
app.state.untrusted_contracts = True
|
|
352
|
+
|
|
353
|
+
# No CORS middleware: the only browser client is the Swagger UI served at "/",
|
|
354
|
+
# which is same-origin and therefore not subject to CORS. Every other client
|
|
355
|
+
# (curl, the SDK, CI) is not a browser and ignores CORS. Allowing arbitrary
|
|
356
|
+
# origins would only let a page the operator happens to visit drive this server
|
|
357
|
+
# and read its responses, so the safe default is to send no CORS headers at all.
|
|
343
358
|
|
|
344
359
|
api_key_header = APIKeyHeader(
|
|
345
360
|
name="x-api-key",
|
|
@@ -446,6 +461,198 @@ def _parse_filters_query(filters: str | None) -> dict[str, str] | None:
|
|
|
446
461
|
return {schema_name: predicate.strip() for schema_name, predicate in parsed.items()}
|
|
447
462
|
|
|
448
463
|
|
|
464
|
+
# For each server type whose connection host can be taken from the posted
|
|
465
|
+
# contract: (the Server attribute that names the connection target, the Config
|
|
466
|
+
# field an operator sets to pin that target in the environment or None when the
|
|
467
|
+
# type has no such field, the Config fields that hold a connection secret).
|
|
468
|
+
# See _reject_environment_credentials_to_untrusted_host.
|
|
469
|
+
_ENVIRONMENT_CREDENTIAL_TARGETS: dict[str, tuple[str, str | None, tuple[str, ...]]] = {
|
|
470
|
+
"postgres": ("host", "postgres_host", ("postgres_password",)),
|
|
471
|
+
"mysql": ("host", "mysql_host", ("mysql_password",)),
|
|
472
|
+
"oracle": ("host", "oracle_host", ("oracle_password",)),
|
|
473
|
+
"impala": ("host", "impala_host", ("impala_password",)),
|
|
474
|
+
"trino": ("host", "trino_host", ("trino_password",)),
|
|
475
|
+
"redshift": ("host", "redshift_host", ("redshift_password",)),
|
|
476
|
+
"sqlserver": ("host", "sqlserver_host", ("sqlserver_password",)),
|
|
477
|
+
"snowflake": (
|
|
478
|
+
"account",
|
|
479
|
+
"snowflake_account",
|
|
480
|
+
("snowflake_password", "snowflake_token", "snowflake_private_key"),
|
|
481
|
+
),
|
|
482
|
+
"databricks": ("host", "databricks_server_hostname", ("databricks_token", "databricks_client_secret")),
|
|
483
|
+
# Kafka's broker host is always the contract's `server.host` (no environment
|
|
484
|
+
# option pins it), so an environment-held SASL password can never be paired
|
|
485
|
+
# with a posted contract safely -- host_field is None, so it is always rejected.
|
|
486
|
+
"kafka": ("broker host", None, ("kafka_sasl_password",)),
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def _selected_server(body: str, server_name: str | None, config):
|
|
491
|
+
"""The server the request would test, or None if that cannot be determined.
|
|
492
|
+
|
|
493
|
+
A contract that does not resolve, or that names no such server, is reported
|
|
494
|
+
by test() itself -- the guards below simply have nothing to check.
|
|
495
|
+
"""
|
|
496
|
+
from datacontract.engines.data_contract_test import get_server
|
|
497
|
+
from datacontract.lint import resolve
|
|
498
|
+
|
|
499
|
+
try:
|
|
500
|
+
data_contract = resolve.resolve_data_contract(data_contract_str=body, config=config)
|
|
501
|
+
return get_server(data_contract, server_name)
|
|
502
|
+
except Exception:
|
|
503
|
+
return None
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
# Server types that read their data through the local filesystem of the machine
|
|
507
|
+
# running the test, rather than from a remote data source.
|
|
508
|
+
_LOCAL_SERVER_TYPES = frozenset({"local", "duckdb"})
|
|
509
|
+
|
|
510
|
+
ALLOW_LOCAL_FILES_ENV = "DATACONTRACT_CLI_API_ALLOW_LOCAL_FILES"
|
|
511
|
+
_TRUTHY = frozenset({"1", "true", "yes", "on"})
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def _local_files_allowed() -> bool:
|
|
515
|
+
"""Whether the operator opted in to serving data from the server's own disk.
|
|
516
|
+
|
|
517
|
+
Off by default. A deployment that mounts the data next to the API -- the data
|
|
518
|
+
directory in the container, say -- turns it on, accepting that a caller then
|
|
519
|
+
picks the path.
|
|
520
|
+
"""
|
|
521
|
+
return (os.getenv(ALLOW_LOCAL_FILES_ENV) or "").strip().lower() in _TRUTHY
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def _reject_local_server_type(body: str, server_name: str | None, config) -> None:
|
|
525
|
+
"""Keep a posted contract from reading the server's own disk.
|
|
526
|
+
|
|
527
|
+
`type: local` points the test at a path on the machine running it. For the
|
|
528
|
+
API server that machine is not the caller's, so the path is not theirs to
|
|
529
|
+
name -- and the result would tell them what is in it.
|
|
530
|
+
"""
|
|
531
|
+
from datacontract.config import Config
|
|
532
|
+
|
|
533
|
+
if _local_files_allowed():
|
|
534
|
+
return
|
|
535
|
+
server = _selected_server(body, server_name, Config.resolve(config))
|
|
536
|
+
if server is None or server.type not in _LOCAL_SERVER_TYPES:
|
|
537
|
+
return
|
|
538
|
+
raise HTTPException(
|
|
539
|
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
540
|
+
detail=(
|
|
541
|
+
f"Server type '{server.type}' reads from the file system of the server running this API, "
|
|
542
|
+
f"so it is refused. Use a server type that names a data source, such as s3, gcs, azure, "
|
|
543
|
+
f"postgres, or snowflake — or set {ALLOW_LOCAL_FILES_ENV}=true if this deployment serves "
|
|
544
|
+
f"its own files on purpose."
|
|
545
|
+
),
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
def _reject_environment_credentials_to_untrusted_host(body: str, server_name: str | None, config) -> None:
|
|
550
|
+
"""Keep an environment-held data-source credential from reaching a host the
|
|
551
|
+
posted contract chose.
|
|
552
|
+
|
|
553
|
+
A contract that arrives over HTTP is untrusted. If it could name the
|
|
554
|
+
connection host (its ``servers`` section) while the server supplies the
|
|
555
|
+
password from its own environment, an attacker could point the host at their
|
|
556
|
+
own machine and capture that password. So when a credential for the selected
|
|
557
|
+
server's type resolves from the environment, the connection target must
|
|
558
|
+
resolve from the environment too -- not from the contract, and not from a
|
|
559
|
+
per-request header (a caller who brings the whole credential set may still
|
|
560
|
+
aim it wherever they like; only the server's environment secret is pinned).
|
|
561
|
+
"""
|
|
562
|
+
from datacontract.config import Config
|
|
563
|
+
from datacontract.config.settings import env_name
|
|
564
|
+
|
|
565
|
+
resolved = Config.resolve(config)
|
|
566
|
+
server = _selected_server(body, server_name, resolved)
|
|
567
|
+
if server is None:
|
|
568
|
+
return
|
|
569
|
+
guard = _ENVIRONMENT_CREDENTIAL_TARGETS.get(server.type)
|
|
570
|
+
if guard is None:
|
|
571
|
+
return
|
|
572
|
+
target_attr, host_field, secret_fields = guard
|
|
573
|
+
if not any(resolved.option_source(field) == "env" for field in secret_fields):
|
|
574
|
+
return # no environment-held secret for this server type; nothing to protect
|
|
575
|
+
if host_field is not None and resolved.option_source(host_field) == "env":
|
|
576
|
+
return # the operator pinned the target host; the contract's host is ignored
|
|
577
|
+
if host_field is not None:
|
|
578
|
+
env_var = env_name(host_field, Config.model_fields[host_field])
|
|
579
|
+
detail = (
|
|
580
|
+
f"This server holds a {server.type} credential in its environment, so the connection "
|
|
581
|
+
f"{target_attr} must also come from the server's environment ({env_var}); it will not be "
|
|
582
|
+
f"taken from the posted data contract. Set {env_var}, or send the full set of credentials "
|
|
583
|
+
f"with the request instead of relying on the server's environment."
|
|
584
|
+
)
|
|
585
|
+
else:
|
|
586
|
+
detail = (
|
|
587
|
+
f"This server holds a {server.type} credential in its environment, but its {target_attr} "
|
|
588
|
+
f"can only come from the posted data contract, so the credential cannot be pinned to a "
|
|
589
|
+
f"trusted target. Send the credentials with the request instead of relying on the server's "
|
|
590
|
+
f"environment."
|
|
591
|
+
)
|
|
592
|
+
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=detail)
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
# The fields backing the platform API key and host, in the fallback order
|
|
596
|
+
# _get_api_key / _get_host read them (current name first, deprecated synonyms after).
|
|
597
|
+
_PLATFORM_KEY_FIELDS = ("entropy_data_api_key", "datamesh_manager_api_key", "datacontract_manager_api_key")
|
|
598
|
+
_PLATFORM_HOST_FIELDS = ("entropy_data_host", "datamesh_manager_host", "datacontract_manager_host")
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def _reject_unpinned_publish_url(publish_url: str | None) -> None:
|
|
602
|
+
"""Keep the API server from POSTing test results to a caller-chosen host.
|
|
603
|
+
|
|
604
|
+
`publish_url` names where the finished run — check details, failed-row
|
|
605
|
+
samples — is POSTed, so an arbitrary URL would let a caller aim a blind
|
|
606
|
+
POST into the operator's network and exfiltrate the results. The target
|
|
607
|
+
must be a platform URL as the server's own environment sees it: a
|
|
608
|
+
per-request `entropy-data-host` header does not widen the set, or the
|
|
609
|
+
guard would be the caller's to configure.
|
|
610
|
+
"""
|
|
611
|
+
from datacontract.integration.entropy_data import is_platform_url
|
|
612
|
+
|
|
613
|
+
if publish_url is None or is_platform_url(publish_url, None):
|
|
614
|
+
return
|
|
615
|
+
raise HTTPException(
|
|
616
|
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
617
|
+
detail=(
|
|
618
|
+
"The publish_url must point at the Entropy Data platform. To publish to a self-hosted "
|
|
619
|
+
"deployment, set the environment variable ENTROPY_DATA_HOST on the server running this "
|
|
620
|
+
"API to that host."
|
|
621
|
+
),
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
def _reject_request_platform_host_with_environment_key(config) -> None:
|
|
626
|
+
"""Keep the environment-held platform API key from following a request-chosen host.
|
|
627
|
+
|
|
628
|
+
The platform host decides where the Entropy Data API key travels, and it can
|
|
629
|
+
be set per request (`entropy-data-host` header). A request that supplies the
|
|
630
|
+
host while the key resolves from the server's environment could therefore
|
|
631
|
+
aim that key at its own machine — same rule as the data sources: an
|
|
632
|
+
environment credential goes only to an environment-pinned destination. A
|
|
633
|
+
caller who brings the key itself may aim it wherever they like.
|
|
634
|
+
"""
|
|
635
|
+
from datacontract.config import Config
|
|
636
|
+
|
|
637
|
+
resolved = Config.resolve(config)
|
|
638
|
+
key_source = next(
|
|
639
|
+
(source for field in _PLATFORM_KEY_FIELDS if (source := resolved.option_source(field)) is not None), None
|
|
640
|
+
)
|
|
641
|
+
if key_source != "env":
|
|
642
|
+
return
|
|
643
|
+
if not any(resolved.option_source(field) == "request" for field in _PLATFORM_HOST_FIELDS):
|
|
644
|
+
return
|
|
645
|
+
raise HTTPException(
|
|
646
|
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
647
|
+
detail=(
|
|
648
|
+
"This server holds an Entropy Data API key in its environment, so the platform host must "
|
|
649
|
+
"also come from the server's environment (ENTROPY_DATA_HOST); it will not be taken from a "
|
|
650
|
+
"request header. Remove the entropy-data-host header, or send the API key with the request "
|
|
651
|
+
"instead of relying on the server's environment."
|
|
652
|
+
),
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
|
|
449
656
|
def check_api_key(api_key_header: str | None):
|
|
450
657
|
correct_api_key = os.getenv("DATACONTRACT_CLI_API_KEY")
|
|
451
658
|
if correct_api_key is None or correct_api_key == "":
|
|
@@ -457,7 +664,7 @@ def check_api_key(api_key_header: str | None):
|
|
|
457
664
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
458
665
|
detail="Missing API key. Use Header 'x-api-key' to provide the API key.",
|
|
459
666
|
)
|
|
460
|
-
if api_key_header
|
|
667
|
+
if not secrets.compare_digest(api_key_header, correct_api_key):
|
|
461
668
|
logging.info("The provided API key is not correct.")
|
|
462
669
|
raise HTTPException(
|
|
463
670
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
@@ -500,8 +707,9 @@ handled. A contract that cannot be parsed is reported the same way, as a failed
|
|
|
500
707
|
},
|
|
501
708
|
},
|
|
502
709
|
422: {
|
|
503
|
-
"description": "`filter` and `filters` were both given,
|
|
504
|
-
"mapping schema name to a SQL predicate
|
|
710
|
+
"description": "`filter` and `filters` were both given, `filters` is not a JSON object "
|
|
711
|
+
"mapping schema name to a SQL predicate, or `publish_url` does not point at the Entropy "
|
|
712
|
+
"Data platform or the host configured via `ENTROPY_DATA_HOST`.",
|
|
505
713
|
"model": UnprocessableEntityResponse,
|
|
506
714
|
},
|
|
507
715
|
},
|
|
@@ -529,7 +737,9 @@ async def test(
|
|
|
529
737
|
publish_url: Annotated[
|
|
530
738
|
str | None,
|
|
531
739
|
Query(
|
|
532
|
-
description="URL to publish test results. Optional, if you want to publish the test results to a Data Mesh Manager or Data Contract Manager. Example: https://api.datamesh-manager.com/api/test-results"
|
|
740
|
+
description="URL to publish test results. Optional, if you want to publish the test results to a Data Mesh Manager or Data Contract Manager. Example: https://api.datamesh-manager.com/api/test-results. "
|
|
741
|
+
"Must point at the Entropy Data platform, or at the host configured on this server "
|
|
742
|
+
"via the environment variable ENTROPY_DATA_HOST — other hosts are refused with 422.",
|
|
533
743
|
examples=["https://api.datamesh-manager.com/api/test-results"],
|
|
534
744
|
),
|
|
535
745
|
] = None,
|
|
@@ -560,14 +770,22 @@ async def test(
|
|
|
560
770
|
)
|
|
561
771
|
logging.info("Testing data contract...")
|
|
562
772
|
logging.info(body)
|
|
773
|
+
config = config_from_headers(request.headers)
|
|
774
|
+
untrusted_contract = getattr(request.app.state, "untrusted_contracts", False)
|
|
775
|
+
if untrusted_contract:
|
|
776
|
+
_reject_unpinned_publish_url(publish_url)
|
|
777
|
+
_reject_request_platform_host_with_environment_key(config)
|
|
778
|
+
_reject_environment_credentials_to_untrusted_host(body, server, config)
|
|
779
|
+
_reject_local_server_type(body, server, config)
|
|
563
780
|
return DataContract(
|
|
564
781
|
data_contract_str=body,
|
|
565
782
|
server=server,
|
|
566
783
|
publish_url=publish_url,
|
|
567
784
|
fastapi_url=str(request.url),
|
|
568
|
-
config=
|
|
785
|
+
config=config,
|
|
569
786
|
filter=filter,
|
|
570
787
|
filters=parsed_filters,
|
|
788
|
+
untrusted_contract=untrusted_contract,
|
|
571
789
|
).test()
|
|
572
790
|
|
|
573
791
|
|
|
@@ -705,6 +923,11 @@ async def changelog_endpoint(
|
|
|
705
923
|
raise HTTPException(status_code=422, detail=f"Invalid YAML: {e}")
|
|
706
924
|
except pydantic.ValidationError as e:
|
|
707
925
|
raise HTTPException(status_code=422, detail=f"Invalid data contract: {e}")
|
|
926
|
+
except DefinitionResolutionError as e:
|
|
927
|
+
# The reason names the host that was contacted and what it answered.
|
|
928
|
+
# Omit for security reasons.
|
|
929
|
+
logging.warning("Definition resolution failed: %s", e)
|
|
930
|
+
raise HTTPException(status_code=422, detail=f"Could not resolve authoritative definition '{e.url}'.")
|
|
708
931
|
except DataContractException as e:
|
|
709
932
|
raise HTTPException(status_code=422, detail=f"Data Contract Validation Failure: {e}")
|
|
710
933
|
finally:
|
|
@@ -823,5 +1046,10 @@ def export(
|
|
|
823
1046
|
raise HTTPException(status_code=422, detail=f"Invalid YAML: {e}")
|
|
824
1047
|
except pydantic.ValidationError as e:
|
|
825
1048
|
raise HTTPException(status_code=422, detail=f"Invalid data contract: {e}")
|
|
1049
|
+
except DefinitionResolutionError as e:
|
|
1050
|
+
# The reason names the host that was contacted and what it answered.
|
|
1051
|
+
# Omit for security reasons.
|
|
1052
|
+
logging.warning("Definition resolution failed: %s", e)
|
|
1053
|
+
raise HTTPException(status_code=422, detail=f"Could not resolve authoritative definition '{e.url}'.")
|
|
826
1054
|
except DataContractException as e:
|
|
827
1055
|
raise HTTPException(status_code=422, detail=f"Data Contract Validation Failure: {e}")
|
|
@@ -245,6 +245,17 @@ def _print_logs(run, out=None):
|
|
|
245
245
|
out.print(log.timestamp.strftime("%y-%m-%d %H:%M:%S"), log.level.ljust(5), log.message)
|
|
246
246
|
|
|
247
247
|
|
|
248
|
+
def _print_publish_failure(run, out=None):
|
|
249
|
+
"""Surface the publish log messages on the console; logging is suppressed without --debug."""
|
|
250
|
+
if out is None:
|
|
251
|
+
out = console
|
|
252
|
+
for log in run.logs:
|
|
253
|
+
if log.level in ("WARN", "ERROR") and "publish" in log.message.lower():
|
|
254
|
+
color = "red" if log.level == "ERROR" else "yellow"
|
|
255
|
+
# highlight=False: render as prose, not rich's code-like repr highlighting.
|
|
256
|
+
out.print(f"[{color}]{escape(log.message)}[/{color}]", highlight=False)
|
|
257
|
+
|
|
258
|
+
|
|
248
259
|
# ---------------------------------------------------------------------------
|
|
249
260
|
# Register commands (must be after app and shared helpers are defined so the
|
|
250
261
|
# command_* modules can import from this module without circular-import issues)
|
|
@@ -4,7 +4,7 @@ from typing_extensions import Annotated
|
|
|
4
4
|
from datacontract.cli import app, debug_option, enable_debug_logging
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def _get_uvicorn_arguments(port: int, host: str, context: typer.Context) -> dict:
|
|
7
|
+
def _get_uvicorn_arguments(port: int, host: str, reload: bool, context: typer.Context) -> dict:
|
|
8
8
|
"""
|
|
9
9
|
Take the default datacontract uvicorn arguments and merge them with the
|
|
10
10
|
extra arguments passed to the command to start the API.
|
|
@@ -13,7 +13,7 @@ def _get_uvicorn_arguments(port: int, host: str, context: typer.Context) -> dict
|
|
|
13
13
|
"app": "datacontract.api:app",
|
|
14
14
|
"port": port,
|
|
15
15
|
"host": host,
|
|
16
|
-
"reload":
|
|
16
|
+
"reload": reload,
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
# Create a list of the extra arguments, remove the leading -- from the cli arguments
|
|
@@ -33,6 +33,13 @@ def api(
|
|
|
33
33
|
host: Annotated[
|
|
34
34
|
str, typer.Option(help="Bind socket to this host. Hint: For running in docker, set it to 0.0.0.0")
|
|
35
35
|
] = "127.0.0.1",
|
|
36
|
+
reload: Annotated[
|
|
37
|
+
bool,
|
|
38
|
+
typer.Option(
|
|
39
|
+
"--reload/--no-reload",
|
|
40
|
+
help="Watch the source files and restart the server on changes. For development only; off by default.",
|
|
41
|
+
),
|
|
42
|
+
] = False,
|
|
36
43
|
debug: debug_option = None,
|
|
37
44
|
):
|
|
38
45
|
"""
|
|
@@ -59,7 +66,7 @@ def api(
|
|
|
59
66
|
log_config = LOGGING_CONFIG
|
|
60
67
|
log_config["root"] = {"level": "INFO"}
|
|
61
68
|
|
|
62
|
-
uvicorn_args = _get_uvicorn_arguments(port, host, ctx)
|
|
69
|
+
uvicorn_args = _get_uvicorn_arguments(port, host, reload, ctx)
|
|
63
70
|
# Add the log config
|
|
64
71
|
uvicorn_args["log_config"] = log_config
|
|
65
72
|
# Run uvicorn
|
|
@@ -8,6 +8,7 @@ from typing_extensions import Annotated
|
|
|
8
8
|
|
|
9
9
|
from datacontract.cli import (
|
|
10
10
|
_print_logs,
|
|
11
|
+
_print_publish_failure,
|
|
11
12
|
app,
|
|
12
13
|
console,
|
|
13
14
|
debug_option,
|
|
@@ -136,6 +137,10 @@ def ci(
|
|
|
136
137
|
write_test_result(run, out, output_format, output)
|
|
137
138
|
except typer.Exit:
|
|
138
139
|
pass
|
|
140
|
+
if run.publish_succeeded is False:
|
|
141
|
+
# A publish that was asked for and failed is a failed run, regardless of --fail-on.
|
|
142
|
+
_print_publish_failure(run, out)
|
|
143
|
+
should_fail = True
|
|
139
144
|
if run.result in fail_results[fail_on]:
|
|
140
145
|
should_fail = True
|
|
141
146
|
|
|
@@ -172,10 +172,11 @@ def _count(n: int, noun: str) -> str:
|
|
|
172
172
|
_MAX_LISTED_FILES = 5
|
|
173
173
|
|
|
174
174
|
|
|
175
|
-
def _print_sync_summary(gen, project_dir: Path, *, debug: bool = False) -> None:
|
|
175
|
+
def _print_sync_summary(gen, project_dir: Path, *, debug: bool = False, dry_run: bool = False) -> None:
|
|
176
176
|
"""Print the per-contract sync summary, naming the touched files."""
|
|
177
177
|
prefix = f"{gen.contract_path.name}: "
|
|
178
|
-
|
|
178
|
+
verb = "Would sync" if dry_run else "Synced"
|
|
179
|
+
line = f"{prefix}{verb} {_count(len(gen.resolved_models), 'model')}: updated {_count(len(gen.written_yaml), 'YAML file')}"
|
|
179
180
|
if gen.written_sql:
|
|
180
181
|
line += f", wrote {_count(len(gen.written_sql), 'singular SQL test')}"
|
|
181
182
|
if gen.deleted_files:
|
|
@@ -315,6 +316,12 @@ def sync_command(
|
|
|
315
316
|
bool,
|
|
316
317
|
typer.Option(help="Remove model columns and tags that are not declared in the contract."),
|
|
317
318
|
] = False,
|
|
319
|
+
dry_run: Annotated[
|
|
320
|
+
bool,
|
|
321
|
+
typer.Option(
|
|
322
|
+
"--dry-run/--no-dry-run", help="Run in dry-run mode, without applying any changes to the dbt project."
|
|
323
|
+
),
|
|
324
|
+
] = False,
|
|
318
325
|
run_tests_flag: Annotated[
|
|
319
326
|
bool,
|
|
320
327
|
typer.Option(
|
|
@@ -336,7 +343,8 @@ def sync_command(
|
|
|
336
343
|
Generate dbt tests and model metadata from one or more ODCS contracts.
|
|
337
344
|
|
|
338
345
|
Modifies the existing dbt model YAML in place (preserving comments and formatting), and creates new model YAML files
|
|
339
|
-
or singular SQL tests if needed. Use `datacontract dbt test` or pass --run-tests to run
|
|
346
|
+
or singular SQL tests if needed (if not run in dry-run mode). Use `datacontract dbt test` or pass --run-tests to run
|
|
347
|
+
the generated tests.
|
|
340
348
|
"""
|
|
341
349
|
enable_debug_logging(debug)
|
|
342
350
|
validate_publish_url(publish)
|
|
@@ -390,6 +398,7 @@ def sync_command(
|
|
|
390
398
|
prune=prune,
|
|
391
399
|
model_version=dbt_version,
|
|
392
400
|
server=server,
|
|
401
|
+
dry_run=dry_run,
|
|
393
402
|
)
|
|
394
403
|
except Exception as e:
|
|
395
404
|
if not multiple_contracts:
|
|
@@ -398,7 +407,7 @@ def sync_command(
|
|
|
398
407
|
continue
|
|
399
408
|
if not contract and not multiple_contracts:
|
|
400
409
|
console.print(f"Resolved contract {gen.contract_path}")
|
|
401
|
-
_print_sync_summary(gen, project_dir, debug=bool(debug))
|
|
410
|
+
_print_sync_summary(gen, project_dir, debug=bool(debug), dry_run=dry_run)
|
|
402
411
|
if not prune:
|
|
403
412
|
_warn_prunable(gen, project_dir)
|
|
404
413
|
ctxs.append(_ContractCtx(gen.contract_path, gen.odcs, gen.resolved_models, gen.generation_run, dbt_version))
|
|
@@ -7,6 +7,7 @@ from typing_extensions import Annotated
|
|
|
7
7
|
|
|
8
8
|
from datacontract.cli import (
|
|
9
9
|
_print_logs,
|
|
10
|
+
_print_publish_failure,
|
|
10
11
|
app,
|
|
11
12
|
console,
|
|
12
13
|
debug_option,
|
|
@@ -290,4 +291,11 @@ def test(
|
|
|
290
291
|
data_contract = resolve_data_contract(location, schema_location=schema, config=cli_config())
|
|
291
292
|
except Exception:
|
|
292
293
|
data_contract = None
|
|
293
|
-
|
|
294
|
+
try:
|
|
295
|
+
write_test_result(run, console, output_format, output, data_contract)
|
|
296
|
+
finally:
|
|
297
|
+
# Publish messages are otherwise only logged, which is suppressed without --debug.
|
|
298
|
+
if run.publish_succeeded is False:
|
|
299
|
+
_print_publish_failure(run)
|
|
300
|
+
if run.publish_succeeded is False:
|
|
301
|
+
raise typer.Exit(code=1)
|
|
@@ -38,6 +38,8 @@ SERVER_OVERRIDE_OPTIONS = {
|
|
|
38
38
|
"databricks_server_hostname": "host",
|
|
39
39
|
"databricks_catalog": "catalog",
|
|
40
40
|
"databricks_schema": "schema",
|
|
41
|
+
"duckdb_database": "database",
|
|
42
|
+
"duckdb_schema": "schema",
|
|
41
43
|
"impala_host": "host",
|
|
42
44
|
"impala_port": "port",
|
|
43
45
|
"impala_database": "database",
|
|
@@ -130,6 +132,11 @@ class Config(BaseSettings):
|
|
|
130
132
|
databricks_catalog: str | None = None
|
|
131
133
|
databricks_schema: str | None = None
|
|
132
134
|
|
|
135
|
+
# duckdb
|
|
136
|
+
# overrides for the contract's servers block
|
|
137
|
+
duckdb_database: str | None = None
|
|
138
|
+
duckdb_schema: str | None = None
|
|
139
|
+
|
|
133
140
|
# gcs
|
|
134
141
|
gcs_key_id: str | None = None
|
|
135
142
|
gcs_secret: SecretStr | None = None
|
|
@@ -348,6 +355,21 @@ class Config(BaseSettings):
|
|
|
348
355
|
value = value.get_secret_value()
|
|
349
356
|
return value
|
|
350
357
|
|
|
358
|
+
def option_source(self, field_name: str) -> str | None:
|
|
359
|
+
"""Where ``get_<field_name>()`` takes its value from.
|
|
360
|
+
|
|
361
|
+
``"request"`` when the value is set on this Config (a per-request header,
|
|
362
|
+
a config file, or a programmatic value), ``"env"`` when it falls back to
|
|
363
|
+
the process environment, and ``None`` when it is unset everywhere. Used to
|
|
364
|
+
keep a credential the server holds in its environment from being sent to a
|
|
365
|
+
host chosen by an untrusted caller.
|
|
366
|
+
"""
|
|
367
|
+
if getattr(self, field_name) is not None:
|
|
368
|
+
return "request"
|
|
369
|
+
if os.environ.get(env_name(field_name, type(self).model_fields[field_name])) is not None:
|
|
370
|
+
return "env"
|
|
371
|
+
return None
|
|
372
|
+
|
|
351
373
|
def _str_option(self, field_name: str, required: bool = False) -> str | None:
|
|
352
374
|
value = self._raw_option(field_name)
|
|
353
375
|
value = str(value) if value is not None else None
|
|
@@ -361,7 +383,7 @@ class Config(BaseSettings):
|
|
|
361
383
|
name=f"missing_env_{key}",
|
|
362
384
|
reason=f"Required configuration {key} is not set. Set the environment variable "
|
|
363
385
|
f"or pass it via DataContract(config=...) to connect to {server_type}.",
|
|
364
|
-
engine="datacontract",
|
|
386
|
+
engine="datacontract-cli",
|
|
365
387
|
)
|
|
366
388
|
return value
|
|
367
389
|
|
|
@@ -395,7 +417,7 @@ class Config(BaseSettings):
|
|
|
395
417
|
type="configuration",
|
|
396
418
|
name=f"invalid_{key}",
|
|
397
419
|
reason=f"{key} must be a whole number, got {value!r}.",
|
|
398
|
-
engine="datacontract",
|
|
420
|
+
engine="datacontract-cli",
|
|
399
421
|
)
|
|
400
422
|
|
|
401
423
|
def _bool_option(self, field_name: str, default: bool) -> bool:
|
|
@@ -608,6 +630,12 @@ class Config(BaseSettings):
|
|
|
608
630
|
def get_postgres_password(self, required: bool = False) -> str | None:
|
|
609
631
|
return self._str_option("postgres_password", required)
|
|
610
632
|
|
|
633
|
+
def get_duckdb_database(self, required: bool = False) -> str | None:
|
|
634
|
+
return self._str_option("duckdb_database", required)
|
|
635
|
+
|
|
636
|
+
def get_duckdb_schema(self, required: bool = False) -> str | None:
|
|
637
|
+
return self._str_option("duckdb_schema", required)
|
|
638
|
+
|
|
611
639
|
def get_postgres_host(self, required: bool = False) -> str | None:
|
|
612
640
|
return self._str_option("postgres_host", required)
|
|
613
641
|
|