datacontract-cli 1.1.1__tar.gz → 1.1.2__tar.gz

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