datacontract-cli 1.1.3__tar.gz → 1.2.0__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.3/datacontract_cli.egg-info → datacontract_cli-1.2.0}/PKG-INFO +8 -7
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/api.py +1 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/breaking/detector.py +28 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/breaking/rules.py +39 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/changelog/normalize.py +70 -5
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/command_import.py +31 -5
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/config/settings.py +60 -17
- datacontract_cli-1.2.0/datacontract/config/variables.py +133 -0
- datacontract_cli-1.2.0/datacontract/editor_assets/VERSION +1 -0
- datacontract_cli-1.2.0/datacontract/editor_assets/assets/editor.worker-4.7.0.js +33 -0
- datacontract_cli-1.2.0/datacontract/editor_assets/assets/yaml.worker-4.7.0.js +452 -0
- datacontract_cli-1.2.0/datacontract/editor_assets/datacontract-editor.css +2 -0
- datacontract_cli-1.2.0/datacontract/editor_assets/datacontract-editor.es.js +84438 -0
- datacontract_cli-1.2.0/datacontract/editor_assets/index.html +81 -0
- datacontract_cli-1.2.0/datacontract/editor_assets/monaco-4.7.0.js +114821 -0
- datacontract_cli-1.2.0/datacontract/editor_assets/robots.txt +17 -0
- datacontract_cli-1.2.0/datacontract/editor_assets/rolldown-runtime-DY7j01NX.js +30 -0
- datacontract_cli-1.2.0/datacontract/editor_assets/sitemap.xml +17 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/checks/create_checks.py +30 -15
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/checks/type_normalize.py +76 -6
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/data_contract_test.py +44 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/fastjsonschema/check_jsonschema.py +1 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/connect.py +25 -5
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/duckdb_connection.py +44 -6
- datacontract_cli-1.2.0/datacontract/engines/ibis/connections/iceberg.py +216 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/kafka.py +15 -3
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/ibis/dtype_category.py +17 -15
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/ibis/ibis_check_execute.py +20 -3
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/ibis/snowflake_structured_types.py +9 -4
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/avro_exporter.py +36 -50
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/avro_idl_exporter.py +36 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/bigquery_exporter.py +6 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/data_caterer_exporter.py +2 -28
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/dcs_exporter.py +22 -2
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/exporter.py +6 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/go_exporter.py +15 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/great_expectations_exporter.py +17 -33
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/iceberg_exporter.py +14 -30
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/jsonschema_exporter.py +20 -26
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/protobuf_exporter.py +29 -4
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/pydantic_exporter.py +30 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/rdf_exporter.py +44 -9
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/sodacl_check_builder.py +3 -2
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/spark_exporter.py +14 -20
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/sql_type_converter.py +86 -16
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/sqlalchemy_exporter.py +5 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/avro_importer.py +9 -7
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/dcs_importer.py +15 -26
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/excel_importer.py +23 -5
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/glue_importer.py +5 -2
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/iceberg_importer.py +71 -22
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/jsonschema_importer.py +2 -12
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/odcs_helper.py +83 -4
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/parquet_importer.py +17 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/postgres_importer.py +4 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/protobuf_importer.py +2 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/snowflake_importer.py +4 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/spark_importer.py +7 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/spark_type_json.py +12 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/sql_importer.py +32 -5
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/unity_importer.py +17 -9
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/init/init_template.py +1 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/integration/dbt_test_mapping.py +3 -28
- datacontract_cli-1.2.0/datacontract/lint/constraints.py +50 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/lint/resolve.py +13 -2
- datacontract_cli-1.2.0/datacontract/model/enum_values.py +60 -0
- datacontract_cli-1.2.0/datacontract/model/map_type.py +74 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/model/server.py +25 -0
- datacontract_cli-1.2.0/datacontract/model/vector_type.py +110 -0
- datacontract_cli-1.1.3/datacontract/schemas/odcs-3.1.0.init.yaml → datacontract_cli-1.2.0/datacontract/schemas/odcs-3.2.0.init.yaml +1 -1
- datacontract_cli-1.2.0/datacontract/schemas/odcs-3.2.0.schema.json +4118 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/datacontract_odcs.html +1 -0
- datacontract_cli-1.2.0/datacontract/templates/macros/context.html +64 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/macros/schema.html +41 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/macros/server.html +11 -1
- datacontract_cli-1.2.0/datacontract/templates/partials_odcs/context.html +16 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/custom_properties.html +3 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/schema.html +17 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/sla.html +30 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0/datacontract_cli.egg-info}/PKG-INFO +8 -7
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract_cli.egg-info/SOURCES.txt +26 -87
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract_cli.egg-info/requires.txt +7 -6
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/pyproject.toml +9 -7
- datacontract_cli-1.2.0/tests/test_breaking_odcs_3_2.py +94 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_config_file.py +7 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_create_checks_nested_type.py +21 -9
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_dtype_category.py +13 -5
- datacontract_cli-1.2.0/tests/test_enum_values.py +177 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_odcs_v3.py +10 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_rdf.py +37 -30
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_athena.py +1 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_avro.py +53 -18
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_csv.py +1 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_excel.py +10 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_iceberg.py +53 -18
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_mysql.py +1 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_parquet.py +11 -2
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_postgres.py +1 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_powerbi.py +1 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_redshift.py +1 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_snowflake.py +1 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_spark_type_json.py +1 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_sql_oracle.py +2 -2
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_sql_postgres.py +2 -2
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_sql_snowflake.py +1 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_sql_sqlserver.py +1 -1
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_lint.py +10 -0
- datacontract_cli-1.2.0/tests/test_lint_odcs_3_2_constraints.py +94 -0
- datacontract_cli-1.2.0/tests/test_map_type.py +313 -0
- datacontract_cli-1.2.0/tests/test_metadata_fields.py +128 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_roundtrip_pydantic_model.py +4 -31
- datacontract_cli-1.2.0/tests/test_servers_3_2_0.py +143 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_snowflake_structured_types.py +11 -4
- datacontract_cli-1.2.0/tests/test_test_iceberg.py +347 -0
- datacontract_cli-1.2.0/tests/test_test_iceberg_rest.py +187 -0
- datacontract_cli-1.2.0/tests/test_test_iceberg_s3tables.py +164 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_parquet.py +1 -1
- datacontract_cli-1.2.0/tests/test_test_variables.py +159 -0
- datacontract_cli-1.2.0/tests/test_variables.py +157 -0
- datacontract_cli-1.2.0/tests/test_vector_type.py +338 -0
- datacontract_cli-1.1.3/datacontract/editor_assets/VERSION +0 -1
- datacontract_cli-1.1.3/datacontract/editor_assets/abap-Bs8kvjsq.js +0 -1398
- datacontract_cli-1.1.3/datacontract/editor_assets/apex-Dspwrl5m.js +0 -326
- datacontract_cli-1.1.3/datacontract/editor_assets/assets/editor.worker-4.7.0.js +0 -9037
- datacontract_cli-1.1.3/datacontract/editor_assets/assets/yaml.worker-4.7.0.js +0 -26455
- datacontract_cli-1.1.3/datacontract/editor_assets/azcli-k1BiOau1.js +0 -68
- datacontract_cli-1.1.3/datacontract/editor_assets/bat-BIP72KWc.js +0 -100
- datacontract_cli-1.1.3/datacontract/editor_assets/bicep-VhQeYhS3.js +0 -102
- datacontract_cli-1.1.3/datacontract/editor_assets/cameligo-DnuAXtHg.js +0 -174
- datacontract_cli-1.1.3/datacontract/editor_assets/clojure-BAuDPsal.js +0 -761
- datacontract_cli-1.1.3/datacontract/editor_assets/coffee-BV06qSnj.js +0 -232
- datacontract_cli-1.1.3/datacontract/editor_assets/cpp-Cz8Jeyrf.js +0 -389
- datacontract_cli-1.1.3/datacontract/editor_assets/csharp-F6hhlXFb.js +0 -326
- datacontract_cli-1.1.3/datacontract/editor_assets/csp-KzlEHCyI.js +0 -53
- datacontract_cli-1.1.3/datacontract/editor_assets/css-e4FKntZ3.js +0 -187
- datacontract_cli-1.1.3/datacontract/editor_assets/cssMode-BNkTa2xp.js +0 -1618
- datacontract_cli-1.1.3/datacontract/editor_assets/cypher-CpvDGKRr.js +0 -263
- datacontract_cli-1.1.3/datacontract/editor_assets/dart-DdHeFWR0.js +0 -281
- datacontract_cli-1.1.3/datacontract/editor_assets/datacontract-editor.css +0 -1
- datacontract_cli-1.1.3/datacontract/editor_assets/datacontract-editor.es.js +0 -78027
- datacontract_cli-1.1.3/datacontract/editor_assets/dockerfile-aoNXDcio.js +0 -130
- datacontract_cli-1.1.3/datacontract/editor_assets/ecl-C4Q0DYuA.js +0 -456
- datacontract_cli-1.1.3/datacontract/editor_assets/elixir-C9X42b0T.js +0 -569
- datacontract_cli-1.1.3/datacontract/editor_assets/flow9-Bff42pfB.js +0 -142
- datacontract_cli-1.1.3/datacontract/editor_assets/freemarker2-Dyfzmb5A.js +0 -989
- datacontract_cli-1.1.3/datacontract/editor_assets/fsharp-C6vPAsHG.js +0 -217
- datacontract_cli-1.1.3/datacontract/editor_assets/go-DEr8zx9f.js +0 -218
- datacontract_cli-1.1.3/datacontract/editor_assets/graphql-BiNBBRFh.js +0 -151
- datacontract_cli-1.1.3/datacontract/editor_assets/handlebars-BtDNI-uC.js +0 -419
- datacontract_cli-1.1.3/datacontract/editor_assets/hcl-2T9lKuFg.js +0 -183
- datacontract_cli-1.1.3/datacontract/editor_assets/html-Cn4TGw_o.js +0 -308
- datacontract_cli-1.1.3/datacontract/editor_assets/htmlMode-Bf84vbo2.js +0 -1628
- datacontract_cli-1.1.3/datacontract/editor_assets/index.html +0 -31
- datacontract_cli-1.1.3/datacontract/editor_assets/ini-CZGtssYM.js +0 -71
- datacontract_cli-1.1.3/datacontract/editor_assets/java-B260NeH3.js +0 -232
- datacontract_cli-1.1.3/datacontract/editor_assets/javascript-B86Kac07.js +0 -70
- datacontract_cli-1.1.3/datacontract/editor_assets/jsonMode-Ckgg5lAE.js +0 -2051
- datacontract_cli-1.1.3/datacontract/editor_assets/julia-kuNLM9c5.js +0 -511
- datacontract_cli-1.1.3/datacontract/editor_assets/kotlin-Bz6kAefA.js +0 -253
- datacontract_cli-1.1.3/datacontract/editor_assets/less-AZUtS89I.js +0 -162
- datacontract_cli-1.1.3/datacontract/editor_assets/lexon-BcmgnyRb.js +0 -157
- datacontract_cli-1.1.3/datacontract/editor_assets/liquid-D0NPwuu_.js +0 -240
- datacontract_cli-1.1.3/datacontract/editor_assets/lua-Dz1RbATg.js +0 -162
- datacontract_cli-1.1.3/datacontract/editor_assets/m3-CIMooLFn.js +0 -210
- datacontract_cli-1.1.3/datacontract/editor_assets/markdown-C1BsCK9P.js +0 -229
- datacontract_cli-1.1.3/datacontract/editor_assets/mdx-QFRSBtVQ.js +0 -165
- datacontract_cli-1.1.3/datacontract/editor_assets/mips-BovsnfLQ.js +0 -198
- datacontract_cli-1.1.3/datacontract/editor_assets/monaco-4.7.0.js +0 -125918
- datacontract_cli-1.1.3/datacontract/editor_assets/msdax-CiRB2ZB-.js +0 -375
- datacontract_cli-1.1.3/datacontract/editor_assets/mysql-BwdpPE19.js +0 -878
- datacontract_cli-1.1.3/datacontract/editor_assets/objective-c-D4nfHfso.js +0 -183
- datacontract_cli-1.1.3/datacontract/editor_assets/pascal--j5-PYtz.js +0 -251
- datacontract_cli-1.1.3/datacontract/editor_assets/pascaligo-C7TcJxNL.js +0 -164
- datacontract_cli-1.1.3/datacontract/editor_assets/perl-BzEWqUb5.js +0 -626
- datacontract_cli-1.1.3/datacontract/editor_assets/pgsql-B_KZ0mGX.js +0 -851
- datacontract_cli-1.1.3/datacontract/editor_assets/php-A19QHzQV.js +0 -500
- datacontract_cli-1.1.3/datacontract/editor_assets/pla-COeKY1hA.js +0 -137
- datacontract_cli-1.1.3/datacontract/editor_assets/postiats-BfMp7zJL.js +0 -907
- datacontract_cli-1.1.3/datacontract/editor_assets/powerquery-B0TS_5Gb.js +0 -890
- datacontract_cli-1.1.3/datacontract/editor_assets/powershell-5jSbKdYB.js +0 -239
- datacontract_cli-1.1.3/datacontract/editor_assets/protobuf-YgnOutn3.js +0 -420
- datacontract_cli-1.1.3/datacontract/editor_assets/pug-DLej6kn5.js +0 -402
- datacontract_cli-1.1.3/datacontract/editor_assets/python-C8RW9B6A.js +0 -301
- datacontract_cli-1.1.3/datacontract/editor_assets/qsharp-AhjqtSHB.js +0 -301
- datacontract_cli-1.1.3/datacontract/editor_assets/r-D-F4P80w.js +0 -243
- datacontract_cli-1.1.3/datacontract/editor_assets/razor-CLZqdY_r.js +0 -550
- datacontract_cli-1.1.3/datacontract/editor_assets/redis-hwwIlMnV.js +0 -302
- datacontract_cli-1.1.3/datacontract/editor_assets/redshift-Bcm_kjEG.js +0 -809
- datacontract_cli-1.1.3/datacontract/editor_assets/restructuredtext-CrdzfPP-.js +0 -174
- datacontract_cli-1.1.3/datacontract/editor_assets/ruby-QC2UNm1s.js +0 -511
- datacontract_cli-1.1.3/datacontract/editor_assets/rust-5aqiQ3L5.js +0 -343
- datacontract_cli-1.1.3/datacontract/editor_assets/sb-B1g1ZfLJ.js +0 -115
- datacontract_cli-1.1.3/datacontract/editor_assets/scala-C2U4R5Gb.js +0 -370
- datacontract_cli-1.1.3/datacontract/editor_assets/scheme-DeFRbslr.js +0 -108
- datacontract_cli-1.1.3/datacontract/editor_assets/scss-RSTCiCmI.js +0 -262
- datacontract_cli-1.1.3/datacontract/editor_assets/shell-BpF-iNu8.js +0 -221
- datacontract_cli-1.1.3/datacontract/editor_assets/solidity-C00r2mbQ.js +0 -1367
- datacontract_cli-1.1.3/datacontract/editor_assets/sophia-DVW5ZKDB.js +0 -199
- datacontract_cli-1.1.3/datacontract/editor_assets/sparql-B0nmpxeX.js +0 -201
- datacontract_cli-1.1.3/datacontract/editor_assets/sql-CJwKPERn.js +0 -853
- datacontract_cli-1.1.3/datacontract/editor_assets/st-DPlKG5DT.js +0 -416
- datacontract_cli-1.1.3/datacontract/editor_assets/swift-YZBAbVHV.js +0 -309
- datacontract_cli-1.1.3/datacontract/editor_assets/systemverilog-CDQSaH63.js +0 -576
- datacontract_cli-1.1.3/datacontract/editor_assets/tcl-Dlu8sp6Q.js +0 -232
- datacontract_cli-1.1.3/datacontract/editor_assets/tsMode-yleMwhPq.js +0 -933
- datacontract_cli-1.1.3/datacontract/editor_assets/twig-C5TO7-kw.js +0 -392
- datacontract_cli-1.1.3/datacontract/editor_assets/typescript-CJBeebKU.js +0 -343
- datacontract_cli-1.1.3/datacontract/editor_assets/typespec-BsmjDEu7.js +0 -117
- datacontract_cli-1.1.3/datacontract/editor_assets/vb-ChHvIDrk.js +0 -372
- datacontract_cli-1.1.3/datacontract/editor_assets/wgsl-XEp1ju8c.js +0 -439
- datacontract_cli-1.1.3/datacontract/editor_assets/xml-vC-NMVX1.js +0 -95
- datacontract_cli-1.1.3/datacontract/editor_assets/yaml-Dlf2VF-T.js +0 -206
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/LICENSE +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/MANIFEST.in +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/README.md +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/__init__.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/breaking/__init__.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/catalog/catalog.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/changelog/__init__.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/changelog/changelog.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/cli.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/command_api.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/command_breaking.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/command_catalog.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/command_changelog.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/command_ci.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/command_dbt.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/command_edit.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/command_export.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/command_init.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/command_lint.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/command_publish.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/command_test.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/config/__init__.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/config/cli_context.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/data_contract.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/editor_assets/logo_fuchsia_v2.svg +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/editor_assets/odcs.svg +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/__init__.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/checks/__init__.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/checks/check_filter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/checks/check_spec.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/checks/dimensions.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/checks/physical_type_match.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/checks/sql_guard.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/datacontract/check_azure_blob_file.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/datacontract/check_that_datacontract_contains_valid_servers_configuration.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/datacontract/check_that_datacontract_file_exists.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/fastjsonschema/s3/s3_read_files.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/ibis/__init__.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/__init__.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/aws_credentials.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/databricks_patch.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/oracle_patch.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/redshift_credentials.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/redshift_patch.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/engines/ibis/native_type.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/__init__.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/custom_exporter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/dbml_exporter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/dbt_exporter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/dqx_exporter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/duckdb_type_converter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/excel_exporter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/exporter_factory.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/html_exporter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/markdown_exporter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/mermaid_exporter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/odcs_v3_exporter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/pandas_type_converter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/sodacl_exporter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/export/sql_exporter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/athena_importer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/bigquery_importer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/csv_importer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/dbml_importer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/dbt_importer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/importer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/importer_factory.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/json_importer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/mysql_importer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/object_storage_importer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/odcs_importer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/oracle_importer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/powerbi_importer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/pydantic_importer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/redshift_importer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/sqlserver_importer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/imports/trino_importer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/integration/dbt_sync.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/integration/entropy_data.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/lint/files.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/lint/resources.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/lint/s3.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/lint/schema.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/lint/urls.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/model/breaking.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/model/changelog.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/model/exceptions.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/model/odcs.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/model/run.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/output/__init__.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/output/ci_output.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/output/json_test_results.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/output/junit_test_results.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/output/output_format.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/output/test_results_writer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/output/text_breaking_results.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/output/text_changelog_results.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/py.typed +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/schemas/datacontract-1.1.0.init.yaml +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/schemas/datacontract-1.1.0.schema.json +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/schemas/datacontract-1.2.0.init.yaml +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/schemas/datacontract-1.2.0.schema.json +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/schemas/datacontract-1.2.1.init.yaml +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/schemas/datacontract-1.2.1.schema.json +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/schemas/odcs-3.0.1.schema.json +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/schemas/odcs-3.0.2.schema.json +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/schemas/odcs-3.1.0.schema.json +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/datacontract.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/excel/odcs-template.xlsx +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/index.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/macros/authoritative_definitions.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/macros/support.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials/datacontract_information.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials/datacontract_servicelevels.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials/datacontract_terms.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials/definition.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials/example.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials/model_field.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials/quality.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials/server.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/diagram.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/dialog_yaml.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/footer.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/fundamentals.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/header.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/pricing.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/roles.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/servers.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/support.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/team.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/term_of_use.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/title.html +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract/templates/style/output.css +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract_cli.egg-info/dependency_links.txt +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract_cli.egg-info/entry_points.txt +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/datacontract_cli.egg-info/top_level.txt +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/setup.cfg +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_api.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_api_openapi.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_breaking.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_catalog.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_changelog.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_changelog_engine.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_changelog_normalize.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_changelog_output_text.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_check_quality_provenance.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_ci_output.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_cli.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_command_api.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_config.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_config_require.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_config_threading.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_connect_athena.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_connect_bigquery.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_connect_databricks.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_connect_impala.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_connect_mysql.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_connect_oracle.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_connect_postgres.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_connect_redshift.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_connect_s3_credentials.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_connect_snowflake.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_connect_sqlserver.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_connect_trino.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_create_checks_array_items.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_create_checks_physical_type.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_create_checks_primary_key.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_create_checks_retention_period.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_create_checks_servicelevel.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_create_checks_sql_placeholders.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_data_contract.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_data_contract_checks.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_data_contract_specification.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_dbt_sync.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_dbt_test_mapping.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_description_linter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_docs_commands.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_docs_config_options.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_docs_ordering.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_download_datacontract_file.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_duckdb_json.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_edit.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_avro.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_avro_idl.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_bigquery.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_complex_data_contract.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_custom.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_custom_exporter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_custom_model.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_data_caterer.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_dbml.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_dbt_models.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_dbt_sources.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_dbt_staging_sql.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_dcs.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_dqx.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_excel.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_go.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_great_expectations.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_html.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_iceberg.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_jsonschema.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_large_contract.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_markdown.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_mermaid.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_protobuf.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_pydantic.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_sodacl.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_spark.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_sql.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_sql_clickhouse.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_sql_query.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_export_sqlalchemy.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_ibis_mssql_pattern.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_ibis_mssql_schema.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_ibis_oracle_schema.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_bigquery.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_dbml.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_dbt.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_glue.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_json.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_jsonschema.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_object_storage.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_odcs_v3.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_oracle.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_protobuf.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_pydantic_model.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_sqlserver.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_trino.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_import_unity_file.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_install_extras.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_integration_entropydata.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_lint_urls.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_native_type_schema_scope.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_oracle_patch.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_physical_type_match.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_publish_test_results.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_publish_test_results_payload.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_quality_sql_dialects.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_redshift_credentials.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_redshift_patch.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_resolve.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_resolve_definitions.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_resolve_local_definitions.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_roundtrip_jsonschema.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_run_check_deprecated_fields.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_run_check_engine.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_server_overrides.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_server_type_mssql.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_server_type_synonyms.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_sql_type_converter_physicaltype.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_api.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_array_items.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_athena_iceberg.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_azure_blob_file.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_azure_remote.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_bigquery.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_checks_filter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_databricks.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_delta.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_diagnostics.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_dimension_filter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_dry_run.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_duckdb.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_failed_samples.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_gcs_csv_remote.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_gcs_json_remote.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_kafka.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_kafka_decoding.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_kafka_remote.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_local_json.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_local_json_nd.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_metadata_only.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_mysql.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_oracle.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_oracle_xe.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_output_json.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_output_junit.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_postgres.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_quality.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_quality_id_and_tag_filter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_quality_local_fallback.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_quality_percent_severity.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_redshift.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_row_filter.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_s3_csv.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_s3_delta.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_s3_json.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_s3_json_complex.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_s3_json_multiple_models.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_s3_json_remote.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_schema_evolution.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_server_not_found.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_snowflake.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_soda_connection_error.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_sqlserver.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_test_trino.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_type_normalize.py +0 -0
- {datacontract_cli-1.1.3 → datacontract_cli-1.2.0}/tests/test_untrusted_contract.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datacontract-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
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
|
|
@@ -40,13 +40,13 @@ Requires-Dist: jsonschema<5.0.0,>=4.23.0
|
|
|
40
40
|
Requires-Dist: pytz>=2024.1
|
|
41
41
|
Requires-Dist: python-multipart<1.0.0,>=0.0.20
|
|
42
42
|
Requires-Dist: rich<16.0,>=13.7
|
|
43
|
-
Requires-Dist: sqlglot<
|
|
43
|
+
Requires-Dist: sqlglot<30.18,>=26.6.0
|
|
44
44
|
Requires-Dist: python-dotenv<2.0.0,>=1.0.0
|
|
45
45
|
Requires-Dist: boto3<2.0.0,>=1.34.41
|
|
46
46
|
Requires-Dist: Jinja2<4.0.0,>=3.1.5
|
|
47
47
|
Requires-Dist: jinja_partials<1.0.0,>=0.2.1
|
|
48
48
|
Requires-Dist: datacontract-specification<2.0.0,>=1.2.3
|
|
49
|
-
Requires-Dist: open-data-contract-standard<3.
|
|
49
|
+
Requires-Dist: open-data-contract-standard<3.3.0,>=3.2.0
|
|
50
50
|
Requires-Dist: deepdiff<10.0.0,>=6.0.0
|
|
51
51
|
Provides-Extra: avro
|
|
52
52
|
Requires-Dist: avro==1.12.2; extra == "avro"
|
|
@@ -67,12 +67,13 @@ 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.136.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"
|
|
74
74
|
Provides-Extra: iceberg
|
|
75
|
-
Requires-Dist: pyiceberg==0.
|
|
75
|
+
Requires-Dist: pyiceberg[pyarrow,rest-sigv4]==0.12.0; extra == "iceberg"
|
|
76
|
+
Requires-Dist: datacontract-cli[duckdb]; extra == "iceberg"
|
|
76
77
|
Provides-Extra: kafka
|
|
77
78
|
Requires-Dist: datacontract-cli[duckdb,parquet]; extra == "kafka"
|
|
78
79
|
Requires-Dist: confluent-kafka<3.0.0,>=2.6.0; extra == "kafka"
|
|
@@ -145,10 +146,10 @@ Requires-Dist: pre-commit<4.7.0,>=3.7.1; extra == "dev"
|
|
|
145
146
|
Requires-Dist: pytest; extra == "dev"
|
|
146
147
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
147
148
|
Requires-Dist: pytest-xdist; extra == "dev"
|
|
148
|
-
Requires-Dist: pymssql==2.
|
|
149
|
+
Requires-Dist: pymssql==2.4.0; extra == "dev"
|
|
149
150
|
Requires-Dist: psycopg2-binary<3.0,>=2.9; extra == "dev"
|
|
150
151
|
Requires-Dist: mysql-connector-python<26.8.0,>=8.0.30; extra == "dev"
|
|
151
|
-
Requires-Dist: ruff==0.16.
|
|
152
|
+
Requires-Dist: ruff==0.16.6; extra == "dev"
|
|
152
153
|
Requires-Dist: testcontainers[kafka,minio,mssql,mysql,postgres]==4.15.0; extra == "dev"
|
|
153
154
|
Requires-Dist: trino==0.339.0; extra == "dev"
|
|
154
155
|
Dynamic: license-file
|
|
@@ -20,7 +20,7 @@ from datacontract.model.changelog import ChangelogEntry
|
|
|
20
20
|
from datacontract.model.exceptions import DataContractException, DefinitionResolutionError
|
|
21
21
|
from datacontract.model.run import Check, ResultEnum, Run
|
|
22
22
|
|
|
23
|
-
DATA_CONTRACT_EXAMPLE_PAYLOAD = """apiVersion: v3.
|
|
23
|
+
DATA_CONTRACT_EXAMPLE_PAYLOAD = """apiVersion: v3.2.0
|
|
24
24
|
kind: DataContract
|
|
25
25
|
id: orders
|
|
26
26
|
name: Orders
|
|
@@ -23,11 +23,38 @@ class BreakingChangeDetector:
|
|
|
23
23
|
def detect(self, changelog: ChangelogResult) -> BreakingChangeResult:
|
|
24
24
|
entries = [self._classify(entry) for entry in changelog.entries]
|
|
25
25
|
# Nothing inside a newly added element can break a consumer: it did not exist before.
|
|
26
|
-
added = {
|
|
26
|
+
added = {
|
|
27
|
+
entry.path
|
|
28
|
+
for entry in entries
|
|
29
|
+
if entry.change_type == ChangelogType.added
|
|
30
|
+
and (
|
|
31
|
+
entry.path.startswith("schema.")
|
|
32
|
+
and (len(entry.path.split(".")) == 2 or entry.path.split(".")[-2] == "properties")
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
removed_enums = {
|
|
36
|
+
entry.path
|
|
37
|
+
for entry in entries
|
|
38
|
+
if entry.change_type == ChangelogType.removed and entry.path.endswith(".enum")
|
|
39
|
+
}
|
|
40
|
+
# Stable IDs may change or swap without changing the set of allowed
|
|
41
|
+
# values. Match removed/updated values against additions in the same enum.
|
|
42
|
+
added_values = defaultdict(set)
|
|
43
|
+
for entry in entries:
|
|
44
|
+
if entry.rule_id == "enum-constraint-changed" and entry.path.endswith(".value"):
|
|
45
|
+
if entry.change_type in (ChangelogType.added, ChangelogType.updated):
|
|
46
|
+
added_values[entry.path.rsplit(".enum.", 1)[0]].add(entry.new_value)
|
|
27
47
|
for entry in entries:
|
|
28
48
|
segments = entry.path.split(".")
|
|
29
49
|
if any(".".join(segments[:i]) in added for i in range(1, len(segments))):
|
|
30
50
|
entry.level = BreakingChangeLevel.INFO
|
|
51
|
+
if entry.rule_id == "enum-constraint-changed":
|
|
52
|
+
if any(entry.path.startswith(path + ".") for path in removed_enums):
|
|
53
|
+
entry.level = BreakingChangeLevel.INFO
|
|
54
|
+
elif entry.path.endswith(".value") and entry.old_value in added_values.get(
|
|
55
|
+
entry.path.rsplit(".enum.", 1)[0], set()
|
|
56
|
+
):
|
|
57
|
+
entry.level = BreakingChangeLevel.INFO
|
|
31
58
|
entries_by_prefix = defaultdict(list)
|
|
32
59
|
for entry in entries:
|
|
33
60
|
prefix = ""
|
|
@@ -140,6 +140,43 @@ class MetadataFallbackRule(BreakingChangeRule):
|
|
|
140
140
|
return RuleEvaluation(self.rule_id, BreakingChangeLevel.INFO, _change_message("contract", entry))
|
|
141
141
|
|
|
142
142
|
|
|
143
|
+
class EnumConstraintRule(BreakingChangeRule):
|
|
144
|
+
rule_id = "enum-constraint-changed"
|
|
145
|
+
|
|
146
|
+
def evaluate(self, entry: ChangelogEntry) -> RuleEvaluation | None:
|
|
147
|
+
if not entry.path.startswith("schema.") or ".properties." not in entry.path:
|
|
148
|
+
return None
|
|
149
|
+
if entry.path.endswith(".enum"):
|
|
150
|
+
level = BreakingChangeLevel.ERROR if entry.type == ChangelogType.added else BreakingChangeLevel.INFO
|
|
151
|
+
elif (
|
|
152
|
+
".enum." in entry.path
|
|
153
|
+
and entry.path.endswith(".value")
|
|
154
|
+
and ".customProperties." not in entry.path.rsplit(".enum.", 1)[1]
|
|
155
|
+
and ".authoritativeDefinitions." not in entry.path.rsplit(".enum.", 1)[1]
|
|
156
|
+
):
|
|
157
|
+
level = BreakingChangeLevel.INFO if entry.type == ChangelogType.added else BreakingChangeLevel.ERROR
|
|
158
|
+
else:
|
|
159
|
+
return None
|
|
160
|
+
return RuleEvaluation(self.rule_id, level, _change_message("allowed values", entry))
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class VectorShapeRule(BreakingChangeRule):
|
|
164
|
+
rule_id = "vector-shape-changed"
|
|
165
|
+
|
|
166
|
+
def evaluate(self, entry: ChangelogEntry) -> RuleEvaluation | None:
|
|
167
|
+
if not entry.path.startswith("schema.") or not entry.path.endswith(
|
|
168
|
+
(".logicalTypeOptions.dimensions", ".logicalTypeOptions.elementType")
|
|
169
|
+
):
|
|
170
|
+
return None
|
|
171
|
+
if entry.path.endswith(".elementType"):
|
|
172
|
+
old = entry.old_value or "float32"
|
|
173
|
+
new = entry.new_value or "float32"
|
|
174
|
+
level = BreakingChangeLevel.INFO if old == new else BreakingChangeLevel.ERROR
|
|
175
|
+
else:
|
|
176
|
+
level = BreakingChangeLevel.WARNING if entry.type == ChangelogType.removed else BreakingChangeLevel.ERROR
|
|
177
|
+
return RuleEvaluation(self.rule_id, level, _change_message("vector shape", entry))
|
|
178
|
+
|
|
179
|
+
|
|
143
180
|
def _is_schema_property_path(segments: list[str]) -> bool:
|
|
144
181
|
return len(segments) >= 3 and segments[0] == "schema" and segments[-2] == "properties"
|
|
145
182
|
|
|
@@ -192,5 +229,7 @@ DEFAULT_RULES: tuple[BreakingChangeRule, ...] = (
|
|
|
192
229
|
UniqueConstraintRule(),
|
|
193
230
|
KeyConstraintRule(),
|
|
194
231
|
ValidationConstraintRule(),
|
|
232
|
+
EnumConstraintRule(),
|
|
233
|
+
VectorShapeRule(),
|
|
195
234
|
MetadataFallbackRule(),
|
|
196
235
|
)
|
|
@@ -44,6 +44,13 @@ Current hardcoded natural keys:
|
|
|
44
44
|
schema[] SchemaObject -> .name (required: [name])
|
|
45
45
|
schema[].properties[] SchemaProperty -> .name (required: [name], recursive)
|
|
46
46
|
slaProperties[] SLAProperty -> .property
|
|
47
|
+
slaProperties[].customProperties[] CustomProperty -> .property
|
|
48
|
+
slaProperties[].authoritativeDefinitions[] AuthoritativeDefinition -> .url
|
|
49
|
+
schema[].properties[].enum[] EnumValue -> .value (or .id)
|
|
50
|
+
schema[]/properties[].synonyms[] Synonym -> .synonym (or .id)
|
|
51
|
+
schema[]/properties[].relationships[] Relationship -> .id, else from:to
|
|
52
|
+
context.verifiedStatements[] VerifiedStatement -> .question (or .id)
|
|
53
|
+
context.constraints[] Constraint -> .constraint (or .id)
|
|
47
54
|
servers[] Server -> .server
|
|
48
55
|
servers[].roles[] Role -> .role
|
|
49
56
|
servers[].customProperties[] CustomProperty -> .property
|
|
@@ -82,15 +89,41 @@ def _normalize_auth_defs(items: list[dict]) -> dict:
|
|
|
82
89
|
return result
|
|
83
90
|
|
|
84
91
|
|
|
92
|
+
def _normalize_keyed(items: list[dict], *keys: str) -> dict:
|
|
93
|
+
"""Key a list of dicts by the first of ``keys`` an item carries, keeping the key in the value.
|
|
94
|
+
|
|
95
|
+
Falls back to the list index if none of the key fields is present.
|
|
96
|
+
"""
|
|
97
|
+
result = {}
|
|
98
|
+
for i, item in enumerate(items):
|
|
99
|
+
key = next((str(item[k]) for k in keys if item.get(k) is not None), None) or f"__pos_{i}__"
|
|
100
|
+
result[key] = item
|
|
101
|
+
return result
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _normalize_context(context) -> object:
|
|
105
|
+
"""Key the lists of a context block; a plain-string context is returned unchanged."""
|
|
106
|
+
if not isinstance(context, dict):
|
|
107
|
+
return context
|
|
108
|
+
entry = dict(context)
|
|
109
|
+
if isinstance(entry.get("verifiedStatements"), list):
|
|
110
|
+
entry["verifiedStatements"] = _normalize_keyed(entry["verifiedStatements"], "id", "question")
|
|
111
|
+
if isinstance(entry.get("constraints"), list):
|
|
112
|
+
entry["constraints"] = _normalize_keyed(entry["constraints"], "id", "constraint")
|
|
113
|
+
return entry
|
|
114
|
+
|
|
115
|
+
|
|
85
116
|
def _normalize_relationships(items: list[dict], schema_level: bool = True) -> dict:
|
|
86
|
-
"""Key relationships by a stable composite key.
|
|
117
|
+
"""Key relationships by their id (ODCS v3.2.0), else by a stable composite key.
|
|
87
118
|
|
|
88
119
|
Schema-level: from:to composite. Property-level: to only.
|
|
89
120
|
Falls back to positional index if key fields are absent.
|
|
90
121
|
"""
|
|
91
122
|
result = {}
|
|
92
123
|
for i, item in enumerate(items):
|
|
93
|
-
if
|
|
124
|
+
if item.get("id"):
|
|
125
|
+
key = str(item["id"])
|
|
126
|
+
elif schema_level:
|
|
94
127
|
from_val = str(item.get("from", ""))
|
|
95
128
|
to_val = str(item.get("to", ""))
|
|
96
129
|
key = f"{from_val}:{to_val}" if (from_val or to_val) else f"__pos_{i}__"
|
|
@@ -125,6 +158,12 @@ def _normalize_schema_fields(entry: dict, *, schema_level: bool) -> dict:
|
|
|
125
158
|
entry["authoritativeDefinitions"] = _normalize_auth_defs(entry["authoritativeDefinitions"])
|
|
126
159
|
if "relationships" in entry and isinstance(entry["relationships"], list):
|
|
127
160
|
entry["relationships"] = _normalize_relationships(entry["relationships"], schema_level=schema_level)
|
|
161
|
+
if "synonyms" in entry and isinstance(entry["synonyms"], list):
|
|
162
|
+
entry["synonyms"] = _normalize_keyed(entry["synonyms"], "id", "synonym")
|
|
163
|
+
if "enum" in entry and isinstance(entry["enum"], list):
|
|
164
|
+
entry["enum"] = _normalize_keyed(entry["enum"], "id", "value")
|
|
165
|
+
if "context" in entry:
|
|
166
|
+
entry["context"] = _normalize_context(entry["context"])
|
|
128
167
|
return entry
|
|
129
168
|
|
|
130
169
|
|
|
@@ -134,13 +173,30 @@ def _normalize_properties(properties: list[dict]) -> dict:
|
|
|
134
173
|
for prop in properties:
|
|
135
174
|
key = prop.get("name", prop.get("id", str(prop)))
|
|
136
175
|
entry = {k: v for k, v in prop.items() if k != "name"}
|
|
137
|
-
|
|
138
|
-
entry["properties"] = _normalize_properties(entry["properties"])
|
|
176
|
+
entry = _normalize_property_children(entry)
|
|
139
177
|
entry = _normalize_schema_fields(entry, schema_level=False)
|
|
140
178
|
result[key] = entry
|
|
141
179
|
return result
|
|
142
180
|
|
|
143
181
|
|
|
182
|
+
def _normalize_property_children(entry: dict) -> dict:
|
|
183
|
+
"""Recurse into the nested property definitions of a property: properties, items, map key and value."""
|
|
184
|
+
if "properties" in entry and isinstance(entry["properties"], list):
|
|
185
|
+
entry["properties"] = _normalize_properties(entry["properties"])
|
|
186
|
+
if isinstance(entry.get("items"), dict):
|
|
187
|
+
entry["items"] = _normalize_schema_fields(
|
|
188
|
+
_normalize_property_children(dict(entry["items"])), schema_level=False
|
|
189
|
+
)
|
|
190
|
+
if isinstance(entry.get("map"), dict):
|
|
191
|
+
entry["map"] = {
|
|
192
|
+
side: _normalize_schema_fields(_normalize_property_children(dict(definition)), schema_level=False)
|
|
193
|
+
if isinstance(definition, dict)
|
|
194
|
+
else definition
|
|
195
|
+
for side, definition in entry["map"].items()
|
|
196
|
+
}
|
|
197
|
+
return entry
|
|
198
|
+
|
|
199
|
+
|
|
144
200
|
def normalize(contract: dict) -> dict:
|
|
145
201
|
"""Convert named lists to dicts keyed by their natural key field.
|
|
146
202
|
|
|
@@ -161,7 +217,16 @@ def normalize(contract: dict) -> dict:
|
|
|
161
217
|
out["schema"] = normalized_schema
|
|
162
218
|
|
|
163
219
|
if "slaProperties" in out and isinstance(out["slaProperties"], list):
|
|
164
|
-
|
|
220
|
+
sla_properties = _normalize_by(out["slaProperties"], "property")
|
|
221
|
+
for entry in sla_properties.values():
|
|
222
|
+
if isinstance(entry.get("customProperties"), list):
|
|
223
|
+
entry["customProperties"] = _normalize_by(entry["customProperties"], "property")
|
|
224
|
+
if isinstance(entry.get("authoritativeDefinitions"), list):
|
|
225
|
+
entry["authoritativeDefinitions"] = _normalize_auth_defs(entry["authoritativeDefinitions"])
|
|
226
|
+
out["slaProperties"] = sla_properties
|
|
227
|
+
|
|
228
|
+
if "context" in out:
|
|
229
|
+
out["context"] = _normalize_context(out["context"])
|
|
165
230
|
|
|
166
231
|
if "servers" in out and isinstance(out["servers"], list):
|
|
167
232
|
normalized_servers = {}
|
|
@@ -461,13 +461,29 @@ def import_spark(
|
|
|
461
461
|
|
|
462
462
|
@import_app.command(
|
|
463
463
|
name="iceberg",
|
|
464
|
-
epilog="
|
|
464
|
+
epilog="Examples: datacontract import iceberg --source schema.json --table orders --output datacontract.yaml; "
|
|
465
|
+
"datacontract import iceberg --catalog-url https://polaris.example.com/api/catalog --namespace sales --table orders",
|
|
465
466
|
)
|
|
466
467
|
def import_iceberg(
|
|
467
|
-
source: Annotated[
|
|
468
|
+
source: Annotated[
|
|
469
|
+
Optional[str], typer.Option(help="Path to the Iceberg schema JSON file. Omit to read from a REST catalog.")
|
|
470
|
+
] = None,
|
|
468
471
|
table: Annotated[
|
|
469
472
|
Optional[str],
|
|
470
|
-
typer.Option(
|
|
473
|
+
typer.Option(
|
|
474
|
+
help="Table name to assign to the model created from the Iceberg schema, or the table to load from the catalog."
|
|
475
|
+
),
|
|
476
|
+
] = None,
|
|
477
|
+
catalog_url: Annotated[
|
|
478
|
+
Optional[str],
|
|
479
|
+
typer.Option(help="REST catalog endpoint to load the table from (DATACONTRACT_ICEBERG_CATALOG_URL)."),
|
|
480
|
+
] = None,
|
|
481
|
+
catalog: Annotated[Optional[str], typer.Option(help="Name of the catalog (DATACONTRACT_ICEBERG_CATALOG).")] = None,
|
|
482
|
+
namespace: Annotated[
|
|
483
|
+
Optional[str], typer.Option(help="Namespace of the table in the catalog (DATACONTRACT_ICEBERG_NAMESPACE).")
|
|
484
|
+
] = None,
|
|
485
|
+
warehouse: Annotated[
|
|
486
|
+
Optional[str], typer.Option(help="Warehouse passed to the catalog (DATACONTRACT_ICEBERG_WAREHOUSE).")
|
|
471
487
|
] = None,
|
|
472
488
|
output: output_option = None,
|
|
473
489
|
schema: schema_option = None,
|
|
@@ -475,10 +491,20 @@ def import_iceberg(
|
|
|
475
491
|
id: id_option = None,
|
|
476
492
|
debug: debug_option = None,
|
|
477
493
|
):
|
|
478
|
-
"""Import a data contract from an Iceberg schema."""
|
|
494
|
+
"""Import a data contract from an Iceberg schema file or a REST catalog."""
|
|
479
495
|
enable_debug_logging(debug)
|
|
480
496
|
result = DataContract.import_from_source(
|
|
481
|
-
config=cli_config(),
|
|
497
|
+
config=cli_config(),
|
|
498
|
+
format="iceberg",
|
|
499
|
+
source=source,
|
|
500
|
+
schema=schema,
|
|
501
|
+
iceberg_table=table,
|
|
502
|
+
iceberg_catalog_url=catalog_url,
|
|
503
|
+
iceberg_catalog=catalog,
|
|
504
|
+
iceberg_namespace=namespace,
|
|
505
|
+
iceberg_warehouse=warehouse,
|
|
506
|
+
owner=owner,
|
|
507
|
+
id=id,
|
|
482
508
|
)
|
|
483
509
|
_write_result(result, output)
|
|
484
510
|
|
|
@@ -4,7 +4,6 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
6
|
import os
|
|
7
|
-
import re
|
|
8
7
|
from pathlib import Path
|
|
9
8
|
|
|
10
9
|
from pydantic import Field, SecretStr
|
|
@@ -33,6 +32,7 @@ SERVER_OVERRIDE_OPTIONS = {
|
|
|
33
32
|
"athena_catalog": "catalog",
|
|
34
33
|
"athena_schema": "schema",
|
|
35
34
|
"athena_staging_dir": "stagingDir",
|
|
35
|
+
"athena_workgroup": "workgroup",
|
|
36
36
|
"bigquery_project": "project",
|
|
37
37
|
"bigquery_dataset": "dataset",
|
|
38
38
|
"databricks_server_hostname": "host",
|
|
@@ -40,6 +40,10 @@ SERVER_OVERRIDE_OPTIONS = {
|
|
|
40
40
|
"databricks_schema": "schema",
|
|
41
41
|
"duckdb_database": "database",
|
|
42
42
|
"duckdb_schema": "schema",
|
|
43
|
+
"iceberg_catalog_url": "catalogUrl",
|
|
44
|
+
"iceberg_catalog": "catalog",
|
|
45
|
+
"iceberg_namespace": "namespace",
|
|
46
|
+
"iceberg_warehouse": "warehouse",
|
|
43
47
|
"impala_host": "host",
|
|
44
48
|
"impala_port": "port",
|
|
45
49
|
"impala_database": "database",
|
|
@@ -104,6 +108,7 @@ class Config(BaseSettings):
|
|
|
104
108
|
athena_catalog: str | None = None
|
|
105
109
|
athena_schema: str | None = None
|
|
106
110
|
athena_staging_dir: str | None = None
|
|
111
|
+
athena_workgroup: str | None = None
|
|
107
112
|
|
|
108
113
|
# azure
|
|
109
114
|
azure_connection_string: SecretStr | None = None
|
|
@@ -141,6 +146,19 @@ class Config(BaseSettings):
|
|
|
141
146
|
gcs_key_id: str | None = None
|
|
142
147
|
gcs_secret: SecretStr | None = None
|
|
143
148
|
|
|
149
|
+
# iceberg (REST catalog by default; data files use the s3_* options)
|
|
150
|
+
iceberg_catalog_type: str | None = None
|
|
151
|
+
iceberg_credential: SecretStr | None = None
|
|
152
|
+
iceberg_token: SecretStr | None = None
|
|
153
|
+
iceberg_s3_endpoint: str | None = None
|
|
154
|
+
iceberg_signing_name: str | None = None
|
|
155
|
+
iceberg_properties: str | None = None
|
|
156
|
+
# overrides for the contract's servers block
|
|
157
|
+
iceberg_catalog_url: str | None = None
|
|
158
|
+
iceberg_catalog: str | None = None
|
|
159
|
+
iceberg_namespace: str | None = None
|
|
160
|
+
iceberg_warehouse: str | None = None
|
|
161
|
+
|
|
144
162
|
# impala
|
|
145
163
|
impala_username: str | None = None
|
|
146
164
|
impala_password: SecretStr | None = None
|
|
@@ -309,9 +327,10 @@ class Config(BaseSettings):
|
|
|
309
327
|
|
|
310
328
|
Nested keys join with ``_`` to form the field name (``snowflake.username``
|
|
311
329
|
→ ``snowflake_username``); top-level scalars address the general options
|
|
312
|
-
(``max_errors``). ``${VAR}`` references in string
|
|
313
|
-
the environment variable's value at load time,
|
|
314
|
-
without holding secrets. Unknown option names
|
|
330
|
+
(``max_errors``). ``${VAR}`` and ``${VAR:-default}`` references in string
|
|
331
|
+
values are replaced with the environment variable's value at load time,
|
|
332
|
+
so files can be committed without holding secrets. Unknown option names
|
|
333
|
+
raise a ValueError.
|
|
315
334
|
"""
|
|
316
335
|
import yaml
|
|
317
336
|
|
|
@@ -469,6 +488,9 @@ class Config(BaseSettings):
|
|
|
469
488
|
def get_athena_staging_dir(self, required: bool = False) -> str | None:
|
|
470
489
|
return self._str_option("athena_staging_dir", required)
|
|
471
490
|
|
|
491
|
+
def get_athena_workgroup(self, required: bool = False) -> str | None:
|
|
492
|
+
return self._str_option("athena_workgroup", required)
|
|
493
|
+
|
|
472
494
|
# --- azure ---
|
|
473
495
|
def get_azure_connection_string(self, required: bool = False) -> str | None:
|
|
474
496
|
return self._str_option("azure_connection_string", required)
|
|
@@ -536,6 +558,37 @@ class Config(BaseSettings):
|
|
|
536
558
|
def get_gcs_secret(self, required: bool = False) -> str | None:
|
|
537
559
|
return self._str_option("gcs_secret", required)
|
|
538
560
|
|
|
561
|
+
# --- iceberg ---
|
|
562
|
+
def get_iceberg_catalog_type(self, required: bool = False) -> str | None:
|
|
563
|
+
return self._str_option("iceberg_catalog_type", required)
|
|
564
|
+
|
|
565
|
+
def get_iceberg_s3_endpoint(self, required: bool = False) -> str | None:
|
|
566
|
+
return self._str_option("iceberg_s3_endpoint", required)
|
|
567
|
+
|
|
568
|
+
def get_iceberg_signing_name(self, required: bool = False) -> str | None:
|
|
569
|
+
return self._str_option("iceberg_signing_name", required)
|
|
570
|
+
|
|
571
|
+
def get_iceberg_properties(self, required: bool = False) -> str | None:
|
|
572
|
+
return self._str_option("iceberg_properties", required)
|
|
573
|
+
|
|
574
|
+
def get_iceberg_credential(self, required: bool = False) -> str | None:
|
|
575
|
+
return self._str_option("iceberg_credential", required)
|
|
576
|
+
|
|
577
|
+
def get_iceberg_token(self, required: bool = False) -> str | None:
|
|
578
|
+
return self._str_option("iceberg_token", required)
|
|
579
|
+
|
|
580
|
+
def get_iceberg_catalog_url(self, required: bool = False) -> str | None:
|
|
581
|
+
return self._str_option("iceberg_catalog_url", required)
|
|
582
|
+
|
|
583
|
+
def get_iceberg_catalog(self, required: bool = False) -> str | None:
|
|
584
|
+
return self._str_option("iceberg_catalog", required)
|
|
585
|
+
|
|
586
|
+
def get_iceberg_namespace(self, required: bool = False) -> str | None:
|
|
587
|
+
return self._str_option("iceberg_namespace", required)
|
|
588
|
+
|
|
589
|
+
def get_iceberg_warehouse(self, required: bool = False) -> str | None:
|
|
590
|
+
return self._str_option("iceberg_warehouse", required)
|
|
591
|
+
|
|
539
592
|
# --- impala ---
|
|
540
593
|
def get_impala_username(self, required: bool = False) -> str | None:
|
|
541
594
|
return self._str_option("impala_username", required)
|
|
@@ -909,18 +962,8 @@ def unknown_snowflake_env_names() -> list[str]:
|
|
|
909
962
|
return sorted(name for name in os.environ if name.startswith("DATACONTRACT_SNOWFLAKE_") and name not in known)
|
|
910
963
|
|
|
911
964
|
|
|
912
|
-
_ENV_REFERENCE = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}")
|
|
913
|
-
|
|
914
|
-
|
|
915
965
|
def _interpolate_env(value, path):
|
|
916
|
-
"""Replace ``${VAR}`` references in string values with environment variable values."""
|
|
917
|
-
|
|
918
|
-
return value
|
|
919
|
-
|
|
920
|
-
def replace(match):
|
|
921
|
-
name = match.group(1)
|
|
922
|
-
if name not in os.environ:
|
|
923
|
-
raise ValueError(f"Environment variable {name} referenced in {path} is not set.")
|
|
924
|
-
return os.environ[name]
|
|
966
|
+
"""Replace ``${VAR}`` and ``${VAR:-default}`` references in string values with environment variable values."""
|
|
967
|
+
from datacontract.config.variables import resolve_variables
|
|
925
968
|
|
|
926
|
-
return
|
|
969
|
+
return resolve_variables(value, source=f"config file {path}")
|
|
@@ -0,0 +1,133 @@
|
|
|
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
|
+
"""
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import re
|
|
16
|
+
|
|
17
|
+
from open_data_contract_standard.model import Server
|
|
18
|
+
from pydantic import BaseModel
|
|
19
|
+
|
|
20
|
+
_VARIABLE = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class UnresolvedVariableError(ValueError):
|
|
24
|
+
"""A ``${VAR}`` reference names a variable that is unset and has no default."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, name: str, source: str = ""):
|
|
27
|
+
self.name = name
|
|
28
|
+
where = f" in {source}" if source else ""
|
|
29
|
+
super().__init__(f"Variable {name} referenced{where} is not set.")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def contains_variables(value) -> bool:
|
|
33
|
+
"""True if ``value`` is a string holding at least one ``${VAR}`` reference."""
|
|
34
|
+
return isinstance(value, str) and _VARIABLE.search(value) is not None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def resolve_variables(value, source: str = ""):
|
|
38
|
+
"""Return ``value`` with every ``${VAR}`` reference replaced.
|
|
39
|
+
|
|
40
|
+
Non-string values pass through unchanged. ``source`` names the value in
|
|
41
|
+
the error message (``"server 'prod' host"``, ``"config file x.yaml"``).
|
|
42
|
+
"""
|
|
43
|
+
if not isinstance(value, str):
|
|
44
|
+
return value
|
|
45
|
+
|
|
46
|
+
def replace(match: re.Match) -> str:
|
|
47
|
+
name, default = match.group(1), match.group(2)
|
|
48
|
+
env_value = os.environ.get(name)
|
|
49
|
+
if env_value:
|
|
50
|
+
return env_value
|
|
51
|
+
if default is not None:
|
|
52
|
+
return default
|
|
53
|
+
raise UnresolvedVariableError(name, source)
|
|
54
|
+
|
|
55
|
+
return _VARIABLE.sub(replace, value)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def resolve_server_variables(server: Server) -> Server:
|
|
59
|
+
"""Return a copy of ``server`` with the references in its string fields resolved.
|
|
60
|
+
|
|
61
|
+
``port`` may hold a reference (the schema allows a string there for that
|
|
62
|
+
reason); a resolved all-digit port becomes an int again. Custom property
|
|
63
|
+
values are resolved too. The contract's server is not mutated.
|
|
64
|
+
"""
|
|
65
|
+
updates = {}
|
|
66
|
+
for field in Server.model_fields:
|
|
67
|
+
value = getattr(server, field)
|
|
68
|
+
if contains_variables(value):
|
|
69
|
+
updates[field] = resolve_variables(value, source=f"server '{server.server}' {_field_name(field)}")
|
|
70
|
+
port = updates.get("port", server.port)
|
|
71
|
+
if isinstance(port, str) and port.isdigit():
|
|
72
|
+
updates["port"] = int(port)
|
|
73
|
+
if server.customProperties:
|
|
74
|
+
custom_properties = []
|
|
75
|
+
changed = False
|
|
76
|
+
for prop in server.customProperties:
|
|
77
|
+
if contains_variables(prop.value):
|
|
78
|
+
changed = True
|
|
79
|
+
prop = prop.model_copy(
|
|
80
|
+
update={
|
|
81
|
+
"value": resolve_variables(
|
|
82
|
+
prop.value, source=f"server '{server.server}' custom property '{prop.property}'"
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
custom_properties.append(prop)
|
|
87
|
+
if changed:
|
|
88
|
+
updates["customProperties"] = custom_properties
|
|
89
|
+
return server.model_copy(update=updates) if updates else server
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _field_name(field: str) -> str:
|
|
93
|
+
return "schema" if field == "schema_" else field
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# These fields document the contract rather than supply test inputs. SQL queries
|
|
97
|
+
# resolve separately, after the engine substitutes its ${model}/${field} tokens;
|
|
98
|
+
# servers resolve separately, after configuration overrides and server selection.
|
|
99
|
+
_DEFERRED_FIELDS = {
|
|
100
|
+
"authoritativeDefinitions",
|
|
101
|
+
"businessName",
|
|
102
|
+
"context",
|
|
103
|
+
"description",
|
|
104
|
+
"examples",
|
|
105
|
+
"implementation",
|
|
106
|
+
"query",
|
|
107
|
+
"servers",
|
|
108
|
+
"synonyms",
|
|
109
|
+
"transformLogic",
|
|
110
|
+
"transformSourceObjects",
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def resolve_runtime_variables(value, source: str = "contract"):
|
|
115
|
+
"""Copy test inputs with variables resolved, recursively, without altering the contract.
|
|
116
|
+
|
|
117
|
+
Handles property names/types, enum values, logical type options, library
|
|
118
|
+
quality arguments, SLA values, and nested array/map definitions. Dictionary
|
|
119
|
+
keys are structural identifiers and are never interpolated.
|
|
120
|
+
"""
|
|
121
|
+
if isinstance(value, BaseModel):
|
|
122
|
+
return value.model_copy(
|
|
123
|
+
update={
|
|
124
|
+
field: resolve_runtime_variables(getattr(value, field), f"{source}.{_field_name(field)}")
|
|
125
|
+
for field in type(value).model_fields
|
|
126
|
+
if field not in _DEFERRED_FIELDS
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
if isinstance(value, dict):
|
|
130
|
+
return {key: resolve_runtime_variables(item, f"{source}.{key}") for key, item in value.items()}
|
|
131
|
+
if isinstance(value, list):
|
|
132
|
+
return [resolve_runtime_variables(item, f"{source}[{index}]") for index, item in enumerate(value)]
|
|
133
|
+
return resolve_variables(value, source=source)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.13
|