datacontract-cli 1.1.1__tar.gz → 1.1.3__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.
Files changed (489) hide show
  1. datacontract_cli-1.1.3/LICENSE +21 -0
  2. {datacontract_cli-1.1.1/datacontract_cli.egg-info → datacontract_cli-1.1.3}/PKG-INFO +11 -7
  3. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/README.md +3 -0
  4. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/api.py +313 -13
  5. datacontract_cli-1.1.3/datacontract/breaking/detector.py +74 -0
  6. datacontract_cli-1.1.3/datacontract/breaking/rules.py +196 -0
  7. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/catalog/catalog.py +1 -1
  8. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/cli.py +24 -3
  9. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/command_api.py +10 -3
  10. datacontract_cli-1.1.3/datacontract/command_breaking.py +39 -0
  11. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/command_ci.py +6 -1
  12. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/command_dbt.py +15 -6
  13. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/command_export.py +7 -0
  14. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/command_test.py +18 -2
  15. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/config/settings.py +34 -2
  16. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/data_contract.py +26 -4
  17. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/checks/check_spec.py +10 -0
  18. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/checks/create_checks.py +126 -31
  19. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/checks/dimensions.py +3 -0
  20. datacontract_cli-1.1.3/datacontract/engines/checks/sql_guard.py +87 -0
  21. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/data_contract_test.py +107 -20
  22. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/datacontract/check_azure_blob_file.py +10 -5
  23. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/datacontract/check_that_datacontract_contains_valid_servers_configuration.py +3 -3
  24. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/fastjsonschema/check_jsonschema.py +31 -5
  25. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/fastjsonschema/s3/s3_read_files.py +1 -1
  26. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/ibis/connections/connect.py +94 -26
  27. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/ibis/connections/duckdb_connection.py +87 -11
  28. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/ibis/connections/kafka.py +12 -11
  29. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/ibis/connections/redshift_credentials.py +4 -4
  30. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/ibis/ibis_check_execute.py +79 -16
  31. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/avro_exporter.py +1 -1
  32. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/dbml_exporter.py +2 -2
  33. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/dcs_exporter.py +26 -1
  34. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/duckdb_type_converter.py +1 -1
  35. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/exporter.py +4 -2
  36. datacontract_cli-1.1.3/datacontract/export/great_expectations_exporter.py +732 -0
  37. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/html_exporter.py +5 -3
  38. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/jsonschema_exporter.py +3 -3
  39. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/mermaid_exporter.py +11 -2
  40. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/protobuf_exporter.py +1 -1
  41. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/pydantic_exporter.py +4 -0
  42. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/sodacl_check_builder.py +22 -6
  43. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/sql_type_converter.py +2 -2
  44. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/athena_importer.py +3 -3
  45. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/avro_importer.py +4 -4
  46. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/bigquery_importer.py +5 -5
  47. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/dbml_importer.py +1 -1
  48. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/dcs_importer.py +2 -0
  49. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/excel_importer.py +2 -2
  50. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/iceberg_importer.py +1 -1
  51. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/jsonschema_importer.py +3 -3
  52. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/mysql_importer.py +6 -6
  53. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/object_storage_importer.py +4 -4
  54. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/odcs_importer.py +4 -4
  55. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/oracle_importer.py +7 -7
  56. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/parquet_importer.py +1 -1
  57. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/postgres_importer.py +5 -5
  58. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/powerbi_importer.py +7 -7
  59. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/protobuf_importer.py +2 -2
  60. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/pydantic_importer.py +32 -6
  61. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/redshift_importer.py +6 -6
  62. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/snowflake_importer.py +5 -5
  63. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/sql_importer.py +81 -21
  64. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/sqlserver_importer.py +6 -6
  65. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/trino_importer.py +7 -7
  66. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/unity_importer.py +4 -4
  67. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/integration/dbt_sync.py +38 -28
  68. datacontract_cli-1.1.3/datacontract/integration/entropy_data.py +248 -0
  69. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/lint/files.py +1 -1
  70. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/lint/resolve.py +287 -39
  71. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/lint/s3.py +1 -1
  72. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/lint/schema.py +1 -1
  73. datacontract_cli-1.1.3/datacontract/lint/urls.py +48 -0
  74. datacontract_cli-1.1.3/datacontract/model/breaking.py +33 -0
  75. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/model/exceptions.py +16 -1
  76. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/model/run.py +41 -5
  77. datacontract_cli-1.1.3/datacontract/model/server.py +112 -0
  78. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/output/test_results_writer.py +5 -0
  79. datacontract_cli-1.1.3/datacontract/output/text_breaking_results.py +81 -0
  80. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials_odcs/diagram.html +1 -1
  81. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3/datacontract_cli.egg-info}/PKG-INFO +11 -7
  82. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract_cli.egg-info/SOURCES.txt +29 -2
  83. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract_cli.egg-info/requires.txt +7 -6
  84. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/pyproject.toml +27 -7
  85. datacontract_cli-1.1.3/tests/test_api.py +677 -0
  86. datacontract_cli-1.1.3/tests/test_breaking.py +337 -0
  87. datacontract_cli-1.1.3/tests/test_check_quality_provenance.py +77 -0
  88. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_cli.py +30 -0
  89. datacontract_cli-1.1.3/tests/test_command_api.py +32 -0
  90. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_config.py +4 -0
  91. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_config_file.py +14 -2
  92. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_connect_bigquery.py +52 -11
  93. datacontract_cli-1.1.3/tests/test_create_checks_array_items.py +71 -0
  94. datacontract_cli-1.1.3/tests/test_create_checks_retention_period.py +57 -0
  95. datacontract_cli-1.1.3/tests/test_create_checks_servicelevel.py +112 -0
  96. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_data_contract_checks.py +64 -0
  97. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_dbt_sync.py +108 -0
  98. datacontract_cli-1.1.3/tests/test_description_linter.py +0 -0
  99. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_duckdb_json.py +48 -0
  100. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_avro.py +17 -0
  101. datacontract_cli-1.1.3/tests/test_export_dcs.py +131 -0
  102. datacontract_cli-1.1.3/tests/test_export_great_expectations.py +1508 -0
  103. datacontract_cli-1.1.3/tests/test_export_html.py +155 -0
  104. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_jsonschema.py +19 -1
  105. datacontract_cli-1.1.3/tests/test_export_large_contract.py +57 -0
  106. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_mermaid.py +36 -0
  107. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_sodacl.py +41 -3
  108. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_sql_oracle.py +1 -1
  109. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_sql_postgres.py +24 -1
  110. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_sql_snowflake.py +42 -1
  111. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_sql_sqlserver.py +1 -1
  112. datacontract_cli-1.1.3/tests/test_lint_urls.py +85 -0
  113. datacontract_cli-1.1.3/tests/test_publish_test_results.py +142 -0
  114. datacontract_cli-1.1.3/tests/test_publish_test_results_payload.py +141 -0
  115. datacontract_cli-1.1.3/tests/test_quality_sql_dialects.py +132 -0
  116. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_resolve.py +37 -0
  117. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_resolve_definitions.py +45 -5
  118. datacontract_cli-1.1.3/tests/test_resolve_local_definitions.py +615 -0
  119. datacontract_cli-1.1.3/tests/test_roundtrip_pydantic_model.py +108 -0
  120. datacontract_cli-1.1.3/tests/test_run_check_engine.py +40 -0
  121. datacontract_cli-1.1.3/tests/test_server_overrides.py +104 -0
  122. datacontract_cli-1.1.3/tests/test_server_type_synonyms.py +85 -0
  123. datacontract_cli-1.1.3/tests/test_test_array_items.py +109 -0
  124. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_azure_blob_file.py +3 -3
  125. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_diagnostics.py +3 -0
  126. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_dimension_filter.py +12 -0
  127. datacontract_cli-1.1.3/tests/test_test_dry_run.py +175 -0
  128. datacontract_cli-1.1.3/tests/test_test_duckdb.py +194 -0
  129. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_kafka_decoding.py +20 -0
  130. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_postgres.py +26 -1
  131. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_quality.py +1 -1
  132. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_redshift.py +1 -1
  133. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_soda_connection_error.py +1 -1
  134. datacontract_cli-1.1.3/tests/test_untrusted_contract.py +324 -0
  135. datacontract_cli-1.1.1/LICENSE +0 -48
  136. datacontract_cli-1.1.1/datacontract/export/great_expectations_exporter.py +0 -357
  137. datacontract_cli-1.1.1/datacontract/export/odcs_export_helper.py +0 -314
  138. datacontract_cli-1.1.1/datacontract/integration/entropy_data.py +0 -143
  139. datacontract_cli-1.1.1/datacontract/lint/urls.py +0 -76
  140. datacontract_cli-1.1.1/datacontract/model/server.py +0 -50
  141. datacontract_cli-1.1.1/tests/test_api.py +0 -337
  142. datacontract_cli-1.1.1/tests/test_export_great_expectations.py +0 -621
  143. datacontract_cli-1.1.1/tests/test_export_html.py +0 -40
  144. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/MANIFEST.in +0 -0
  145. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/__init__.py +0 -0
  146. {datacontract_cli-1.1.1/datacontract/engines → datacontract_cli-1.1.3/datacontract/breaking}/__init__.py +0 -0
  147. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/changelog/__init__.py +0 -0
  148. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/changelog/changelog.py +0 -0
  149. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/changelog/normalize.py +0 -0
  150. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/command_catalog.py +0 -0
  151. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/command_changelog.py +0 -0
  152. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/command_edit.py +0 -0
  153. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/command_import.py +0 -0
  154. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/command_init.py +0 -0
  155. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/command_lint.py +0 -0
  156. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/command_publish.py +0 -0
  157. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/config/__init__.py +0 -0
  158. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/config/cli_context.py +0 -0
  159. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/VERSION +0 -0
  160. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/abap-Bs8kvjsq.js +0 -0
  161. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/apex-Dspwrl5m.js +0 -0
  162. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/assets/editor.worker-4.7.0.js +0 -0
  163. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/assets/yaml.worker-4.7.0.js +0 -0
  164. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/azcli-k1BiOau1.js +0 -0
  165. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/bat-BIP72KWc.js +0 -0
  166. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/bicep-VhQeYhS3.js +0 -0
  167. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/cameligo-DnuAXtHg.js +0 -0
  168. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/clojure-BAuDPsal.js +0 -0
  169. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/coffee-BV06qSnj.js +0 -0
  170. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/cpp-Cz8Jeyrf.js +0 -0
  171. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/csharp-F6hhlXFb.js +0 -0
  172. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/csp-KzlEHCyI.js +0 -0
  173. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/css-e4FKntZ3.js +0 -0
  174. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/cssMode-BNkTa2xp.js +0 -0
  175. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/cypher-CpvDGKRr.js +0 -0
  176. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/dart-DdHeFWR0.js +0 -0
  177. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/datacontract-editor.css +0 -0
  178. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/datacontract-editor.es.js +0 -0
  179. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/dockerfile-aoNXDcio.js +0 -0
  180. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/ecl-C4Q0DYuA.js +0 -0
  181. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/elixir-C9X42b0T.js +0 -0
  182. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/flow9-Bff42pfB.js +0 -0
  183. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/freemarker2-Dyfzmb5A.js +0 -0
  184. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/fsharp-C6vPAsHG.js +0 -0
  185. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/go-DEr8zx9f.js +0 -0
  186. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/graphql-BiNBBRFh.js +0 -0
  187. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/handlebars-BtDNI-uC.js +0 -0
  188. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/hcl-2T9lKuFg.js +0 -0
  189. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/html-Cn4TGw_o.js +0 -0
  190. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/htmlMode-Bf84vbo2.js +0 -0
  191. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/index.html +0 -0
  192. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/ini-CZGtssYM.js +0 -0
  193. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/java-B260NeH3.js +0 -0
  194. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/javascript-B86Kac07.js +0 -0
  195. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/jsonMode-Ckgg5lAE.js +0 -0
  196. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/julia-kuNLM9c5.js +0 -0
  197. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/kotlin-Bz6kAefA.js +0 -0
  198. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/less-AZUtS89I.js +0 -0
  199. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/lexon-BcmgnyRb.js +0 -0
  200. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/liquid-D0NPwuu_.js +0 -0
  201. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/logo_fuchsia_v2.svg +0 -0
  202. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/lua-Dz1RbATg.js +0 -0
  203. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/m3-CIMooLFn.js +0 -0
  204. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/markdown-C1BsCK9P.js +0 -0
  205. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/mdx-QFRSBtVQ.js +0 -0
  206. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/mips-BovsnfLQ.js +0 -0
  207. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/monaco-4.7.0.js +0 -0
  208. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/msdax-CiRB2ZB-.js +0 -0
  209. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/mysql-BwdpPE19.js +0 -0
  210. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/objective-c-D4nfHfso.js +0 -0
  211. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/odcs.svg +0 -0
  212. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/pascal--j5-PYtz.js +0 -0
  213. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/pascaligo-C7TcJxNL.js +0 -0
  214. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/perl-BzEWqUb5.js +0 -0
  215. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/pgsql-B_KZ0mGX.js +0 -0
  216. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/php-A19QHzQV.js +0 -0
  217. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/pla-COeKY1hA.js +0 -0
  218. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/postiats-BfMp7zJL.js +0 -0
  219. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/powerquery-B0TS_5Gb.js +0 -0
  220. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/powershell-5jSbKdYB.js +0 -0
  221. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/protobuf-YgnOutn3.js +0 -0
  222. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/pug-DLej6kn5.js +0 -0
  223. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/python-C8RW9B6A.js +0 -0
  224. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/qsharp-AhjqtSHB.js +0 -0
  225. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/r-D-F4P80w.js +0 -0
  226. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/razor-CLZqdY_r.js +0 -0
  227. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/redis-hwwIlMnV.js +0 -0
  228. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/redshift-Bcm_kjEG.js +0 -0
  229. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/restructuredtext-CrdzfPP-.js +0 -0
  230. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/ruby-QC2UNm1s.js +0 -0
  231. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/rust-5aqiQ3L5.js +0 -0
  232. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/sb-B1g1ZfLJ.js +0 -0
  233. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/scala-C2U4R5Gb.js +0 -0
  234. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/scheme-DeFRbslr.js +0 -0
  235. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/scss-RSTCiCmI.js +0 -0
  236. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/shell-BpF-iNu8.js +0 -0
  237. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/solidity-C00r2mbQ.js +0 -0
  238. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/sophia-DVW5ZKDB.js +0 -0
  239. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/sparql-B0nmpxeX.js +0 -0
  240. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/sql-CJwKPERn.js +0 -0
  241. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/st-DPlKG5DT.js +0 -0
  242. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/swift-YZBAbVHV.js +0 -0
  243. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/systemverilog-CDQSaH63.js +0 -0
  244. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/tcl-Dlu8sp6Q.js +0 -0
  245. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/tsMode-yleMwhPq.js +0 -0
  246. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/twig-C5TO7-kw.js +0 -0
  247. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/typescript-CJBeebKU.js +0 -0
  248. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/typespec-BsmjDEu7.js +0 -0
  249. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/vb-ChHvIDrk.js +0 -0
  250. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/wgsl-XEp1ju8c.js +0 -0
  251. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/xml-vC-NMVX1.js +0 -0
  252. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/editor_assets/yaml-Dlf2VF-T.js +0 -0
  253. {datacontract_cli-1.1.1/datacontract/engines/checks → datacontract_cli-1.1.3/datacontract/engines}/__init__.py +0 -0
  254. {datacontract_cli-1.1.1/datacontract/engines/ibis → datacontract_cli-1.1.3/datacontract/engines/checks}/__init__.py +0 -0
  255. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/checks/check_filter.py +0 -0
  256. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/checks/physical_type_match.py +0 -0
  257. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/checks/type_normalize.py +0 -0
  258. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/datacontract/check_that_datacontract_file_exists.py +0 -0
  259. {datacontract_cli-1.1.1/datacontract/engines/ibis/connections → datacontract_cli-1.1.3/datacontract/engines/ibis}/__init__.py +0 -0
  260. {datacontract_cli-1.1.1/datacontract/export → datacontract_cli-1.1.3/datacontract/engines/ibis/connections}/__init__.py +0 -0
  261. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/ibis/connections/aws_credentials.py +0 -0
  262. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/ibis/connections/databricks_patch.py +0 -0
  263. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/ibis/connections/oracle_patch.py +0 -0
  264. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/ibis/connections/redshift_patch.py +0 -0
  265. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/ibis/dtype_category.py +0 -0
  266. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/ibis/native_type.py +0 -0
  267. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/engines/ibis/snowflake_structured_types.py +0 -0
  268. {datacontract_cli-1.1.1/datacontract/output → datacontract_cli-1.1.3/datacontract/export}/__init__.py +0 -0
  269. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/avro_idl_exporter.py +0 -0
  270. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/bigquery_exporter.py +0 -0
  271. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/custom_exporter.py +0 -0
  272. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/data_caterer_exporter.py +0 -0
  273. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/dbt_exporter.py +0 -0
  274. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/dqx_exporter.py +0 -0
  275. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/excel_exporter.py +0 -0
  276. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/exporter_factory.py +0 -0
  277. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/go_exporter.py +0 -0
  278. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/iceberg_exporter.py +0 -0
  279. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/markdown_exporter.py +0 -0
  280. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/odcs_v3_exporter.py +0 -0
  281. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/pandas_type_converter.py +0 -0
  282. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/rdf_exporter.py +0 -0
  283. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/sodacl_exporter.py +0 -0
  284. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/spark_exporter.py +0 -0
  285. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/sql_exporter.py +0 -0
  286. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/export/sqlalchemy_exporter.py +0 -0
  287. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/csv_importer.py +0 -0
  288. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/dbt_importer.py +0 -0
  289. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/glue_importer.py +0 -0
  290. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/importer.py +0 -0
  291. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/importer_factory.py +0 -0
  292. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/json_importer.py +0 -0
  293. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/odcs_helper.py +0 -0
  294. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/spark_importer.py +0 -0
  295. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/imports/spark_type_json.py +0 -0
  296. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/init/init_template.py +0 -0
  297. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/integration/dbt_test_mapping.py +0 -0
  298. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/lint/resources.py +0 -0
  299. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/model/changelog.py +0 -0
  300. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/model/odcs.py +0 -0
  301. /datacontract_cli-1.1.1/datacontract/py.typed → /datacontract_cli-1.1.3/datacontract/output/__init__.py +0 -0
  302. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/output/ci_output.py +0 -0
  303. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/output/json_test_results.py +0 -0
  304. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/output/junit_test_results.py +0 -0
  305. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/output/output_format.py +0 -0
  306. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/output/text_changelog_results.py +0 -0
  307. /datacontract_cli-1.1.1/tests/test_description_linter.py → /datacontract_cli-1.1.3/datacontract/py.typed +0 -0
  308. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/schemas/datacontract-1.1.0.init.yaml +0 -0
  309. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/schemas/datacontract-1.1.0.schema.json +0 -0
  310. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/schemas/datacontract-1.2.0.init.yaml +0 -0
  311. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/schemas/datacontract-1.2.0.schema.json +0 -0
  312. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/schemas/datacontract-1.2.1.init.yaml +0 -0
  313. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/schemas/datacontract-1.2.1.schema.json +0 -0
  314. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/schemas/odcs-3.0.1.schema.json +0 -0
  315. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/schemas/odcs-3.0.2.schema.json +0 -0
  316. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/schemas/odcs-3.1.0.init.yaml +0 -0
  317. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/schemas/odcs-3.1.0.schema.json +0 -0
  318. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/datacontract.html +0 -0
  319. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/datacontract_odcs.html +0 -0
  320. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/excel/odcs-template.xlsx +0 -0
  321. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/index.html +0 -0
  322. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/macros/authoritative_definitions.html +0 -0
  323. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/macros/schema.html +0 -0
  324. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/macros/server.html +0 -0
  325. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/macros/support.html +0 -0
  326. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials/datacontract_information.html +0 -0
  327. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials/datacontract_servicelevels.html +0 -0
  328. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials/datacontract_terms.html +0 -0
  329. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials/definition.html +0 -0
  330. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials/example.html +0 -0
  331. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials/model_field.html +0 -0
  332. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials/quality.html +0 -0
  333. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials/server.html +0 -0
  334. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials_odcs/custom_properties.html +0 -0
  335. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials_odcs/dialog_yaml.html +0 -0
  336. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials_odcs/footer.html +0 -0
  337. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials_odcs/fundamentals.html +0 -0
  338. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials_odcs/header.html +0 -0
  339. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials_odcs/pricing.html +0 -0
  340. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials_odcs/roles.html +0 -0
  341. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials_odcs/schema.html +0 -0
  342. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials_odcs/servers.html +0 -0
  343. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials_odcs/sla.html +0 -0
  344. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials_odcs/support.html +0 -0
  345. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials_odcs/team.html +0 -0
  346. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials_odcs/term_of_use.html +0 -0
  347. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/partials_odcs/title.html +0 -0
  348. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract/templates/style/output.css +0 -0
  349. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract_cli.egg-info/dependency_links.txt +0 -0
  350. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract_cli.egg-info/entry_points.txt +0 -0
  351. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/datacontract_cli.egg-info/top_level.txt +0 -0
  352. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/setup.cfg +0 -0
  353. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_api_openapi.py +0 -0
  354. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_catalog.py +0 -0
  355. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_changelog.py +0 -0
  356. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_changelog_engine.py +0 -0
  357. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_changelog_normalize.py +0 -0
  358. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_changelog_output_text.py +0 -0
  359. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_ci_output.py +0 -0
  360. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_config_require.py +0 -0
  361. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_config_threading.py +0 -0
  362. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_connect_athena.py +0 -0
  363. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_connect_databricks.py +0 -0
  364. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_connect_impala.py +0 -0
  365. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_connect_mysql.py +0 -0
  366. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_connect_oracle.py +0 -0
  367. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_connect_postgres.py +0 -0
  368. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_connect_redshift.py +0 -0
  369. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_connect_s3_credentials.py +0 -0
  370. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_connect_snowflake.py +0 -0
  371. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_connect_sqlserver.py +0 -0
  372. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_connect_trino.py +0 -0
  373. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_create_checks_nested_type.py +0 -0
  374. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_create_checks_physical_type.py +0 -0
  375. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_create_checks_primary_key.py +0 -0
  376. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_create_checks_sql_placeholders.py +0 -0
  377. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_data_contract.py +0 -0
  378. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_data_contract_specification.py +0 -0
  379. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_dbt_test_mapping.py +0 -0
  380. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_docs_commands.py +0 -0
  381. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_docs_config_options.py +0 -0
  382. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_docs_ordering.py +0 -0
  383. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_download_datacontract_file.py +0 -0
  384. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_dtype_category.py +0 -0
  385. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_edit.py +0 -0
  386. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_avro_idl.py +0 -0
  387. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_bigquery.py +0 -0
  388. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_complex_data_contract.py +0 -0
  389. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_custom.py +0 -0
  390. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_custom_exporter.py +0 -0
  391. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_custom_model.py +0 -0
  392. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_data_caterer.py +0 -0
  393. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_dbml.py +0 -0
  394. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_dbt_models.py +0 -0
  395. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_dbt_sources.py +0 -0
  396. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_dbt_staging_sql.py +0 -0
  397. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_dqx.py +0 -0
  398. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_excel.py +0 -0
  399. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_go.py +0 -0
  400. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_iceberg.py +0 -0
  401. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_markdown.py +0 -0
  402. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_odcs_v3.py +0 -0
  403. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_protobuf.py +0 -0
  404. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_pydantic.py +0 -0
  405. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_rdf.py +0 -0
  406. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_spark.py +0 -0
  407. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_sql.py +0 -0
  408. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_sql_clickhouse.py +0 -0
  409. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_sql_query.py +0 -0
  410. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_export_sqlalchemy.py +0 -0
  411. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_ibis_mssql_pattern.py +0 -0
  412. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_ibis_mssql_schema.py +0 -0
  413. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_ibis_oracle_schema.py +0 -0
  414. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_athena.py +0 -0
  415. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_avro.py +0 -0
  416. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_bigquery.py +0 -0
  417. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_csv.py +0 -0
  418. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_dbml.py +0 -0
  419. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_dbt.py +0 -0
  420. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_excel.py +0 -0
  421. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_glue.py +0 -0
  422. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_iceberg.py +0 -0
  423. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_json.py +0 -0
  424. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_jsonschema.py +0 -0
  425. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_mysql.py +0 -0
  426. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_object_storage.py +0 -0
  427. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_odcs_v3.py +0 -0
  428. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_oracle.py +0 -0
  429. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_parquet.py +0 -0
  430. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_postgres.py +0 -0
  431. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_powerbi.py +0 -0
  432. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_protobuf.py +0 -0
  433. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_pydantic_model.py +0 -0
  434. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_redshift.py +0 -0
  435. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_snowflake.py +0 -0
  436. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_spark_type_json.py +0 -0
  437. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_sqlserver.py +0 -0
  438. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_trino.py +0 -0
  439. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_import_unity_file.py +0 -0
  440. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_install_extras.py +0 -0
  441. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_integration_entropydata.py +0 -0
  442. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_lint.py +0 -0
  443. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_native_type_schema_scope.py +0 -0
  444. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_oracle_patch.py +0 -0
  445. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_physical_type_match.py +0 -0
  446. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_redshift_credentials.py +0 -0
  447. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_redshift_patch.py +0 -0
  448. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_roundtrip_jsonschema.py +0 -0
  449. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_run_check_deprecated_fields.py +0 -0
  450. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_server_type_mssql.py +0 -0
  451. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_snowflake_structured_types.py +0 -0
  452. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_sql_type_converter_physicaltype.py +0 -0
  453. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_api.py +0 -0
  454. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_athena_iceberg.py +0 -0
  455. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_azure_remote.py +0 -0
  456. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_bigquery.py +0 -0
  457. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_checks_filter.py +0 -0
  458. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_databricks.py +0 -0
  459. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_delta.py +0 -0
  460. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_failed_samples.py +0 -0
  461. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_gcs_csv_remote.py +0 -0
  462. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_gcs_json_remote.py +0 -0
  463. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_kafka.py +0 -0
  464. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_kafka_remote.py +0 -0
  465. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_local_json.py +0 -0
  466. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_local_json_nd.py +0 -0
  467. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_metadata_only.py +0 -0
  468. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_mysql.py +0 -0
  469. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_oracle.py +0 -0
  470. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_oracle_xe.py +0 -0
  471. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_output_json.py +0 -0
  472. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_output_junit.py +0 -0
  473. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_parquet.py +0 -0
  474. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_quality_id_and_tag_filter.py +0 -0
  475. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_quality_local_fallback.py +0 -0
  476. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_quality_percent_severity.py +0 -0
  477. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_row_filter.py +0 -0
  478. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_s3_csv.py +0 -0
  479. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_s3_delta.py +0 -0
  480. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_s3_json.py +0 -0
  481. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_s3_json_complex.py +0 -0
  482. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_s3_json_multiple_models.py +0 -0
  483. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_s3_json_remote.py +0 -0
  484. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_schema_evolution.py +0 -0
  485. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_server_not_found.py +0 -0
  486. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_snowflake.py +0 -0
  487. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_sqlserver.py +0 -0
  488. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_test_trino.py +0 -0
  489. {datacontract_cli-1.1.1 → datacontract_cli-1.1.3}/tests/test_type_normalize.py +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Entropy Data GmbH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datacontract-cli
3
- Version: 1.1.1
3
+ Version: 1.1.3
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
@@ -46,10 +46,10 @@ 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<4.0.0,>=3.1.2
49
+ Requires-Dist: open-data-contract-standard<3.2.0,>=3.1.2
50
50
  Requires-Dist: deepdiff<10.0.0,>=6.0.0
51
51
  Provides-Extra: avro
52
- Requires-Dist: avro==1.12.1; extra == "avro"
52
+ Requires-Dist: avro==1.12.2; extra == "avro"
53
53
  Provides-Extra: bigquery
54
54
  Requires-Dist: ibis-framework[bigquery]<13.0.0,>=10.0.0; extra == "bigquery"
55
55
  Provides-Extra: csv
@@ -67,7 +67,7 @@ Requires-Dist: rich>=12.4.4; extra == "dataframe"
67
67
  Provides-Extra: databricks
68
68
  Requires-Dist: ibis-framework[databricks]<13.0.0,>=10.0.0; extra == "databricks"
69
69
  Requires-Dist: databricks-sql-connector<4.5.0,>=3.7.0; extra == "databricks"
70
- Requires-Dist: databricks-sdk<0.126.0; extra == "databricks"
70
+ Requires-Dist: databricks-sdk<0.134.0; extra == "databricks"
71
71
  Requires-Dist: packaging<27.0,>=21.3; extra == "databricks"
72
72
  Provides-Extra: databricks-runtime
73
73
  Requires-Dist: datacontract-cli[databricks]; extra == "databricks-runtime"
@@ -138,18 +138,19 @@ Requires-Dist: datacontract-cli[all]; extra == "dev"
138
138
  Requires-Dist: httpx==0.28.1; extra == "dev"
139
139
  Requires-Dist: kafka-python; extra == "dev"
140
140
  Requires-Dist: minio==7.2.20; extra == "dev"
141
- Requires-Dist: moto==5.2.2; extra == "dev"
141
+ Requires-Dist: moto==5.2.3; extra == "dev"
142
142
  Requires-Dist: openapi-spec-validator<0.10,>=0.7; extra == "dev"
143
143
  Requires-Dist: pandas>=2.1.0; extra == "dev"
144
144
  Requires-Dist: pre-commit<4.7.0,>=3.7.1; extra == "dev"
145
145
  Requires-Dist: pytest; extra == "dev"
146
+ Requires-Dist: pytest-cov; extra == "dev"
146
147
  Requires-Dist: pytest-xdist; extra == "dev"
147
148
  Requires-Dist: pymssql==2.3.13; extra == "dev"
148
149
  Requires-Dist: psycopg2-binary<3.0,>=2.9; extra == "dev"
149
150
  Requires-Dist: mysql-connector-python<26.8.0,>=8.0.30; extra == "dev"
150
- Requires-Dist: ruff==0.16.2; extra == "dev"
151
+ Requires-Dist: ruff==0.16.5; extra == "dev"
151
152
  Requires-Dist: testcontainers[kafka,minio,mssql,mysql,postgres]==4.15.0; extra == "dev"
152
- Requires-Dist: trino==0.338.0; extra == "dev"
153
+ Requires-Dist: trino==0.339.0; extra == "dev"
153
154
  Dynamic: license-file
154
155
 
155
156
  # Data Contract CLI
@@ -295,6 +296,9 @@ $ datacontract lint odcs.yaml
295
296
  # show a changelog between two data contracts
296
297
  $ datacontract changelog v1.odcs.yaml v2.odcs.yaml
297
298
 
299
+ # fail when a contract change is backward-incompatible
300
+ $ datacontract breaking v1.odcs.yaml v2.odcs.yaml
301
+
298
302
  # execute schema and quality checks (define credentials as environment variables)
299
303
  $ datacontract test odcs.yaml
300
304
 
@@ -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
 
@@ -1,6 +1,7 @@
1
1
  import json
2
2
  import logging
3
3
  import os
4
+ import secrets
4
5
  import tempfile
5
6
  from importlib import metadata
6
7
  from typing import Annotated, Optional
@@ -8,15 +9,15 @@ from typing import Annotated, Optional
8
9
  import pydantic
9
10
  import yaml
10
11
  from fastapi import Body, Depends, FastAPI, HTTPException, Query, Request, status
11
- from fastapi.middleware.cors import CORSMiddleware
12
12
  from fastapi.responses import PlainTextResponse
13
13
  from fastapi.security.api_key import APIKeyHeader
14
14
  from pydantic import BaseModel, Field, ValidationError
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
- from datacontract.model.exceptions import DataContractException
20
+ from datacontract.model.exceptions import DataContractException, DefinitionResolutionError
20
21
  from datacontract.model.run import Check, ResultEnum, Run
21
22
 
22
23
  DATA_CONTRACT_EXAMPLE_PAYLOAD = """apiVersion: v3.1.0
@@ -251,6 +252,16 @@ that value, and answers `401` when the header is missing and `403` when it is wr
251
252
  Securing the API is strongly recommended: `POST /test` connects to your data sources, and test
252
253
  results may contain sensitive information.
253
254
 
255
+ ## Contracts are untrusted
256
+
257
+ A posted contract carries SQL and names the hosts to connect to, so the server treats it as
258
+ untrusted input: a `quality.type: sql` rule must be a read-only query, a credential held in the
259
+ server's environment is never sent to a host the contract names, a `publish_url` may only point at
260
+ the Entropy Data platform or the host configured via `ENTROPY_DATA_HOST`, and `servers[].type: local`
261
+ is refused so a caller cannot read the server's own files. Set
262
+ `DATACONTRACT_CLI_API_ALLOW_LOCAL_FILES=true` if the deployment serves its own files on purpose;
263
+ the contract is then still confined to the paths it declares.
264
+
254
265
  ## Connecting to data sources
255
266
 
256
267
  `POST /test` needs credentials for the server described in the contract's `servers` section. Provide
@@ -313,6 +324,14 @@ app = FastAPI(
313
324
  "url": "https://docs.datacontract.com/commands/changelog",
314
325
  },
315
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
+ },
316
335
  ],
317
336
  )
318
337
 
@@ -334,12 +353,17 @@ def _openapi_with_external_docs() -> dict:
334
353
 
335
354
  app.openapi = _openapi_with_external_docs
336
355
 
337
- app.add_middleware(
338
- CORSMiddleware,
339
- allow_origins=["*"],
340
- allow_methods=["*"],
341
- allow_headers=["*"],
342
- )
356
+ # Contracts posted to this server arrive over HTTP from callers we do not trust,
357
+ # so a credential the server holds in its environment must never be sent to a
358
+ # host the contract names. The `edit` server reuses the `test` handler for a
359
+ # local file the user owns, so it does not set this flag and is not guarded.
360
+ app.state.untrusted_contracts = True
361
+
362
+ # No CORS middleware: the only browser client is the Swagger UI served at "/",
363
+ # which is same-origin and therefore not subject to CORS. Every other client
364
+ # (curl, the SDK, CI) is not a browser and ignores CORS. Allowing arbitrary
365
+ # origins would only let a page the operator happens to visit drive this server
366
+ # and read its responses, so the safe default is to send no CORS headers at all.
343
367
 
344
368
  api_key_header = APIKeyHeader(
345
369
  name="x-api-key",
@@ -446,6 +470,198 @@ def _parse_filters_query(filters: str | None) -> dict[str, str] | None:
446
470
  return {schema_name: predicate.strip() for schema_name, predicate in parsed.items()}
447
471
 
448
472
 
473
+ # For each server type whose connection host can be taken from the posted
474
+ # contract: (the Server attribute that names the connection target, the Config
475
+ # field an operator sets to pin that target in the environment or None when the
476
+ # type has no such field, the Config fields that hold a connection secret).
477
+ # See _reject_environment_credentials_to_untrusted_host.
478
+ _ENVIRONMENT_CREDENTIAL_TARGETS: dict[str, tuple[str, str | None, tuple[str, ...]]] = {
479
+ "postgres": ("host", "postgres_host", ("postgres_password",)),
480
+ "mysql": ("host", "mysql_host", ("mysql_password",)),
481
+ "oracle": ("host", "oracle_host", ("oracle_password",)),
482
+ "impala": ("host", "impala_host", ("impala_password",)),
483
+ "trino": ("host", "trino_host", ("trino_password",)),
484
+ "redshift": ("host", "redshift_host", ("redshift_password",)),
485
+ "sqlserver": ("host", "sqlserver_host", ("sqlserver_password",)),
486
+ "snowflake": (
487
+ "account",
488
+ "snowflake_account",
489
+ ("snowflake_password", "snowflake_token", "snowflake_private_key"),
490
+ ),
491
+ "databricks": ("host", "databricks_server_hostname", ("databricks_token", "databricks_client_secret")),
492
+ # Kafka's broker host is always the contract's `server.host` (no environment
493
+ # option pins it), so an environment-held SASL password can never be paired
494
+ # with a posted contract safely -- host_field is None, so it is always rejected.
495
+ "kafka": ("broker host", None, ("kafka_sasl_password",)),
496
+ }
497
+
498
+
499
+ def _selected_server(body: str, server_name: str | None, config):
500
+ """The server the request would test, or None if that cannot be determined.
501
+
502
+ A contract that does not resolve, or that names no such server, is reported
503
+ by test() itself -- the guards below simply have nothing to check.
504
+ """
505
+ from datacontract.engines.data_contract_test import get_server
506
+ from datacontract.lint import resolve
507
+
508
+ try:
509
+ data_contract = resolve.resolve_data_contract(data_contract_str=body, config=config)
510
+ return get_server(data_contract, server_name)
511
+ except Exception:
512
+ return None
513
+
514
+
515
+ # Server types that read their data through the local filesystem of the machine
516
+ # running the test, rather than from a remote data source.
517
+ _LOCAL_SERVER_TYPES = frozenset({"local", "duckdb"})
518
+
519
+ ALLOW_LOCAL_FILES_ENV = "DATACONTRACT_CLI_API_ALLOW_LOCAL_FILES"
520
+ _TRUTHY = frozenset({"1", "true", "yes", "on"})
521
+
522
+
523
+ def _local_files_allowed() -> bool:
524
+ """Whether the operator opted in to serving data from the server's own disk.
525
+
526
+ Off by default. A deployment that mounts the data next to the API -- the data
527
+ directory in the container, say -- turns it on, accepting that a caller then
528
+ picks the path.
529
+ """
530
+ return (os.getenv(ALLOW_LOCAL_FILES_ENV) or "").strip().lower() in _TRUTHY
531
+
532
+
533
+ def _reject_local_server_type(body: str, server_name: str | None, config) -> None:
534
+ """Keep a posted contract from reading the server's own disk.
535
+
536
+ `type: local` points the test at a path on the machine running it. For the
537
+ API server that machine is not the caller's, so the path is not theirs to
538
+ name -- and the result would tell them what is in it.
539
+ """
540
+ from datacontract.config import Config
541
+
542
+ if _local_files_allowed():
543
+ return
544
+ server = _selected_server(body, server_name, Config.resolve(config))
545
+ if server is None or server.type not in _LOCAL_SERVER_TYPES:
546
+ return
547
+ raise HTTPException(
548
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
549
+ detail=(
550
+ f"Server type '{server.type}' reads from the file system of the server running this API, "
551
+ f"so it is refused. Use a server type that names a data source, such as s3, gcs, azure, "
552
+ f"postgres, or snowflake — or set {ALLOW_LOCAL_FILES_ENV}=true if this deployment serves "
553
+ f"its own files on purpose."
554
+ ),
555
+ )
556
+
557
+
558
+ def _reject_environment_credentials_to_untrusted_host(body: str, server_name: str | None, config) -> None:
559
+ """Keep an environment-held data-source credential from reaching a host the
560
+ posted contract chose.
561
+
562
+ A contract that arrives over HTTP is untrusted. If it could name the
563
+ connection host (its ``servers`` section) while the server supplies the
564
+ password from its own environment, an attacker could point the host at their
565
+ own machine and capture that password. So when a credential for the selected
566
+ server's type resolves from the environment, the connection target must
567
+ resolve from the environment too -- not from the contract, and not from a
568
+ per-request header (a caller who brings the whole credential set may still
569
+ aim it wherever they like; only the server's environment secret is pinned).
570
+ """
571
+ from datacontract.config import Config
572
+ from datacontract.config.settings import env_name
573
+
574
+ resolved = Config.resolve(config)
575
+ server = _selected_server(body, server_name, resolved)
576
+ if server is None:
577
+ return
578
+ guard = _ENVIRONMENT_CREDENTIAL_TARGETS.get(server.type)
579
+ if guard is None:
580
+ return
581
+ target_attr, host_field, secret_fields = guard
582
+ if not any(resolved.option_source(field) == "env" for field in secret_fields):
583
+ return # no environment-held secret for this server type; nothing to protect
584
+ if host_field is not None and resolved.option_source(host_field) == "env":
585
+ return # the operator pinned the target host; the contract's host is ignored
586
+ if host_field is not None:
587
+ env_var = env_name(host_field, Config.model_fields[host_field])
588
+ detail = (
589
+ f"This server holds a {server.type} credential in its environment, so the connection "
590
+ f"{target_attr} must also come from the server's environment ({env_var}); it will not be "
591
+ f"taken from the posted data contract. Set {env_var}, or send the full set of credentials "
592
+ f"with the request instead of relying on the server's environment."
593
+ )
594
+ else:
595
+ detail = (
596
+ f"This server holds a {server.type} credential in its environment, but its {target_attr} "
597
+ f"can only come from the posted data contract, so the credential cannot be pinned to a "
598
+ f"trusted target. Send the credentials with the request instead of relying on the server's "
599
+ f"environment."
600
+ )
601
+ raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=detail)
602
+
603
+
604
+ # The fields backing the platform API key and host, in the fallback order
605
+ # _get_api_key / _get_host read them (current name first, deprecated synonyms after).
606
+ _PLATFORM_KEY_FIELDS = ("entropy_data_api_key", "datamesh_manager_api_key", "datacontract_manager_api_key")
607
+ _PLATFORM_HOST_FIELDS = ("entropy_data_host", "datamesh_manager_host", "datacontract_manager_host")
608
+
609
+
610
+ def _reject_unpinned_publish_url(publish_url: str | None) -> None:
611
+ """Keep the API server from POSTing test results to a caller-chosen host.
612
+
613
+ `publish_url` names where the finished run — check details, failed-row
614
+ samples — is POSTed, so an arbitrary URL would let a caller aim a blind
615
+ POST into the operator's network and exfiltrate the results. The target
616
+ must be a platform URL as the server's own environment sees it: a
617
+ per-request `entropy-data-host` header does not widen the set, or the
618
+ guard would be the caller's to configure.
619
+ """
620
+ from datacontract.integration.entropy_data import is_platform_url
621
+
622
+ if publish_url is None or is_platform_url(publish_url, None):
623
+ return
624
+ raise HTTPException(
625
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
626
+ detail=(
627
+ "The publish_url must point at the Entropy Data platform. To publish to a self-hosted "
628
+ "deployment, set the environment variable ENTROPY_DATA_HOST on the server running this "
629
+ "API to that host."
630
+ ),
631
+ )
632
+
633
+
634
+ def _reject_request_platform_host_with_environment_key(config) -> None:
635
+ """Keep the environment-held platform API key from following a request-chosen host.
636
+
637
+ The platform host decides where the Entropy Data API key travels, and it can
638
+ be set per request (`entropy-data-host` header). A request that supplies the
639
+ host while the key resolves from the server's environment could therefore
640
+ aim that key at its own machine — same rule as the data sources: an
641
+ environment credential goes only to an environment-pinned destination. A
642
+ caller who brings the key itself may aim it wherever they like.
643
+ """
644
+ from datacontract.config import Config
645
+
646
+ resolved = Config.resolve(config)
647
+ key_source = next(
648
+ (source for field in _PLATFORM_KEY_FIELDS if (source := resolved.option_source(field)) is not None), None
649
+ )
650
+ if key_source != "env":
651
+ return
652
+ if not any(resolved.option_source(field) == "request" for field in _PLATFORM_HOST_FIELDS):
653
+ return
654
+ raise HTTPException(
655
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
656
+ detail=(
657
+ "This server holds an Entropy Data API key in its environment, so the platform host must "
658
+ "also come from the server's environment (ENTROPY_DATA_HOST); it will not be taken from a "
659
+ "request header. Remove the entropy-data-host header, or send the API key with the request "
660
+ "instead of relying on the server's environment."
661
+ ),
662
+ )
663
+
664
+
449
665
  def check_api_key(api_key_header: str | None):
450
666
  correct_api_key = os.getenv("DATACONTRACT_CLI_API_KEY")
451
667
  if correct_api_key is None or correct_api_key == "":
@@ -457,7 +673,7 @@ def check_api_key(api_key_header: str | None):
457
673
  status_code=status.HTTP_401_UNAUTHORIZED,
458
674
  detail="Missing API key. Use Header 'x-api-key' to provide the API key.",
459
675
  )
460
- if api_key_header != correct_api_key:
676
+ if not secrets.compare_digest(api_key_header, correct_api_key):
461
677
  logging.info("The provided API key is not correct.")
462
678
  raise HTTPException(
463
679
  status_code=status.HTTP_403_FORBIDDEN,
@@ -500,8 +716,9 @@ handled. A contract that cannot be parsed is reported the same way, as a failed
500
716
  },
501
717
  },
502
718
  422: {
503
- "description": "`filter` and `filters` were both given, or `filters` is not a JSON object "
504
- "mapping schema name to a SQL predicate.",
719
+ "description": "`filter` and `filters` were both given, `filters` is not a JSON object "
720
+ "mapping schema name to a SQL predicate, or `publish_url` does not point at the Entropy "
721
+ "Data platform or the host configured via `ENTROPY_DATA_HOST`.",
505
722
  "model": UnprocessableEntityResponse,
506
723
  },
507
724
  },
@@ -529,7 +746,9 @@ async def test(
529
746
  publish_url: Annotated[
530
747
  str | None,
531
748
  Query(
532
- description="URL to publish test results. Optional, if you want to publish the test results to a Data Mesh Manager or Data Contract Manager. Example: https://api.datamesh-manager.com/api/test-results",
749
+ description="URL to publish test results. Optional, if you want to publish the test results to a Data Mesh Manager or Data Contract Manager. Example: https://api.datamesh-manager.com/api/test-results. "
750
+ "Must point at the Entropy Data platform, or at the host configured on this server "
751
+ "via the environment variable ENTROPY_DATA_HOST — other hosts are refused with 422.",
533
752
  examples=["https://api.datamesh-manager.com/api/test-results"],
534
753
  ),
535
754
  ] = None,
@@ -560,14 +779,22 @@ async def test(
560
779
  )
561
780
  logging.info("Testing data contract...")
562
781
  logging.info(body)
782
+ config = config_from_headers(request.headers)
783
+ untrusted_contract = getattr(request.app.state, "untrusted_contracts", False)
784
+ if untrusted_contract:
785
+ _reject_unpinned_publish_url(publish_url)
786
+ _reject_request_platform_host_with_environment_key(config)
787
+ _reject_environment_credentials_to_untrusted_host(body, server, config)
788
+ _reject_local_server_type(body, server, config)
563
789
  return DataContract(
564
790
  data_contract_str=body,
565
791
  server=server,
566
792
  publish_url=publish_url,
567
793
  fastapi_url=str(request.url),
568
- config=config_from_headers(request.headers),
794
+ config=config,
569
795
  filter=filter,
570
796
  filters=parsed_filters,
797
+ untrusted_contract=untrusted_contract,
571
798
  ).test()
572
799
 
573
800
 
@@ -663,6 +890,21 @@ class ChangelogResponse(BaseModel):
663
890
  )
664
891
 
665
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
+
666
908
  @app.post(
667
909
  "/changelog",
668
910
  tags=["changelog"],
@@ -705,6 +947,59 @@ async def changelog_endpoint(
705
947
  raise HTTPException(status_code=422, detail=f"Invalid YAML: {e}")
706
948
  except pydantic.ValidationError as e:
707
949
  raise HTTPException(status_code=422, detail=f"Invalid data contract: {e}")
950
+ except DefinitionResolutionError as e:
951
+ # The reason names the host that was contacted and what it answered.
952
+ # Omit for security reasons.
953
+ logging.warning("Definition resolution failed: %s", e)
954
+ raise HTTPException(status_code=422, detail=f"Could not resolve authoritative definition '{e.url}'.")
955
+ except DataContractException as e:
956
+ raise HTTPException(status_code=422, detail=f"Data Contract Validation Failure: {e}")
957
+ finally:
958
+ os.unlink(v1_path)
959
+ os.unlink(v2_path)
960
+
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}")
708
1003
  except DataContractException as e:
709
1004
  raise HTTPException(status_code=422, detail=f"Data Contract Validation Failure: {e}")
710
1005
  finally:
@@ -823,5 +1118,10 @@ def export(
823
1118
  raise HTTPException(status_code=422, detail=f"Invalid YAML: {e}")
824
1119
  except pydantic.ValidationError as e:
825
1120
  raise HTTPException(status_code=422, detail=f"Invalid data contract: {e}")
1121
+ except DefinitionResolutionError as e:
1122
+ # The reason names the host that was contacted and what it answered.
1123
+ # Omit for security reasons.
1124
+ logging.warning("Definition resolution failed: %s", e)
1125
+ raise HTTPException(status_code=422, detail=f"Could not resolve authoritative definition '{e.url}'.")
826
1126
  except DataContractException as e:
827
1127
  raise HTTPException(status_code=422, detail=f"Data Contract Validation Failure: {e}")
@@ -0,0 +1,74 @@
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 = {entry.path for entry in entries if entry.change_type == ChangelogType.added}
27
+ for entry in entries:
28
+ segments = entry.path.split(".")
29
+ if any(".".join(segments[:i]) in added for i in range(1, len(segments))):
30
+ entry.level = BreakingChangeLevel.INFO
31
+ entries_by_prefix = defaultdict(list)
32
+ for entry in entries:
33
+ prefix = ""
34
+ for segment in entry.path.split("."):
35
+ prefix = f"{prefix}.{segment}" if prefix else segment
36
+ entries_by_prefix[prefix].append(entry)
37
+ summary = [self._summarize(entry, entries_by_prefix.get(entry.path, [])) for entry in changelog.summary]
38
+ return BreakingChangeResult(v1=changelog.v1, v2=changelog.v2, summary=summary, entries=entries)
39
+
40
+ def _classify(self, entry: ChangelogEntry) -> BreakingChangeEntry:
41
+ for rule in self._rules:
42
+ evaluation = rule.evaluate(entry)
43
+ if evaluation is not None:
44
+ return BreakingChangeEntry(
45
+ path=entry.path,
46
+ change_type=entry.type,
47
+ level=evaluation.level,
48
+ message=evaluation.message,
49
+ rule_id=evaluation.rule_id,
50
+ old_value=entry.old_value,
51
+ new_value=entry.new_value,
52
+ )
53
+ raise ValueError(f"No breaking-change rule classified {entry.path}")
54
+
55
+ @staticmethod
56
+ def _summarize(summary_entry: ChangelogEntry, entries: list[BreakingChangeEntry]) -> BreakingChangeEntry:
57
+ if not entries:
58
+ return BreakingChangeEntry(
59
+ path=summary_entry.path,
60
+ change_type=summary_entry.type,
61
+ level=BreakingChangeLevel.INFO,
62
+ message=f"Changed contract at {summary_entry.path}",
63
+ rule_id="summary-fallback",
64
+ )
65
+ highest = max(entries, key=lambda entry: _LEVEL_ORDER[entry.level])
66
+ return BreakingChangeEntry(
67
+ path=summary_entry.path,
68
+ change_type=summary_entry.type,
69
+ level=highest.level,
70
+ message=f"{highest.level.value.capitalize()} impact at {summary_entry.path}",
71
+ rule_id=f"summary:{highest.rule_id}",
72
+ old_value=highest.old_value,
73
+ new_value=highest.new_value,
74
+ )