datacontract-cli 1.1.2__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.2/datacontract_cli.egg-info → datacontract_cli-1.2.0}/PKG-INFO +12 -8
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/README.md +3 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/api.py +73 -1
- datacontract_cli-1.2.0/datacontract/breaking/detector.py +101 -0
- datacontract_cli-1.2.0/datacontract/breaking/rules.py +235 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/changelog/normalize.py +70 -5
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/cli.py +13 -3
- datacontract_cli-1.2.0/datacontract/command_breaking.py +39 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/command_ci.py +1 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/command_dbt.py +2 -2
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/command_export.py +7 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/command_import.py +31 -5
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/command_test.py +9 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/config/settings.py +64 -17
- datacontract_cli-1.2.0/datacontract/config/variables.py +133 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/data_contract.py +16 -3
- 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.2 → datacontract_cli-1.2.0}/datacontract/engines/checks/check_spec.py +6 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/checks/create_checks.py +89 -30
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/checks/dimensions.py +3 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/checks/type_normalize.py +76 -6
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/data_contract_test.py +140 -12
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/fastjsonschema/check_jsonschema.py +17 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/connect.py +25 -5
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/duckdb_connection.py +51 -12
- datacontract_cli-1.2.0/datacontract/engines/ibis/connections/iceberg.py +216 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/kafka.py +17 -4
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/ibis/dtype_category.py +17 -15
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/ibis/ibis_check_execute.py +52 -3
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/ibis/snowflake_structured_types.py +9 -4
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/avro_exporter.py +37 -51
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/avro_idl_exporter.py +36 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/bigquery_exporter.py +6 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/data_caterer_exporter.py +2 -28
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/dcs_exporter.py +22 -2
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/duckdb_type_converter.py +1 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/exporter.py +6 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/go_exporter.py +15 -0
- datacontract_cli-1.2.0/datacontract/export/great_expectations_exporter.py +716 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/iceberg_exporter.py +14 -30
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/jsonschema_exporter.py +23 -29
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/protobuf_exporter.py +29 -4
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/pydantic_exporter.py +30 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/rdf_exporter.py +44 -9
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/sodacl_check_builder.py +5 -4
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/spark_exporter.py +14 -20
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/sql_type_converter.py +86 -16
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/sqlalchemy_exporter.py +5 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/avro_importer.py +9 -7
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/dcs_importer.py +17 -26
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/excel_importer.py +23 -5
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/glue_importer.py +5 -2
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/iceberg_importer.py +71 -22
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/jsonschema_importer.py +2 -12
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/odcs_helper.py +83 -4
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/parquet_importer.py +17 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/postgres_importer.py +4 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/protobuf_importer.py +2 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/snowflake_importer.py +4 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/spark_importer.py +7 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/spark_type_json.py +12 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/sql_importer.py +111 -24
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/unity_importer.py +17 -9
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/init/init_template.py +1 -1
- {datacontract_cli-1.1.2 → 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.2 → datacontract_cli-1.2.0}/datacontract/lint/resolve.py +286 -24
- datacontract_cli-1.2.0/datacontract/model/breaking.py +33 -0
- 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.2 → datacontract_cli-1.2.0}/datacontract/model/run.py +15 -0
- {datacontract_cli-1.1.2 → 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.2 → datacontract_cli-1.2.0}/datacontract/output/test_results_writer.py +5 -0
- datacontract_cli-1.2.0/datacontract/output/text_breaking_results.py +81 -0
- datacontract_cli-1.1.2/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.2 → 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.2 → datacontract_cli-1.2.0}/datacontract/templates/macros/schema.html +41 -0
- {datacontract_cli-1.1.2 → 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.2 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/custom_properties.html +3 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/schema.html +17 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/sla.html +30 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0/datacontract_cli.egg-info}/PKG-INFO +12 -8
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract_cli.egg-info/SOURCES.txt +38 -87
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract_cli.egg-info/requires.txt +8 -7
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/pyproject.toml +10 -8
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_api.py +22 -0
- datacontract_cli-1.2.0/tests/test_breaking.py +337 -0
- datacontract_cli-1.2.0/tests/test_breaking_odcs_3_2.py +94 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_cli.py +30 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_config_file.py +7 -0
- datacontract_cli-1.2.0/tests/test_create_checks_array_items.py +71 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_create_checks_nested_type.py +21 -9
- datacontract_cli-1.2.0/tests/test_create_checks_retention_period.py +57 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_create_checks_servicelevel.py +25 -0
- datacontract_cli-1.2.0/tests/test_description_linter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_dtype_category.py +13 -5
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_duckdb_json.py +48 -0
- datacontract_cli-1.2.0/tests/test_enum_values.py +177 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_avro.py +17 -0
- datacontract_cli-1.2.0/tests/test_export_great_expectations.py +1508 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_jsonschema.py +19 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_odcs_v3.py +10 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_rdf.py +37 -30
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_sodacl.py +41 -3
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_athena.py +1 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_avro.py +53 -18
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_csv.py +1 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_excel.py +10 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_iceberg.py +53 -18
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_mysql.py +1 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_parquet.py +11 -2
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_postgres.py +1 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_powerbi.py +1 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_redshift.py +1 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_snowflake.py +1 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_spark_type_json.py +1 -1
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_sql_oracle.py +3 -3
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_sql_postgres.py +26 -3
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_sql_snowflake.py +43 -2
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_sql_sqlserver.py +2 -2
- {datacontract_cli-1.1.2 → 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.2 → datacontract_cli-1.2.0}/tests/test_resolve.py +37 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_resolve_definitions.py +45 -5
- datacontract_cli-1.2.0/tests/test_resolve_local_definitions.py +615 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_roundtrip_pydantic_model.py +2 -1
- datacontract_cli-1.2.0/tests/test_servers_3_2_0.py +143 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_snowflake_structured_types.py +11 -4
- datacontract_cli-1.2.0/tests/test_test_array_items.py +109 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_dimension_filter.py +3 -0
- datacontract_cli-1.2.0/tests/test_test_dry_run.py +175 -0
- 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.2 → datacontract_cli-1.2.0}/tests/test_test_kafka_decoding.py +20 -0
- {datacontract_cli-1.1.2 → 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.2/datacontract/editor_assets/VERSION +0 -1
- datacontract_cli-1.1.2/datacontract/editor_assets/abap-Bs8kvjsq.js +0 -1398
- datacontract_cli-1.1.2/datacontract/editor_assets/apex-Dspwrl5m.js +0 -326
- datacontract_cli-1.1.2/datacontract/editor_assets/assets/editor.worker-4.7.0.js +0 -9037
- datacontract_cli-1.1.2/datacontract/editor_assets/assets/yaml.worker-4.7.0.js +0 -26455
- datacontract_cli-1.1.2/datacontract/editor_assets/azcli-k1BiOau1.js +0 -68
- datacontract_cli-1.1.2/datacontract/editor_assets/bat-BIP72KWc.js +0 -100
- datacontract_cli-1.1.2/datacontract/editor_assets/bicep-VhQeYhS3.js +0 -102
- datacontract_cli-1.1.2/datacontract/editor_assets/cameligo-DnuAXtHg.js +0 -174
- datacontract_cli-1.1.2/datacontract/editor_assets/clojure-BAuDPsal.js +0 -761
- datacontract_cli-1.1.2/datacontract/editor_assets/coffee-BV06qSnj.js +0 -232
- datacontract_cli-1.1.2/datacontract/editor_assets/cpp-Cz8Jeyrf.js +0 -389
- datacontract_cli-1.1.2/datacontract/editor_assets/csharp-F6hhlXFb.js +0 -326
- datacontract_cli-1.1.2/datacontract/editor_assets/csp-KzlEHCyI.js +0 -53
- datacontract_cli-1.1.2/datacontract/editor_assets/css-e4FKntZ3.js +0 -187
- datacontract_cli-1.1.2/datacontract/editor_assets/cssMode-BNkTa2xp.js +0 -1618
- datacontract_cli-1.1.2/datacontract/editor_assets/cypher-CpvDGKRr.js +0 -263
- datacontract_cli-1.1.2/datacontract/editor_assets/dart-DdHeFWR0.js +0 -281
- datacontract_cli-1.1.2/datacontract/editor_assets/datacontract-editor.css +0 -1
- datacontract_cli-1.1.2/datacontract/editor_assets/datacontract-editor.es.js +0 -78027
- datacontract_cli-1.1.2/datacontract/editor_assets/dockerfile-aoNXDcio.js +0 -130
- datacontract_cli-1.1.2/datacontract/editor_assets/ecl-C4Q0DYuA.js +0 -456
- datacontract_cli-1.1.2/datacontract/editor_assets/elixir-C9X42b0T.js +0 -569
- datacontract_cli-1.1.2/datacontract/editor_assets/flow9-Bff42pfB.js +0 -142
- datacontract_cli-1.1.2/datacontract/editor_assets/freemarker2-Dyfzmb5A.js +0 -989
- datacontract_cli-1.1.2/datacontract/editor_assets/fsharp-C6vPAsHG.js +0 -217
- datacontract_cli-1.1.2/datacontract/editor_assets/go-DEr8zx9f.js +0 -218
- datacontract_cli-1.1.2/datacontract/editor_assets/graphql-BiNBBRFh.js +0 -151
- datacontract_cli-1.1.2/datacontract/editor_assets/handlebars-BtDNI-uC.js +0 -419
- datacontract_cli-1.1.2/datacontract/editor_assets/hcl-2T9lKuFg.js +0 -183
- datacontract_cli-1.1.2/datacontract/editor_assets/html-Cn4TGw_o.js +0 -308
- datacontract_cli-1.1.2/datacontract/editor_assets/htmlMode-Bf84vbo2.js +0 -1628
- datacontract_cli-1.1.2/datacontract/editor_assets/index.html +0 -31
- datacontract_cli-1.1.2/datacontract/editor_assets/ini-CZGtssYM.js +0 -71
- datacontract_cli-1.1.2/datacontract/editor_assets/java-B260NeH3.js +0 -232
- datacontract_cli-1.1.2/datacontract/editor_assets/javascript-B86Kac07.js +0 -70
- datacontract_cli-1.1.2/datacontract/editor_assets/jsonMode-Ckgg5lAE.js +0 -2051
- datacontract_cli-1.1.2/datacontract/editor_assets/julia-kuNLM9c5.js +0 -511
- datacontract_cli-1.1.2/datacontract/editor_assets/kotlin-Bz6kAefA.js +0 -253
- datacontract_cli-1.1.2/datacontract/editor_assets/less-AZUtS89I.js +0 -162
- datacontract_cli-1.1.2/datacontract/editor_assets/lexon-BcmgnyRb.js +0 -157
- datacontract_cli-1.1.2/datacontract/editor_assets/liquid-D0NPwuu_.js +0 -240
- datacontract_cli-1.1.2/datacontract/editor_assets/lua-Dz1RbATg.js +0 -162
- datacontract_cli-1.1.2/datacontract/editor_assets/m3-CIMooLFn.js +0 -210
- datacontract_cli-1.1.2/datacontract/editor_assets/markdown-C1BsCK9P.js +0 -229
- datacontract_cli-1.1.2/datacontract/editor_assets/mdx-QFRSBtVQ.js +0 -165
- datacontract_cli-1.1.2/datacontract/editor_assets/mips-BovsnfLQ.js +0 -198
- datacontract_cli-1.1.2/datacontract/editor_assets/monaco-4.7.0.js +0 -125918
- datacontract_cli-1.1.2/datacontract/editor_assets/msdax-CiRB2ZB-.js +0 -375
- datacontract_cli-1.1.2/datacontract/editor_assets/mysql-BwdpPE19.js +0 -878
- datacontract_cli-1.1.2/datacontract/editor_assets/objective-c-D4nfHfso.js +0 -183
- datacontract_cli-1.1.2/datacontract/editor_assets/pascal--j5-PYtz.js +0 -251
- datacontract_cli-1.1.2/datacontract/editor_assets/pascaligo-C7TcJxNL.js +0 -164
- datacontract_cli-1.1.2/datacontract/editor_assets/perl-BzEWqUb5.js +0 -626
- datacontract_cli-1.1.2/datacontract/editor_assets/pgsql-B_KZ0mGX.js +0 -851
- datacontract_cli-1.1.2/datacontract/editor_assets/php-A19QHzQV.js +0 -500
- datacontract_cli-1.1.2/datacontract/editor_assets/pla-COeKY1hA.js +0 -137
- datacontract_cli-1.1.2/datacontract/editor_assets/postiats-BfMp7zJL.js +0 -907
- datacontract_cli-1.1.2/datacontract/editor_assets/powerquery-B0TS_5Gb.js +0 -890
- datacontract_cli-1.1.2/datacontract/editor_assets/powershell-5jSbKdYB.js +0 -239
- datacontract_cli-1.1.2/datacontract/editor_assets/protobuf-YgnOutn3.js +0 -420
- datacontract_cli-1.1.2/datacontract/editor_assets/pug-DLej6kn5.js +0 -402
- datacontract_cli-1.1.2/datacontract/editor_assets/python-C8RW9B6A.js +0 -301
- datacontract_cli-1.1.2/datacontract/editor_assets/qsharp-AhjqtSHB.js +0 -301
- datacontract_cli-1.1.2/datacontract/editor_assets/r-D-F4P80w.js +0 -243
- datacontract_cli-1.1.2/datacontract/editor_assets/razor-CLZqdY_r.js +0 -550
- datacontract_cli-1.1.2/datacontract/editor_assets/redis-hwwIlMnV.js +0 -302
- datacontract_cli-1.1.2/datacontract/editor_assets/redshift-Bcm_kjEG.js +0 -809
- datacontract_cli-1.1.2/datacontract/editor_assets/restructuredtext-CrdzfPP-.js +0 -174
- datacontract_cli-1.1.2/datacontract/editor_assets/ruby-QC2UNm1s.js +0 -511
- datacontract_cli-1.1.2/datacontract/editor_assets/rust-5aqiQ3L5.js +0 -343
- datacontract_cli-1.1.2/datacontract/editor_assets/sb-B1g1ZfLJ.js +0 -115
- datacontract_cli-1.1.2/datacontract/editor_assets/scala-C2U4R5Gb.js +0 -370
- datacontract_cli-1.1.2/datacontract/editor_assets/scheme-DeFRbslr.js +0 -108
- datacontract_cli-1.1.2/datacontract/editor_assets/scss-RSTCiCmI.js +0 -262
- datacontract_cli-1.1.2/datacontract/editor_assets/shell-BpF-iNu8.js +0 -221
- datacontract_cli-1.1.2/datacontract/editor_assets/solidity-C00r2mbQ.js +0 -1367
- datacontract_cli-1.1.2/datacontract/editor_assets/sophia-DVW5ZKDB.js +0 -199
- datacontract_cli-1.1.2/datacontract/editor_assets/sparql-B0nmpxeX.js +0 -201
- datacontract_cli-1.1.2/datacontract/editor_assets/sql-CJwKPERn.js +0 -853
- datacontract_cli-1.1.2/datacontract/editor_assets/st-DPlKG5DT.js +0 -416
- datacontract_cli-1.1.2/datacontract/editor_assets/swift-YZBAbVHV.js +0 -309
- datacontract_cli-1.1.2/datacontract/editor_assets/systemverilog-CDQSaH63.js +0 -576
- datacontract_cli-1.1.2/datacontract/editor_assets/tcl-Dlu8sp6Q.js +0 -232
- datacontract_cli-1.1.2/datacontract/editor_assets/tsMode-yleMwhPq.js +0 -933
- datacontract_cli-1.1.2/datacontract/editor_assets/twig-C5TO7-kw.js +0 -392
- datacontract_cli-1.1.2/datacontract/editor_assets/typescript-CJBeebKU.js +0 -343
- datacontract_cli-1.1.2/datacontract/editor_assets/typespec-BsmjDEu7.js +0 -117
- datacontract_cli-1.1.2/datacontract/editor_assets/vb-ChHvIDrk.js +0 -372
- datacontract_cli-1.1.2/datacontract/editor_assets/wgsl-XEp1ju8c.js +0 -439
- datacontract_cli-1.1.2/datacontract/editor_assets/xml-vC-NMVX1.js +0 -95
- datacontract_cli-1.1.2/datacontract/editor_assets/yaml-Dlf2VF-T.js +0 -206
- datacontract_cli-1.1.2/datacontract/export/great_expectations_exporter.py +0 -357
- datacontract_cli-1.1.2/tests/test_export_great_expectations.py +0 -621
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/LICENSE +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/MANIFEST.in +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/__init__.py +0 -0
- {datacontract_cli-1.1.2/datacontract/engines → datacontract_cli-1.2.0/datacontract/breaking}/__init__.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/catalog/catalog.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/changelog/__init__.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/changelog/changelog.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/command_api.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/command_catalog.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/command_changelog.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/command_edit.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/command_init.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/command_lint.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/command_publish.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/config/__init__.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/config/cli_context.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/editor_assets/logo_fuchsia_v2.svg +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/editor_assets/odcs.svg +0 -0
- {datacontract_cli-1.1.2/datacontract/engines/checks → datacontract_cli-1.2.0/datacontract/engines}/__init__.py +0 -0
- {datacontract_cli-1.1.2/datacontract/engines/ibis → datacontract_cli-1.2.0/datacontract/engines/checks}/__init__.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/checks/check_filter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/checks/physical_type_match.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/checks/sql_guard.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/datacontract/check_azure_blob_file.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/datacontract/check_that_datacontract_contains_valid_servers_configuration.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/datacontract/check_that_datacontract_file_exists.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/fastjsonschema/s3/s3_read_files.py +0 -0
- {datacontract_cli-1.1.2/datacontract/engines/ibis/connections → datacontract_cli-1.2.0/datacontract/engines/ibis}/__init__.py +0 -0
- {datacontract_cli-1.1.2/datacontract/export → datacontract_cli-1.2.0/datacontract/engines/ibis/connections}/__init__.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/aws_credentials.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/databricks_patch.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/oracle_patch.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/redshift_credentials.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/ibis/connections/redshift_patch.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/engines/ibis/native_type.py +0 -0
- {datacontract_cli-1.1.2/datacontract/output → datacontract_cli-1.2.0/datacontract/export}/__init__.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/custom_exporter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/dbml_exporter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/dbt_exporter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/dqx_exporter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/excel_exporter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/exporter_factory.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/html_exporter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/markdown_exporter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/mermaid_exporter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/odcs_v3_exporter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/pandas_type_converter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/sodacl_exporter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/export/sql_exporter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/athena_importer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/bigquery_importer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/csv_importer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/dbml_importer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/dbt_importer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/importer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/importer_factory.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/json_importer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/mysql_importer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/object_storage_importer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/odcs_importer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/oracle_importer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/powerbi_importer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/pydantic_importer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/redshift_importer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/sqlserver_importer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/imports/trino_importer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/integration/dbt_sync.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/integration/entropy_data.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/lint/files.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/lint/resources.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/lint/s3.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/lint/schema.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/lint/urls.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/model/changelog.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/model/exceptions.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/model/odcs.py +0 -0
- /datacontract_cli-1.1.2/datacontract/py.typed → /datacontract_cli-1.2.0/datacontract/output/__init__.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/output/ci_output.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/output/json_test_results.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/output/junit_test_results.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/output/output_format.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/output/text_changelog_results.py +0 -0
- /datacontract_cli-1.1.2/tests/test_description_linter.py → /datacontract_cli-1.2.0/datacontract/py.typed +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/schemas/datacontract-1.1.0.init.yaml +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/schemas/datacontract-1.1.0.schema.json +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/schemas/datacontract-1.2.0.init.yaml +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/schemas/datacontract-1.2.0.schema.json +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/schemas/datacontract-1.2.1.init.yaml +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/schemas/datacontract-1.2.1.schema.json +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/schemas/odcs-3.0.1.schema.json +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/schemas/odcs-3.0.2.schema.json +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/schemas/odcs-3.1.0.schema.json +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/datacontract.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/excel/odcs-template.xlsx +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/index.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/macros/authoritative_definitions.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/macros/support.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials/datacontract_information.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials/datacontract_servicelevels.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials/datacontract_terms.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials/definition.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials/example.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials/model_field.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials/quality.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials/server.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/diagram.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/dialog_yaml.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/footer.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/fundamentals.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/header.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/pricing.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/roles.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/servers.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/support.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/team.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/term_of_use.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/partials_odcs/title.html +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract/templates/style/output.css +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract_cli.egg-info/dependency_links.txt +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract_cli.egg-info/entry_points.txt +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/datacontract_cli.egg-info/top_level.txt +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/setup.cfg +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_api_openapi.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_catalog.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_changelog.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_changelog_engine.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_changelog_normalize.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_changelog_output_text.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_check_quality_provenance.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_ci_output.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_command_api.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_config.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_config_require.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_config_threading.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_connect_athena.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_connect_bigquery.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_connect_databricks.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_connect_impala.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_connect_mysql.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_connect_oracle.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_connect_postgres.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_connect_redshift.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_connect_s3_credentials.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_connect_snowflake.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_connect_sqlserver.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_connect_trino.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_create_checks_physical_type.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_create_checks_primary_key.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_create_checks_sql_placeholders.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_data_contract.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_data_contract_checks.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_data_contract_specification.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_dbt_sync.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_dbt_test_mapping.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_docs_commands.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_docs_config_options.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_docs_ordering.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_download_datacontract_file.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_edit.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_avro_idl.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_bigquery.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_complex_data_contract.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_custom.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_custom_exporter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_custom_model.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_data_caterer.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_dbml.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_dbt_models.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_dbt_sources.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_dbt_staging_sql.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_dcs.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_dqx.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_excel.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_go.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_html.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_iceberg.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_large_contract.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_markdown.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_mermaid.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_protobuf.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_pydantic.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_spark.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_sql.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_sql_clickhouse.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_sql_query.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_export_sqlalchemy.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_ibis_mssql_pattern.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_ibis_mssql_schema.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_ibis_oracle_schema.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_bigquery.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_dbml.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_dbt.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_glue.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_json.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_jsonschema.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_object_storage.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_odcs_v3.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_oracle.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_protobuf.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_pydantic_model.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_sqlserver.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_trino.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_import_unity_file.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_install_extras.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_integration_entropydata.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_lint_urls.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_native_type_schema_scope.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_oracle_patch.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_physical_type_match.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_publish_test_results.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_publish_test_results_payload.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_quality_sql_dialects.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_redshift_credentials.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_redshift_patch.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_roundtrip_jsonschema.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_run_check_deprecated_fields.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_run_check_engine.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_server_overrides.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_server_type_mssql.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_server_type_synonyms.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_sql_type_converter_physicaltype.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_api.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_athena_iceberg.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_azure_blob_file.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_azure_remote.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_bigquery.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_checks_filter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_databricks.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_delta.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_diagnostics.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_duckdb.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_failed_samples.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_gcs_csv_remote.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_gcs_json_remote.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_kafka.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_kafka_remote.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_local_json.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_local_json_nd.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_metadata_only.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_mysql.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_oracle.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_oracle_xe.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_output_json.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_output_junit.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_postgres.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_quality.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_quality_id_and_tag_filter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_quality_local_fallback.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_quality_percent_severity.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_redshift.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_row_filter.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_s3_csv.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_s3_delta.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_s3_json.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_s3_json_complex.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_s3_json_multiple_models.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_s3_json_remote.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_schema_evolution.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_server_not_found.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_snowflake.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_soda_connection_error.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_sqlserver.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_test_trino.py +0 -0
- {datacontract_cli-1.1.2 → datacontract_cli-1.2.0}/tests/test_type_normalize.py +0 -0
- {datacontract_cli-1.1.2 → 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<
|
|
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"
|
|
@@ -138,17 +139,17 @@ Requires-Dist: datacontract-cli[all]; extra == "dev"
|
|
|
138
139
|
Requires-Dist: httpx==0.28.1; extra == "dev"
|
|
139
140
|
Requires-Dist: kafka-python; extra == "dev"
|
|
140
141
|
Requires-Dist: minio==7.2.20; extra == "dev"
|
|
141
|
-
Requires-Dist: moto==5.2.
|
|
142
|
+
Requires-Dist: moto==5.2.3; extra == "dev"
|
|
142
143
|
Requires-Dist: openapi-spec-validator<0.10,>=0.7; extra == "dev"
|
|
143
144
|
Requires-Dist: pandas>=2.1.0; extra == "dev"
|
|
144
145
|
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
|
|
@@ -296,6 +297,9 @@ $ datacontract lint odcs.yaml
|
|
|
296
297
|
# show a changelog between two data contracts
|
|
297
298
|
$ datacontract changelog v1.odcs.yaml v2.odcs.yaml
|
|
298
299
|
|
|
300
|
+
# fail when a contract change is backward-incompatible
|
|
301
|
+
$ datacontract breaking v1.odcs.yaml v2.odcs.yaml
|
|
302
|
+
|
|
299
303
|
# execute schema and quality checks (define credentials as environment variables)
|
|
300
304
|
$ datacontract test odcs.yaml
|
|
301
305
|
|
|
@@ -141,6 +141,9 @@ $ datacontract lint odcs.yaml
|
|
|
141
141
|
# show a changelog between two data contracts
|
|
142
142
|
$ datacontract changelog v1.odcs.yaml v2.odcs.yaml
|
|
143
143
|
|
|
144
|
+
# fail when a contract change is backward-incompatible
|
|
145
|
+
$ datacontract breaking v1.odcs.yaml v2.odcs.yaml
|
|
146
|
+
|
|
144
147
|
# execute schema and quality checks (define credentials as environment variables)
|
|
145
148
|
$ datacontract test odcs.yaml
|
|
146
149
|
|
|
@@ -15,11 +15,12 @@ from pydantic import BaseModel, Field, ValidationError
|
|
|
15
15
|
|
|
16
16
|
from datacontract.config import Config, known_env_names
|
|
17
17
|
from datacontract.data_contract import DataContract, ExportFormat
|
|
18
|
+
from datacontract.model.breaking import BreakingChangeEntry
|
|
18
19
|
from datacontract.model.changelog import ChangelogEntry
|
|
19
20
|
from datacontract.model.exceptions import DataContractException, DefinitionResolutionError
|
|
20
21
|
from datacontract.model.run import Check, ResultEnum, Run
|
|
21
22
|
|
|
22
|
-
DATA_CONTRACT_EXAMPLE_PAYLOAD = """apiVersion: v3.
|
|
23
|
+
DATA_CONTRACT_EXAMPLE_PAYLOAD = """apiVersion: v3.2.0
|
|
23
24
|
kind: DataContract
|
|
24
25
|
id: orders
|
|
25
26
|
name: Orders
|
|
@@ -323,6 +324,14 @@ app = FastAPI(
|
|
|
323
324
|
"url": "https://docs.datacontract.com/commands/changelog",
|
|
324
325
|
},
|
|
325
326
|
},
|
|
327
|
+
{
|
|
328
|
+
"name": "breaking",
|
|
329
|
+
"description": "Compare two versions of a data contract and grade each change by compatibility impact.",
|
|
330
|
+
"externalDocs": {
|
|
331
|
+
"description": "Documentation",
|
|
332
|
+
"url": "https://docs.datacontract.com/commands/breaking",
|
|
333
|
+
},
|
|
334
|
+
},
|
|
326
335
|
],
|
|
327
336
|
)
|
|
328
337
|
|
|
@@ -881,6 +890,21 @@ class ChangelogResponse(BaseModel):
|
|
|
881
890
|
)
|
|
882
891
|
|
|
883
892
|
|
|
893
|
+
class BreakingChangesResponse(BaseModel):
|
|
894
|
+
"""Every change between two versions of a data contract, each graded `info`, `warning` or `error`."""
|
|
895
|
+
|
|
896
|
+
summary: list[BreakingChangeEntry] = Field(
|
|
897
|
+
description="One entry per changed element, graded by the most severe change. A property whose type and requiredness "
|
|
898
|
+
"both changed appears once.",
|
|
899
|
+
)
|
|
900
|
+
entries: list[BreakingChangeEntry] = Field(
|
|
901
|
+
description="Every single change with its grade and the old and new value.",
|
|
902
|
+
)
|
|
903
|
+
is_breaking: bool = Field(
|
|
904
|
+
description="Whether any detected change is classified as an error-level breaking change.",
|
|
905
|
+
)
|
|
906
|
+
|
|
907
|
+
|
|
884
908
|
@app.post(
|
|
885
909
|
"/changelog",
|
|
886
910
|
tags=["changelog"],
|
|
@@ -935,6 +959,54 @@ async def changelog_endpoint(
|
|
|
935
959
|
os.unlink(v2_path)
|
|
936
960
|
|
|
937
961
|
|
|
962
|
+
@app.post(
|
|
963
|
+
"/breaking",
|
|
964
|
+
tags=["breaking"],
|
|
965
|
+
operation_id="breakingChangesDataContracts",
|
|
966
|
+
summary="Show compatibility impact between two data contracts.",
|
|
967
|
+
description="""
|
|
968
|
+
Compare two versions of an ODCS data contract and detect backward-incompatible changes.
|
|
969
|
+
|
|
970
|
+
`POST` a JSON body with `v1` (before) and `v2` (after) as YAML strings. A contract that cannot be
|
|
971
|
+
parsed is answered with `422`.
|
|
972
|
+
""",
|
|
973
|
+
response_description="The breaking changes detected between the two data contracts.",
|
|
974
|
+
responses={
|
|
975
|
+
**AUTHENTICATION_RESPONSES,
|
|
976
|
+
422: {
|
|
977
|
+
"description": "One of the two data contracts is not valid YAML or not a valid data contract.",
|
|
978
|
+
"model": UnprocessableEntityResponse,
|
|
979
|
+
"content": {"application/json": {"example": {"detail": "Invalid YAML: while parsing a block mapping"}}},
|
|
980
|
+
},
|
|
981
|
+
},
|
|
982
|
+
)
|
|
983
|
+
def breaking_endpoint(
|
|
984
|
+
body: ChangelogRequest,
|
|
985
|
+
api_key: Annotated[str | None, Depends(api_key_header)] = None,
|
|
986
|
+
) -> BreakingChangesResponse:
|
|
987
|
+
check_api_key(api_key)
|
|
988
|
+
|
|
989
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f1:
|
|
990
|
+
f1.write(body.v1)
|
|
991
|
+
v1_path = f1.name
|
|
992
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f2:
|
|
993
|
+
f2.write(body.v2)
|
|
994
|
+
v2_path = f2.name
|
|
995
|
+
|
|
996
|
+
try:
|
|
997
|
+
result = DataContract(data_contract_file=v1_path).breaking(DataContract(data_contract_file=v2_path))
|
|
998
|
+
return BreakingChangesResponse(summary=result.summary, entries=result.entries, is_breaking=result.is_breaking)
|
|
999
|
+
except yaml.YAMLError as e:
|
|
1000
|
+
raise HTTPException(status_code=422, detail=f"Invalid YAML: {e}")
|
|
1001
|
+
except pydantic.ValidationError as e:
|
|
1002
|
+
raise HTTPException(status_code=422, detail=f"Invalid data contract: {e}")
|
|
1003
|
+
except DataContractException as e:
|
|
1004
|
+
raise HTTPException(status_code=422, detail=f"Data Contract Validation Failure: {e}")
|
|
1005
|
+
finally:
|
|
1006
|
+
os.unlink(v1_path)
|
|
1007
|
+
os.unlink(v2_path)
|
|
1008
|
+
|
|
1009
|
+
|
|
938
1010
|
@app.post(
|
|
939
1011
|
"/export",
|
|
940
1012
|
tags=["export"],
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from collections.abc import Iterable
|
|
3
|
+
|
|
4
|
+
from datacontract.breaking.rules import DEFAULT_RULES, BreakingChangeRule
|
|
5
|
+
from datacontract.model.breaking import BreakingChangeEntry, BreakingChangeLevel, BreakingChangeResult
|
|
6
|
+
from datacontract.model.changelog import ChangelogEntry, ChangelogResult, ChangelogType
|
|
7
|
+
|
|
8
|
+
_LEVEL_ORDER = {
|
|
9
|
+
BreakingChangeLevel.INFO: 0,
|
|
10
|
+
BreakingChangeLevel.WARNING: 1,
|
|
11
|
+
BreakingChangeLevel.ERROR: 2,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BreakingChangeDetector:
|
|
16
|
+
"""Classify detailed changelog entries using an ordered rule set."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, rules: Iterable[BreakingChangeRule] = DEFAULT_RULES):
|
|
19
|
+
self._rules = tuple(rules)
|
|
20
|
+
if not self._rules:
|
|
21
|
+
raise ValueError("At least one breaking-change rule is required")
|
|
22
|
+
|
|
23
|
+
def detect(self, changelog: ChangelogResult) -> BreakingChangeResult:
|
|
24
|
+
entries = [self._classify(entry) for entry in changelog.entries]
|
|
25
|
+
# Nothing inside a newly added element can break a consumer: it did not exist before.
|
|
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)
|
|
47
|
+
for entry in entries:
|
|
48
|
+
segments = entry.path.split(".")
|
|
49
|
+
if any(".".join(segments[:i]) in added for i in range(1, len(segments))):
|
|
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
|
|
58
|
+
entries_by_prefix = defaultdict(list)
|
|
59
|
+
for entry in entries:
|
|
60
|
+
prefix = ""
|
|
61
|
+
for segment in entry.path.split("."):
|
|
62
|
+
prefix = f"{prefix}.{segment}" if prefix else segment
|
|
63
|
+
entries_by_prefix[prefix].append(entry)
|
|
64
|
+
summary = [self._summarize(entry, entries_by_prefix.get(entry.path, [])) for entry in changelog.summary]
|
|
65
|
+
return BreakingChangeResult(v1=changelog.v1, v2=changelog.v2, summary=summary, entries=entries)
|
|
66
|
+
|
|
67
|
+
def _classify(self, entry: ChangelogEntry) -> BreakingChangeEntry:
|
|
68
|
+
for rule in self._rules:
|
|
69
|
+
evaluation = rule.evaluate(entry)
|
|
70
|
+
if evaluation is not None:
|
|
71
|
+
return BreakingChangeEntry(
|
|
72
|
+
path=entry.path,
|
|
73
|
+
change_type=entry.type,
|
|
74
|
+
level=evaluation.level,
|
|
75
|
+
message=evaluation.message,
|
|
76
|
+
rule_id=evaluation.rule_id,
|
|
77
|
+
old_value=entry.old_value,
|
|
78
|
+
new_value=entry.new_value,
|
|
79
|
+
)
|
|
80
|
+
raise ValueError(f"No breaking-change rule classified {entry.path}")
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def _summarize(summary_entry: ChangelogEntry, entries: list[BreakingChangeEntry]) -> BreakingChangeEntry:
|
|
84
|
+
if not entries:
|
|
85
|
+
return BreakingChangeEntry(
|
|
86
|
+
path=summary_entry.path,
|
|
87
|
+
change_type=summary_entry.type,
|
|
88
|
+
level=BreakingChangeLevel.INFO,
|
|
89
|
+
message=f"Changed contract at {summary_entry.path}",
|
|
90
|
+
rule_id="summary-fallback",
|
|
91
|
+
)
|
|
92
|
+
highest = max(entries, key=lambda entry: _LEVEL_ORDER[entry.level])
|
|
93
|
+
return BreakingChangeEntry(
|
|
94
|
+
path=summary_entry.path,
|
|
95
|
+
change_type=summary_entry.type,
|
|
96
|
+
level=highest.level,
|
|
97
|
+
message=f"{highest.level.value.capitalize()} impact at {summary_entry.path}",
|
|
98
|
+
rule_id=f"summary:{highest.rule_id}",
|
|
99
|
+
old_value=highest.old_value,
|
|
100
|
+
new_value=highest.new_value,
|
|
101
|
+
)
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from datetime import date
|
|
4
|
+
|
|
5
|
+
from datacontract.model.breaking import BreakingChangeLevel
|
|
6
|
+
from datacontract.model.changelog import ChangelogEntry, ChangelogType
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class RuleEvaluation:
|
|
11
|
+
rule_id: str
|
|
12
|
+
level: BreakingChangeLevel
|
|
13
|
+
message: str
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BreakingChangeRule(ABC):
|
|
17
|
+
rule_id: str
|
|
18
|
+
|
|
19
|
+
@abstractmethod
|
|
20
|
+
def evaluate(self, entry: ChangelogEntry) -> RuleEvaluation | None:
|
|
21
|
+
"""Return a classification when this rule applies to ``entry``."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SchemaRemovedRule(BreakingChangeRule):
|
|
25
|
+
rule_id = "schema-removed"
|
|
26
|
+
|
|
27
|
+
def evaluate(self, entry: ChangelogEntry) -> RuleEvaluation | None:
|
|
28
|
+
segments = entry.path.split(".")
|
|
29
|
+
if entry.type == ChangelogType.removed and len(segments) == 2 and segments[0] == "schema":
|
|
30
|
+
return RuleEvaluation(self.rule_id, BreakingChangeLevel.ERROR, f"Removed schema {segments[1]}")
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class FieldRemovedRule(BreakingChangeRule):
|
|
35
|
+
rule_id = "field-removed"
|
|
36
|
+
|
|
37
|
+
def evaluate(self, entry: ChangelogEntry) -> RuleEvaluation | None:
|
|
38
|
+
segments = entry.path.split(".")
|
|
39
|
+
if entry.type != ChangelogType.removed or not _is_schema_property_path(segments):
|
|
40
|
+
return None
|
|
41
|
+
property_name = segments[-1]
|
|
42
|
+
return RuleEvaluation(self.rule_id, BreakingChangeLevel.ERROR, f"Removed property {property_name}")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class RequiredChangedRule(BreakingChangeRule):
|
|
46
|
+
rule_id = "required-changed"
|
|
47
|
+
|
|
48
|
+
def evaluate(self, entry: ChangelogEntry) -> RuleEvaluation | None:
|
|
49
|
+
if not entry.path.endswith(".required"):
|
|
50
|
+
return None
|
|
51
|
+
old = _parse_bool(entry.old_value)
|
|
52
|
+
new = _parse_bool(entry.new_value)
|
|
53
|
+
if entry.type == ChangelogType.added and new is True:
|
|
54
|
+
level = BreakingChangeLevel.ERROR
|
|
55
|
+
elif entry.type == ChangelogType.removed or new is False:
|
|
56
|
+
# Dropping the requirement, or writing out the optional default, only loosens the contract.
|
|
57
|
+
level = BreakingChangeLevel.INFO
|
|
58
|
+
elif entry.type == ChangelogType.updated and old is False and new is True:
|
|
59
|
+
level = BreakingChangeLevel.ERROR
|
|
60
|
+
elif entry.type in (ChangelogType.added, ChangelogType.updated):
|
|
61
|
+
level = BreakingChangeLevel.WARNING
|
|
62
|
+
else:
|
|
63
|
+
return None
|
|
64
|
+
return RuleEvaluation(self.rule_id, level, _change_message("requiredness", entry))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class TypeChangedRule(BreakingChangeRule):
|
|
68
|
+
rule_id = "type-changed"
|
|
69
|
+
|
|
70
|
+
def evaluate(self, entry: ChangelogEntry) -> RuleEvaluation | None:
|
|
71
|
+
if not entry.path.endswith((".logicalType", ".physicalType")):
|
|
72
|
+
return None
|
|
73
|
+
if entry.type == ChangelogType.updated:
|
|
74
|
+
level = BreakingChangeLevel.ERROR
|
|
75
|
+
elif entry.type == ChangelogType.added:
|
|
76
|
+
level = BreakingChangeLevel.INFO
|
|
77
|
+
elif entry.type == ChangelogType.removed:
|
|
78
|
+
level = BreakingChangeLevel.WARNING
|
|
79
|
+
else:
|
|
80
|
+
return None
|
|
81
|
+
return RuleEvaluation(self.rule_id, level, _change_message("type", entry))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class UniqueConstraintRule(BreakingChangeRule):
|
|
85
|
+
rule_id = "unique-constraint-changed"
|
|
86
|
+
|
|
87
|
+
def evaluate(self, entry: ChangelogEntry) -> RuleEvaluation | None:
|
|
88
|
+
if not entry.path.endswith(".unique"):
|
|
89
|
+
return None
|
|
90
|
+
old = _parse_bool(entry.old_value)
|
|
91
|
+
new = _parse_bool(entry.new_value)
|
|
92
|
+
if new is True and old is not True:
|
|
93
|
+
level = BreakingChangeLevel.ERROR
|
|
94
|
+
elif old is True and new is not True:
|
|
95
|
+
level = BreakingChangeLevel.INFO
|
|
96
|
+
else:
|
|
97
|
+
level = BreakingChangeLevel.WARNING
|
|
98
|
+
return RuleEvaluation(self.rule_id, level, _change_message("uniqueness", entry))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class KeyConstraintRule(BreakingChangeRule):
|
|
102
|
+
rule_id = "key-constraint-changed"
|
|
103
|
+
|
|
104
|
+
def evaluate(self, entry: ChangelogEntry) -> RuleEvaluation | None:
|
|
105
|
+
if not entry.path.endswith((".primaryKey", ".primary_key")):
|
|
106
|
+
return None
|
|
107
|
+
return RuleEvaluation(self.rule_id, BreakingChangeLevel.WARNING, _change_message("key constraint", entry))
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class ValidationConstraintRule(BreakingChangeRule):
|
|
111
|
+
rule_id = "validation-constraint-changed"
|
|
112
|
+
_suffixes = (
|
|
113
|
+
".pattern",
|
|
114
|
+
".minLength",
|
|
115
|
+
".maxLength",
|
|
116
|
+
".minimum",
|
|
117
|
+
".maximum",
|
|
118
|
+
".exclusiveMinimum",
|
|
119
|
+
".exclusiveMaximum",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
def evaluate(self, entry: ChangelogEntry) -> RuleEvaluation | None:
|
|
123
|
+
if not entry.path.endswith(self._suffixes):
|
|
124
|
+
return None
|
|
125
|
+
old = _parse_number(entry.old_value)
|
|
126
|
+
new = _parse_number(entry.new_value)
|
|
127
|
+
if old is None or new is None:
|
|
128
|
+
level = BreakingChangeLevel.WARNING
|
|
129
|
+
elif _is_tightening(entry.path, old, new):
|
|
130
|
+
level = BreakingChangeLevel.ERROR
|
|
131
|
+
else:
|
|
132
|
+
level = BreakingChangeLevel.INFO
|
|
133
|
+
return RuleEvaluation(self.rule_id, level, _change_message("validation constraint", entry))
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class MetadataFallbackRule(BreakingChangeRule):
|
|
137
|
+
rule_id = "metadata-or-unknown-change"
|
|
138
|
+
|
|
139
|
+
def evaluate(self, entry: ChangelogEntry) -> RuleEvaluation:
|
|
140
|
+
return RuleEvaluation(self.rule_id, BreakingChangeLevel.INFO, _change_message("contract", entry))
|
|
141
|
+
|
|
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
|
+
|
|
180
|
+
def _is_schema_property_path(segments: list[str]) -> bool:
|
|
181
|
+
return len(segments) >= 3 and segments[0] == "schema" and segments[-2] == "properties"
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _parse_bool(value: str | None) -> bool | None:
|
|
185
|
+
if value is None:
|
|
186
|
+
return None
|
|
187
|
+
normalized = value.strip().lower()
|
|
188
|
+
if normalized in {"true", "1"}:
|
|
189
|
+
return True
|
|
190
|
+
if normalized in {"false", "0"}:
|
|
191
|
+
return False
|
|
192
|
+
return None
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _parse_number(value: str | None) -> float | None:
|
|
196
|
+
if value is None:
|
|
197
|
+
return None
|
|
198
|
+
normalized = value.strip()
|
|
199
|
+
try:
|
|
200
|
+
return float(normalized)
|
|
201
|
+
except ValueError:
|
|
202
|
+
try:
|
|
203
|
+
return float(date.fromisoformat(normalized).toordinal())
|
|
204
|
+
except ValueError:
|
|
205
|
+
return None
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _is_tightening(path: str, old: float, new: float) -> bool:
|
|
209
|
+
if path.endswith((".minLength", ".minimum", ".exclusiveMinimum")):
|
|
210
|
+
return new > old
|
|
211
|
+
if path.endswith((".maxLength", ".maximum", ".exclusiveMaximum")):
|
|
212
|
+
return new < old
|
|
213
|
+
return True
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _change_message(subject: str, entry: ChangelogEntry) -> str:
|
|
217
|
+
if entry.type == ChangelogType.added:
|
|
218
|
+
return f"Added {subject} at {entry.path}"
|
|
219
|
+
if entry.type == ChangelogType.removed:
|
|
220
|
+
return f"Removed {subject} at {entry.path}"
|
|
221
|
+
return f"Changed {subject} at {entry.path} from {entry.old_value!r} to {entry.new_value!r}"
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
DEFAULT_RULES: tuple[BreakingChangeRule, ...] = (
|
|
225
|
+
RequiredChangedRule(),
|
|
226
|
+
SchemaRemovedRule(),
|
|
227
|
+
FieldRemovedRule(),
|
|
228
|
+
TypeChangedRule(),
|
|
229
|
+
UniqueConstraintRule(),
|
|
230
|
+
KeyConstraintRule(),
|
|
231
|
+
ValidationConstraintRule(),
|
|
232
|
+
EnumConstraintRule(),
|
|
233
|
+
VectorShapeRule(),
|
|
234
|
+
MetadataFallbackRule(),
|
|
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 = {}
|