datacontract-cli 0.10.34__tar.gz → 0.10.35__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.

Potentially problematic release.


This version of datacontract-cli might be problematic. Click here for more details.

Files changed (223) hide show
  1. {datacontract_cli-0.10.34/datacontract_cli.egg-info → datacontract_cli-0.10.35}/PKG-INFO +44 -35
  2. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/README.md +38 -29
  3. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/api.py +9 -2
  4. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/cli.py +4 -2
  5. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/fastjsonschema/check_jsonschema.py +29 -19
  6. datacontract_cli-0.10.35/datacontract/export/dqx_converter.py +121 -0
  7. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/exporter.py +1 -0
  8. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/exporter_factory.py +6 -0
  9. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/mermaid_exporter.py +24 -11
  10. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/spark_converter.py +28 -3
  11. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/odcs_v3_importer.py +29 -1
  12. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35/datacontract_cli.egg-info}/PKG-INFO +44 -35
  13. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract_cli.egg-info/SOURCES.txt +3 -0
  14. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract_cli.egg-info/requires.txt +5 -5
  15. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/pyproject.toml +6 -6
  16. datacontract_cli-0.10.35/tests/test_export_dqx.py +319 -0
  17. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_spark.py +3 -2
  18. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_spark.py +1 -1
  19. datacontract_cli-0.10.35/tests/test_test_local_json_nd.py +19 -0
  20. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/LICENSE +0 -0
  21. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/MANIFEST.in +0 -0
  22. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/__init__.py +0 -0
  23. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/breaking/breaking.py +0 -0
  24. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/breaking/breaking_change.py +0 -0
  25. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/breaking/breaking_rules.py +0 -0
  26. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/catalog/catalog.py +0 -0
  27. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/data_contract.py +0 -0
  28. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/__init__.py +0 -0
  29. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/data_contract_checks.py +0 -0
  30. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/data_contract_test.py +0 -0
  31. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/datacontract/check_that_datacontract_contains_valid_servers_configuration.py +0 -0
  32. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/datacontract/check_that_datacontract_file_exists.py +0 -0
  33. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/fastjsonschema/s3/s3_read_files.py +0 -0
  34. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/soda/__init__.py +0 -0
  35. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/soda/check_soda_execute.py +0 -0
  36. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/soda/connections/athena.py +0 -0
  37. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/soda/connections/bigquery.py +0 -0
  38. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/soda/connections/databricks.py +0 -0
  39. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/soda/connections/duckdb_connection.py +0 -0
  40. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/soda/connections/kafka.py +0 -0
  41. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/soda/connections/postgres.py +0 -0
  42. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/soda/connections/snowflake.py +0 -0
  43. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/soda/connections/sqlserver.py +0 -0
  44. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/engines/soda/connections/trino.py +0 -0
  45. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/__init__.py +0 -0
  46. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/avro_converter.py +0 -0
  47. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/avro_idl_converter.py +0 -0
  48. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/bigquery_converter.py +0 -0
  49. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/custom_converter.py +0 -0
  50. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/data_caterer_converter.py +0 -0
  51. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/dbml_converter.py +0 -0
  52. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/dbt_converter.py +0 -0
  53. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/dcs_exporter.py +0 -0
  54. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/duckdb_type_converter.py +0 -0
  55. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/excel_exporter.py +0 -0
  56. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/go_converter.py +0 -0
  57. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/great_expectations_converter.py +0 -0
  58. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/html_exporter.py +0 -0
  59. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/iceberg_converter.py +0 -0
  60. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/jsonschema_converter.py +0 -0
  61. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/markdown_converter.py +0 -0
  62. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/odcs_v3_exporter.py +0 -0
  63. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/pandas_type_converter.py +0 -0
  64. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/protobuf_converter.py +0 -0
  65. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/pydantic_converter.py +0 -0
  66. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/rdf_converter.py +0 -0
  67. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/sodacl_converter.py +0 -0
  68. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/sql_converter.py +0 -0
  69. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/sql_type_converter.py +0 -0
  70. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/sqlalchemy_converter.py +0 -0
  71. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/export/terraform_converter.py +0 -0
  72. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/avro_importer.py +0 -0
  73. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/bigquery_importer.py +0 -0
  74. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/csv_importer.py +0 -0
  75. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/dbml_importer.py +0 -0
  76. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/dbt_importer.py +0 -0
  77. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/excel_importer.py +0 -0
  78. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/glue_importer.py +0 -0
  79. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/iceberg_importer.py +0 -0
  80. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/importer.py +0 -0
  81. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/importer_factory.py +0 -0
  82. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/json_importer.py +0 -0
  83. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/jsonschema_importer.py +0 -0
  84. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/odcs_importer.py +0 -0
  85. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/parquet_importer.py +0 -0
  86. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/protobuf_importer.py +0 -0
  87. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/spark_importer.py +0 -0
  88. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/sql_importer.py +0 -0
  89. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/imports/unity_importer.py +0 -0
  90. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/init/init_template.py +0 -0
  91. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/integration/datamesh_manager.py +0 -0
  92. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/lint/files.py +0 -0
  93. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/lint/lint.py +0 -0
  94. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/lint/linters/__init__.py +0 -0
  95. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/lint/linters/description_linter.py +0 -0
  96. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/lint/linters/field_pattern_linter.py +0 -0
  97. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/lint/linters/field_reference_linter.py +0 -0
  98. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/lint/linters/notice_period_linter.py +0 -0
  99. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/lint/linters/valid_constraints_linter.py +0 -0
  100. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/lint/resolve.py +0 -0
  101. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/lint/resources.py +0 -0
  102. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/lint/schema.py +0 -0
  103. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/lint/urls.py +0 -0
  104. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/model/data_contract_specification/__init__.py +0 -0
  105. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/model/exceptions.py +0 -0
  106. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/model/odcs.py +0 -0
  107. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/model/run.py +0 -0
  108. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/output/__init__.py +0 -0
  109. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/output/junit_test_results.py +0 -0
  110. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/output/output_format.py +0 -0
  111. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/output/test_results_writer.py +0 -0
  112. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/py.typed +0 -0
  113. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/schemas/datacontract-1.1.0.init.yaml +0 -0
  114. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/schemas/datacontract-1.1.0.schema.json +0 -0
  115. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/schemas/datacontract-1.2.0.init.yaml +0 -0
  116. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/schemas/datacontract-1.2.0.schema.json +0 -0
  117. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/schemas/odcs-3.0.1.schema.json +0 -0
  118. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/templates/datacontract.html +0 -0
  119. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/templates/datacontract_odcs.html +0 -0
  120. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/templates/index.html +0 -0
  121. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/templates/partials/datacontract_information.html +0 -0
  122. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/templates/partials/datacontract_servicelevels.html +0 -0
  123. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/templates/partials/datacontract_terms.html +0 -0
  124. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/templates/partials/definition.html +0 -0
  125. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/templates/partials/example.html +0 -0
  126. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/templates/partials/model_field.html +0 -0
  127. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/templates/partials/quality.html +0 -0
  128. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/templates/partials/server.html +0 -0
  129. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract/templates/style/output.css +0 -0
  130. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract_cli.egg-info/dependency_links.txt +0 -0
  131. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract_cli.egg-info/entry_points.txt +0 -0
  132. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/datacontract_cli.egg-info/top_level.txt +0 -0
  133. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/setup.cfg +0 -0
  134. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_api.py +0 -0
  135. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_breaking.py +0 -0
  136. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_catalog.py +0 -0
  137. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_changelog.py +0 -0
  138. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_cli.py +0 -0
  139. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_data_contract_checks.py +0 -0
  140. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_data_contract_specification.py +0 -0
  141. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_description_linter.py +0 -0
  142. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_documentation_linter.py +0 -0
  143. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_download_datacontract_file.py +0 -0
  144. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_duckdb_json.py +0 -0
  145. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_avro.py +0 -0
  146. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_avro_idl.py +0 -0
  147. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_bigquery.py +0 -0
  148. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_complex_data_contract.py +0 -0
  149. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_custom.py +0 -0
  150. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_custom_exporter.py +0 -0
  151. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_data_caterer.py +0 -0
  152. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_dbml.py +0 -0
  153. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_dbt_models.py +0 -0
  154. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_dbt_sources.py +0 -0
  155. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_dbt_staging_sql.py +0 -0
  156. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_excel.py +0 -0
  157. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_go.py +0 -0
  158. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_great_expectations.py +0 -0
  159. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_html.py +0 -0
  160. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_iceberg.py +0 -0
  161. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_jsonschema.py +0 -0
  162. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_markdown.py +0 -0
  163. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_mermaid.py +0 -0
  164. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_odcs_v3.py +0 -0
  165. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_protobuf.py +0 -0
  166. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_pydantic.py +0 -0
  167. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_rdf.py +0 -0
  168. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_sodacl.py +0 -0
  169. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_sql.py +0 -0
  170. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_sql_query.py +0 -0
  171. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_sqlalchemy.py +0 -0
  172. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_export_terraform.py +0 -0
  173. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_field_constraint_linter.py +0 -0
  174. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_field_pattern_linter.py +0 -0
  175. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_field_reference_linter.py +0 -0
  176. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_avro.py +0 -0
  177. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_bigquery.py +0 -0
  178. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_csv.py +0 -0
  179. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_dbml.py +0 -0
  180. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_dbt.py +0 -0
  181. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_excel.py +0 -0
  182. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_glue.py +0 -0
  183. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_iceberg.py +0 -0
  184. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_json.py +0 -0
  185. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_jsonschema.py +0 -0
  186. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_odcs_v3.py +0 -0
  187. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_parquet.py +0 -0
  188. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_protobuf.py +0 -0
  189. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_sql_postgres.py +0 -0
  190. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_sql_sqlserver.py +0 -0
  191. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_import_unity_file.py +0 -0
  192. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_integration_datameshmanager.py +0 -0
  193. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_lint.py +0 -0
  194. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_notice_period_linter.py +0 -0
  195. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_resolve.py +0 -0
  196. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_roundtrip_jsonschema.py +0 -0
  197. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_spec_fields_field.py +0 -0
  198. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_spec_ref.py +0 -0
  199. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_api.py +0 -0
  200. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_athena_iceberg.py +0 -0
  201. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_azure_remote.py +0 -0
  202. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_bigquery.py +0 -0
  203. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_databricks.py +0 -0
  204. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_dataframe.py +0 -0
  205. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_delta.py +0 -0
  206. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_gcs_csv_remote.py +0 -0
  207. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_gcs_json_remote.py +0 -0
  208. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_kafka.py +0 -0
  209. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_kafka_remote.py +0 -0
  210. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_local_json.py +0 -0
  211. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_output_junit.py +0 -0
  212. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_parquet.py +0 -0
  213. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_postgres.py +0 -0
  214. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_quality.py +0 -0
  215. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_s3_csv.py +0 -0
  216. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_s3_delta.py +0 -0
  217. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_s3_json.py +0 -0
  218. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_s3_json_complex.py +0 -0
  219. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_s3_json_multiple_models.py +0 -0
  220. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_s3_json_remote.py +0 -0
  221. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_snowflake.py +0 -0
  222. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_sqlserver.py +0 -0
  223. {datacontract_cli-0.10.34 → datacontract_cli-0.10.35}/tests/test_test_trino.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datacontract-cli
3
- Version: 0.10.34
3
+ Version: 0.10.35
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@innoq.com>, Stefan Negele <stefan.negele@innoq.com>, Simon Harrer <simon.harrer@innoq.com>
6
6
  License-Expression: MIT
@@ -42,7 +42,7 @@ Provides-Extra: databricks
42
42
  Requires-Dist: soda-core-spark-df<3.6.0,>=3.3.20; extra == "databricks"
43
43
  Requires-Dist: soda-core-spark[databricks]<3.6.0,>=3.3.20; extra == "databricks"
44
44
  Requires-Dist: databricks-sql-connector<4.1.0,>=3.7.0; extra == "databricks"
45
- Requires-Dist: databricks-sdk<0.61.0; extra == "databricks"
45
+ Requires-Dist: databricks-sdk<0.64.0; extra == "databricks"
46
46
  Requires-Dist: pyspark<4.0.0,>=3.5.5; extra == "databricks"
47
47
  Provides-Extra: iceberg
48
48
  Requires-Dist: pyiceberg==0.9.1; extra == "iceberg"
@@ -54,7 +54,7 @@ Provides-Extra: postgres
54
54
  Requires-Dist: soda-core-postgres<3.6.0,>=3.3.20; extra == "postgres"
55
55
  Provides-Extra: s3
56
56
  Requires-Dist: s3fs<2026.0.0,>=2025.2.0; extra == "s3"
57
- Requires-Dist: aiobotocore<2.24.0,>=2.17.0; extra == "s3"
57
+ Requires-Dist: aiobotocore<2.25.0,>=2.17.0; extra == "s3"
58
58
  Provides-Extra: snowflake
59
59
  Requires-Dist: snowflake-connector-python[pandas]<3.17,>=3.6; extra == "snowflake"
60
60
  Requires-Dist: soda-core-snowflake<3.6.0,>=3.3.20; extra == "snowflake"
@@ -83,15 +83,15 @@ Provides-Extra: dev
83
83
  Requires-Dist: datacontract-cli[all]; extra == "dev"
84
84
  Requires-Dist: httpx==0.28.1; extra == "dev"
85
85
  Requires-Dist: kafka-python; extra == "dev"
86
- Requires-Dist: moto==5.1.8; extra == "dev"
86
+ Requires-Dist: moto==5.1.10; extra == "dev"
87
87
  Requires-Dist: pandas>=2.1.0; extra == "dev"
88
- Requires-Dist: pre-commit<4.3.0,>=3.7.1; extra == "dev"
88
+ Requires-Dist: pre-commit<4.4.0,>=3.7.1; extra == "dev"
89
89
  Requires-Dist: pytest; extra == "dev"
90
90
  Requires-Dist: pytest-xdist; extra == "dev"
91
91
  Requires-Dist: pymssql==2.3.7; extra == "dev"
92
92
  Requires-Dist: ruff; extra == "dev"
93
93
  Requires-Dist: testcontainers[kafka,minio,mssql,postgres]==4.12.0; extra == "dev"
94
- Requires-Dist: trino==0.335.0; extra == "dev"
94
+ Requires-Dist: trino==0.336.0; extra == "dev"
95
95
  Dynamic: license-file
96
96
 
97
97
  # Data Contract CLI
@@ -257,6 +257,14 @@ if not run.has_passed():
257
257
 
258
258
  Choose the most appropriate installation method for your needs:
259
259
 
260
+ ### uv
261
+
262
+ If you have [uv](https://docs.astral.sh/uv/) installed, you can run datacontract-cli directly without installing:
263
+
264
+ ```
265
+ uv run --with 'datacontract-cli[all]' datacontract --version
266
+ ```
267
+
260
268
  ### pip
261
269
  Python 3.10, 3.11, and 3.12 are supported. We recommend to use Python 3.11.
262
270
 
@@ -1011,7 +1019,7 @@ models:
1011
1019
  │ terraform|avro-idl|sql|sql-query|mer │
1012
1020
  │ maid|html|go|bigquery|dbml|spark|sql │
1013
1021
  │ alchemy|data-caterer|dcs|markdown|ic │
1014
- │ eberg|custom|excel] │
1022
+ │ eberg|custom|excel|dqx] │
1015
1023
  │ --output PATH Specify the file path where the │
1016
1024
  │ exported data will be saved. If no │
1017
1025
  │ path is provided, the output will be │
@@ -1057,35 +1065,36 @@ datacontract export --format html --output datacontract.html
1057
1065
 
1058
1066
  Available export options:
1059
1067
 
1060
- | Type | Description | Status |
1061
- |----------------------|---------------------------------------------------------|--------|
1062
- | `html` | Export to HTML | ✅ |
1063
- | `jsonschema` | Export to JSON Schema | ✅ |
1064
- | `odcs` | Export to Open Data Contract Standard (ODCS) V3 | ✅ |
1065
- | `sodacl` | Export to SodaCL quality checks in YAML format | ✅ |
1066
- | `dbt` | Export to dbt models in YAML format | ✅ |
1067
- | `dbt-sources` | Export to dbt sources in YAML format | ✅ |
1068
- | `dbt-staging-sql` | Export to dbt staging SQL models | ✅ |
1069
- | `rdf` | Export data contract to RDF representation in N3 format | ✅ |
1070
- | `avro` | Export to AVRO models | ✅ |
1071
- | `protobuf` | Export to Protobuf | ✅ |
1072
- | `terraform` | Export to terraform resources | ✅ |
1073
- | `sql` | Export to SQL DDL | ✅ |
1074
- | `sql-query` | Export to SQL Query | ✅ |
1075
- | `great-expectations` | Export to Great Expectations Suites in JSON Format | ✅ |
1076
- | `bigquery` | Export to BigQuery Schemas | ✅ |
1077
- | `go` | Export to Go types | ✅ |
1078
- | `pydantic-model` | Export to pydantic models | ✅ |
1079
- | `DBML` | Export to a DBML Diagram description | ✅ |
1080
- | `spark` | Export to a Spark StructType | ✅ |
1081
- | `sqlalchemy` | Export to SQLAlchemy Models | ✅ |
1082
- | `data-caterer` | Export to Data Caterer in YAML format | ✅ |
1083
- | `dcs` | Export to Data Contract Specification in YAML format | ✅ |
1084
- | `markdown` | Export to Markdown | ✅ |
1068
+ | Type | Description | Status |
1069
+ |----------------------|---------------------------------------------------------|---------|
1070
+ | `html` | Export to HTML | ✅ |
1071
+ | `jsonschema` | Export to JSON Schema | ✅ |
1072
+ | `odcs` | Export to Open Data Contract Standard (ODCS) V3 | ✅ |
1073
+ | `sodacl` | Export to SodaCL quality checks in YAML format | ✅ |
1074
+ | `dbt` | Export to dbt models in YAML format | ✅ |
1075
+ | `dbt-sources` | Export to dbt sources in YAML format | ✅ |
1076
+ | `dbt-staging-sql` | Export to dbt staging SQL models | ✅ |
1077
+ | `rdf` | Export data contract to RDF representation in N3 format | ✅ |
1078
+ | `avro` | Export to AVRO models | ✅ |
1079
+ | `protobuf` | Export to Protobuf | ✅ |
1080
+ | `terraform` | Export to terraform resources | ✅ |
1081
+ | `sql` | Export to SQL DDL | ✅ |
1082
+ | `sql-query` | Export to SQL Query | ✅ |
1083
+ | `great-expectations` | Export to Great Expectations Suites in JSON Format | ✅ |
1084
+ | `bigquery` | Export to BigQuery Schemas | ✅ |
1085
+ | `go` | Export to Go types | ✅ |
1086
+ | `pydantic-model` | Export to pydantic models | ✅ |
1087
+ | `DBML` | Export to a DBML Diagram description | ✅ |
1088
+ | `spark` | Export to a Spark StructType | ✅ |
1089
+ | `sqlalchemy` | Export to SQLAlchemy Models | ✅ |
1090
+ | `data-caterer` | Export to Data Caterer in YAML format | ✅ |
1091
+ | `dcs` | Export to Data Contract Specification in YAML format | ✅ |
1092
+ | `markdown` | Export to Markdown | ✅ |
1085
1093
  | `iceberg` | Export to an Iceberg JSON Schema Definition | partial |
1086
- | `excel` | Export to ODCS Excel Template | ✅ |
1087
- | `custom` | Export to Custom format with Jinja | ✅ |
1088
- | Missing something? | Please create an issue on GitHub | TBD |
1094
+ | `excel` | Export to ODCS Excel Template | ✅ |
1095
+ | `custom` | Export to Custom format with Jinja | ✅ |
1096
+ | `dqx` | Export to DQX in YAML format | |
1097
+ | Missing something? | Please create an issue on GitHub | TBD |
1089
1098
 
1090
1099
  #### SQL
1091
1100
 
@@ -161,6 +161,14 @@ if not run.has_passed():
161
161
 
162
162
  Choose the most appropriate installation method for your needs:
163
163
 
164
+ ### uv
165
+
166
+ If you have [uv](https://docs.astral.sh/uv/) installed, you can run datacontract-cli directly without installing:
167
+
168
+ ```
169
+ uv run --with 'datacontract-cli[all]' datacontract --version
170
+ ```
171
+
164
172
  ### pip
165
173
  Python 3.10, 3.11, and 3.12 are supported. We recommend to use Python 3.11.
166
174
 
@@ -915,7 +923,7 @@ models:
915
923
  │ terraform|avro-idl|sql|sql-query|mer │
916
924
  │ maid|html|go|bigquery|dbml|spark|sql │
917
925
  │ alchemy|data-caterer|dcs|markdown|ic │
918
- │ eberg|custom|excel] │
926
+ │ eberg|custom|excel|dqx] │
919
927
  │ --output PATH Specify the file path where the │
920
928
  │ exported data will be saved. If no │
921
929
  │ path is provided, the output will be │
@@ -961,35 +969,36 @@ datacontract export --format html --output datacontract.html
961
969
 
962
970
  Available export options:
963
971
 
964
- | Type | Description | Status |
965
- |----------------------|---------------------------------------------------------|--------|
966
- | `html` | Export to HTML | ✅ |
967
- | `jsonschema` | Export to JSON Schema | ✅ |
968
- | `odcs` | Export to Open Data Contract Standard (ODCS) V3 | ✅ |
969
- | `sodacl` | Export to SodaCL quality checks in YAML format | ✅ |
970
- | `dbt` | Export to dbt models in YAML format | ✅ |
971
- | `dbt-sources` | Export to dbt sources in YAML format | ✅ |
972
- | `dbt-staging-sql` | Export to dbt staging SQL models | ✅ |
973
- | `rdf` | Export data contract to RDF representation in N3 format | ✅ |
974
- | `avro` | Export to AVRO models | ✅ |
975
- | `protobuf` | Export to Protobuf | ✅ |
976
- | `terraform` | Export to terraform resources | ✅ |
977
- | `sql` | Export to SQL DDL | ✅ |
978
- | `sql-query` | Export to SQL Query | ✅ |
979
- | `great-expectations` | Export to Great Expectations Suites in JSON Format | ✅ |
980
- | `bigquery` | Export to BigQuery Schemas | ✅ |
981
- | `go` | Export to Go types | ✅ |
982
- | `pydantic-model` | Export to pydantic models | ✅ |
983
- | `DBML` | Export to a DBML Diagram description | ✅ |
984
- | `spark` | Export to a Spark StructType | ✅ |
985
- | `sqlalchemy` | Export to SQLAlchemy Models | ✅ |
986
- | `data-caterer` | Export to Data Caterer in YAML format | ✅ |
987
- | `dcs` | Export to Data Contract Specification in YAML format | ✅ |
988
- | `markdown` | Export to Markdown | ✅ |
972
+ | Type | Description | Status |
973
+ |----------------------|---------------------------------------------------------|---------|
974
+ | `html` | Export to HTML | ✅ |
975
+ | `jsonschema` | Export to JSON Schema | ✅ |
976
+ | `odcs` | Export to Open Data Contract Standard (ODCS) V3 | ✅ |
977
+ | `sodacl` | Export to SodaCL quality checks in YAML format | ✅ |
978
+ | `dbt` | Export to dbt models in YAML format | ✅ |
979
+ | `dbt-sources` | Export to dbt sources in YAML format | ✅ |
980
+ | `dbt-staging-sql` | Export to dbt staging SQL models | ✅ |
981
+ | `rdf` | Export data contract to RDF representation in N3 format | ✅ |
982
+ | `avro` | Export to AVRO models | ✅ |
983
+ | `protobuf` | Export to Protobuf | ✅ |
984
+ | `terraform` | Export to terraform resources | ✅ |
985
+ | `sql` | Export to SQL DDL | ✅ |
986
+ | `sql-query` | Export to SQL Query | ✅ |
987
+ | `great-expectations` | Export to Great Expectations Suites in JSON Format | ✅ |
988
+ | `bigquery` | Export to BigQuery Schemas | ✅ |
989
+ | `go` | Export to Go types | ✅ |
990
+ | `pydantic-model` | Export to pydantic models | ✅ |
991
+ | `DBML` | Export to a DBML Diagram description | ✅ |
992
+ | `spark` | Export to a Spark StructType | ✅ |
993
+ | `sqlalchemy` | Export to SQLAlchemy Models | ✅ |
994
+ | `data-caterer` | Export to Data Caterer in YAML format | ✅ |
995
+ | `dcs` | Export to Data Contract Specification in YAML format | ✅ |
996
+ | `markdown` | Export to Markdown | ✅ |
989
997
  | `iceberg` | Export to an Iceberg JSON Schema Definition | partial |
990
- | `excel` | Export to ODCS Excel Template | ✅ |
991
- | `custom` | Export to Custom format with Jinja | ✅ |
992
- | Missing something? | Please create an issue on GitHub | TBD |
998
+ | `excel` | Export to ODCS Excel Template | ✅ |
999
+ | `custom` | Export to Custom format with Jinja | ✅ |
1000
+ | `dqx` | Export to DQX in YAML format | |
1001
+ | Missing something? | Please create an issue on GitHub | TBD |
993
1002
 
994
1003
  #### SQL
995
1004
 
@@ -162,15 +162,22 @@ async def test(
162
162
  server: Annotated[
163
163
  str | None,
164
164
  Query(
165
- examples=["production"],
166
165
  description="The server name to test. Optional, if there is only one server.",
166
+ examples=["production"],
167
+ ),
168
+ ] = None,
169
+ publish_url: Annotated[
170
+ str | None,
171
+ Query(
172
+ 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",
173
+ examples=["https://api.datamesh-manager.com/api/test-results"],
167
174
  ),
168
175
  ] = None,
169
176
  ) -> Run:
170
177
  check_api_key(api_key)
171
178
  logging.info("Testing data contract...")
172
179
  logging.info(body)
173
- return DataContract(data_contract_str=body, server=server).test()
180
+ return DataContract(data_contract_str=body, server=server, publish_url=publish_url).test()
174
181
 
175
182
 
176
183
  @app.post(
@@ -126,8 +126,10 @@ def test(
126
126
  "servers (default)."
127
127
  ),
128
128
  ] = "all",
129
- publish_test_results: Annotated[bool, typer.Option(help="Publish the results after the test")] = False,
130
- publish: Annotated[str, typer.Option(help="DEPRECATED. The url to publish the results after the test.")] = None,
129
+ publish_test_results: Annotated[
130
+ bool, typer.Option(help="Deprecated. Use publish parameter. Publish the results after the test")
131
+ ] = False,
132
+ publish: Annotated[str, typer.Option(help="The url to publish the results after the test.")] = None,
131
133
  output: Annotated[
132
134
  Path,
133
135
  typer.Option(
@@ -1,8 +1,9 @@
1
+ import glob
1
2
  import json
2
3
  import logging
3
4
  import os
4
5
  import threading
5
- from typing import List, Optional
6
+ from typing import Any, Callable, Generator, List, Optional
6
7
 
7
8
  import fastjsonschema
8
9
  from fastjsonschema import JsonSchemaValueException
@@ -85,7 +86,7 @@ def process_exceptions(run, exceptions: List[DataContractException]):
85
86
 
86
87
 
87
88
  def validate_json_stream(
88
- schema: dict, model_name: str, validate: callable, json_stream: list[dict]
89
+ schema: dict, model_name: str, validate: Callable, json_stream: Generator[Any, Any, None]
89
90
  ) -> List[DataContractException]:
90
91
  logging.info(f"Validating JSON stream for model: '{model_name}'.")
91
92
  exceptions: List[DataContractException] = []
@@ -99,7 +100,7 @@ def validate_json_stream(
99
100
  DataContractException(
100
101
  type="schema",
101
102
  name="Check that JSON has valid schema",
102
- result="failed",
103
+ result=ResultEnum.failed,
103
104
  reason=f"{f'#{primary_key_value}: ' if primary_key_value is not None else ''}{e.message}",
104
105
  model=model_name,
105
106
  engine="jsonschema",
@@ -170,24 +171,33 @@ def process_local_file(run, server, schema, model_name, validate):
170
171
  if "{model}" in path:
171
172
  path = path.format(model=model_name)
172
173
 
174
+ all_files = []
173
175
  if os.path.isdir(path):
174
- return process_directory(run, path, server, model_name, validate)
176
+ # Fetch all JSONs in the directory
177
+ for root, _, files in os.walk(path):
178
+ for file in files:
179
+ if file.endswith(".json"):
180
+ all_files.append(os.path.join(root, file))
175
181
  else:
176
- logging.info(f"Processing file {path}")
177
- with open(path, "r") as file:
178
- process_json_file(run, schema, model_name, validate, file, server.delimiter)
182
+ # Use glob to fetch all JSONs
183
+ for file_path in glob.glob(path, recursive=True):
184
+ if os.path.isfile(file_path):
185
+ if file_path.endswith(".json"):
186
+ all_files.append(file_path)
179
187
 
188
+ if not all_files:
189
+ raise DataContractException(
190
+ type="schema",
191
+ name="Check that JSON has valid schema",
192
+ result=ResultEnum.warning,
193
+ reason=f"No files found in '{path}'.",
194
+ engine="datacontract",
195
+ )
180
196
 
181
- def process_directory(run, path, server, model_name, validate):
182
- success = True
183
- for filename in os.listdir(path):
184
- if filename.endswith(".json"): # or make this a parameter
185
- file_path = os.path.join(path, filename)
186
- with open(file_path, "r") as file:
187
- if not process_json_file(run, model_name, validate, file, server.delimiter):
188
- success = False
189
- break
190
- return success
197
+ for file in all_files:
198
+ logging.info(f"Processing file: {file}")
199
+ with open(file, "r") as f:
200
+ process_json_file(run, schema, model_name, validate, f, server.delimiter)
191
201
 
192
202
 
193
203
  def process_s3_file(run, server, schema, model_name, validate):
@@ -209,7 +219,7 @@ def process_s3_file(run, server, schema, model_name, validate):
209
219
  raise DataContractException(
210
220
  type="schema",
211
221
  name="Check that JSON has valid schema",
212
- result="warning",
222
+ result=ResultEnum.warning,
213
223
  reason=f"Cannot find any file in {s3_location}",
214
224
  engine="datacontract",
215
225
  )
@@ -230,7 +240,7 @@ def check_jsonschema(run: Run, data_contract: DataContractSpecification, server:
230
240
  Check(
231
241
  type="schema",
232
242
  name="Check that JSON has valid schema",
233
- result="warning",
243
+ result=ResultEnum.warning,
234
244
  reason="Server format is not 'json'. Skip validating jsonschema.",
235
245
  engine="jsonschema",
236
246
  )
@@ -0,0 +1,121 @@
1
+ from typing import Any, Dict, List, Union
2
+
3
+ import yaml
4
+
5
+ from datacontract.export.exporter import Exporter, _check_models_for_export
6
+ from datacontract.model.data_contract_specification import DataContractSpecification, Field, Model, Quality
7
+
8
+
9
+ class DqxKeys:
10
+ CHECK = "check"
11
+ ARGUMENTS = "arguments"
12
+ SPECIFICATION = "specification"
13
+ COL_NAME = "column"
14
+ COL_NAMES = "for_each_column"
15
+ COLUMNS = "columns"
16
+ FUNCTION = "function"
17
+
18
+
19
+ class DqxExporter(Exporter):
20
+ """Exporter implementation for converting data contracts to DQX YAML file."""
21
+
22
+ def export(
23
+ self,
24
+ data_contract: DataContractSpecification,
25
+ model: Model,
26
+ server: str,
27
+ sql_server_type: str,
28
+ export_args: Dict[str, Any],
29
+ ) -> str:
30
+ """Exports a data contract to DQX format."""
31
+ model_name, model_value = _check_models_for_export(data_contract, model, self.export_format)
32
+ return to_dqx_yaml(model_value)
33
+
34
+
35
+ def to_dqx_yaml(model_value: Model) -> str:
36
+ """
37
+ Converts the data contract's quality checks to DQX YAML format.
38
+
39
+ Args:
40
+ model_value (Model): The data contract to convert.
41
+
42
+ Returns:
43
+ str: YAML representation of the data contract's quality checks.
44
+ """
45
+ extracted_rules = extract_quality_rules(model_value)
46
+ return yaml.dump(extracted_rules, sort_keys=False, allow_unicode=True, default_flow_style=False)
47
+
48
+
49
+ def process_quality_rule(rule: Quality, column_name: str) -> Dict[str, Any]:
50
+ """
51
+ Processes a single quality rule by injecting the column path into its arguments if absent.
52
+
53
+ Args:
54
+ rule (Quality): The quality rule to process.
55
+ column_name (str): The full path to the current column.
56
+
57
+ Returns:
58
+ dict: The processed quality rule specification.
59
+ """
60
+ rule_data = rule.model_extra
61
+ specification = rule_data[DqxKeys.SPECIFICATION]
62
+ check = specification[DqxKeys.CHECK]
63
+
64
+ arguments = check.setdefault(DqxKeys.ARGUMENTS, {})
65
+
66
+ if DqxKeys.COL_NAME not in arguments and DqxKeys.COL_NAMES not in arguments and DqxKeys.COLUMNS not in arguments:
67
+ if check[DqxKeys.FUNCTION] not in ("is_unique", "foreign_key"):
68
+ arguments[DqxKeys.COL_NAME] = column_name
69
+ else:
70
+ arguments[DqxKeys.COLUMNS] = [column_name]
71
+
72
+ return specification
73
+
74
+
75
+ def extract_quality_rules(data: Union[Model, Field, Quality], column_path: str = "") -> List[Dict[str, Any]]:
76
+ """
77
+ Recursively extracts all quality rules from a data contract structure.
78
+
79
+ Args:
80
+ data (Union[Model, Field, Quality]): The data contract model, field, or quality rule.
81
+ column_path (str, optional): The current path in the schema hierarchy. Defaults to "".
82
+
83
+ Returns:
84
+ List[Dict[str, Any]]: A list of quality rule specifications.
85
+ """
86
+ quality_rules = []
87
+
88
+ if isinstance(data, Quality):
89
+ return [process_quality_rule(data, column_path)]
90
+
91
+ if isinstance(data, (Model, Field)):
92
+ for key, field in data.fields.items():
93
+ current_path = build_column_path(column_path, key)
94
+
95
+ if field.fields:
96
+ # Field is a struct-like object, recurse deeper
97
+ quality_rules.extend(extract_quality_rules(field, current_path))
98
+ else:
99
+ # Process quality rules at leaf fields
100
+ for rule in field.quality:
101
+ quality_rules.append(process_quality_rule(rule, current_path))
102
+
103
+ # Process any quality rules attached directly to this level
104
+ for rule in data.quality:
105
+ quality_rules.append(process_quality_rule(rule, column_path))
106
+
107
+ return quality_rules
108
+
109
+
110
+ def build_column_path(current_path: str, key: str) -> str:
111
+ """
112
+ Builds the full column path by concatenating parent path with current key.
113
+
114
+ Args:
115
+ current_path (str): The current path prefix.
116
+ key (str): The current field's key.
117
+
118
+ Returns:
119
+ str: The full path.
120
+ """
121
+ return f"{current_path}.{key}" if current_path else key
@@ -46,6 +46,7 @@ class ExportFormat(str, Enum):
46
46
  iceberg = "iceberg"
47
47
  custom = "custom"
48
48
  excel = "excel"
49
+ dqx = "dqx"
49
50
 
50
51
  @classmethod
51
52
  def get_supported_formats(cls):
@@ -197,6 +197,12 @@ exporter_factory.register_lazy_exporter(
197
197
  class_name="MarkdownExporter",
198
198
  )
199
199
 
200
+ exporter_factory.register_lazy_exporter(
201
+ name=ExportFormat.dqx,
202
+ module_path="datacontract.export.dqx_converter",
203
+ class_name="DqxExporter",
204
+ )
205
+
200
206
  exporter_factory.register_lazy_exporter(
201
207
  name=ExportFormat.iceberg, module_path="datacontract.export.iceberg_converter", class_name="IcebergExporter"
202
208
  )
@@ -27,31 +27,33 @@ def dcs_to_mermaid(data_contract_spec: DataContractSpecification) -> str | None:
27
27
  mmd_references = []
28
28
 
29
29
  for model_name, model in data_contract_spec.models.items():
30
+ clean_model = _sanitize_name(model_name)
30
31
  entity_block = ""
31
32
 
32
33
  for field_name, field in model.fields.items():
33
34
  clean_name = _sanitize_name(field_name)
34
- indicators = ""
35
+ field_type = field.type or "unknown"
35
36
 
36
- if field.primaryKey or (field.unique and field.required):
37
- indicators += "🔑"
38
- if field.references:
39
- indicators += "⌘"
37
+ is_pk = bool(field.primaryKey or (field.unique and field.required))
38
+ is_fk = bool(field.references)
40
39
 
41
- field_type = field.type or "unknown"
42
- entity_block += f"\t{clean_name}{indicators} {field_type}\n"
40
+ entity_block += _field_line(clean_name, field_type, pk=is_pk, uk=bool(field.unique), fk=is_fk)
43
41
 
44
42
  if field.references:
45
- referenced_model = field.references.split(".")[0] if "." in field.references else ""
43
+ references = field.references.replace(".", "·")
44
+ parts = references.split("·")
45
+ referenced_model = _sanitize_name(parts[0]) if len(parts) > 0 else ""
46
+ referenced_field = _sanitize_name(parts[1]) if len(parts) > 1 else ""
46
47
  if referenced_model:
47
- mmd_references.append(f'"📑{referenced_model}"' + "}o--{ ||" + f'"📑{model_name}"')
48
+ label = referenced_field or clean_name
49
+ mmd_references.append(f'"**{referenced_model}**" ||--o{{ "**{clean_model}**" : {label}')
48
50
 
49
- mmd_entity += f'\t"**{model_name}**"' + "{\n" + entity_block + "}\n"
51
+ mmd_entity += f'\t"**{clean_model}**" {{\n{entity_block}}}\n'
50
52
 
51
53
  if mmd_references:
52
54
  mmd_entity += "\n" + "\n".join(mmd_references)
53
55
 
54
- return f"{mmd_entity}\n"
56
+ return mmd_entity + "\n"
55
57
 
56
58
  except Exception as e:
57
59
  print(f"Error generating DCS mermaid diagram: {e}")
@@ -95,3 +97,14 @@ def odcs_to_mermaid(data_contract_spec: OpenDataContractStandard) -> str | None:
95
97
 
96
98
  def _sanitize_name(name: str) -> str:
97
99
  return name.replace("#", "Nb").replace(" ", "_").replace("/", "by")
100
+
101
+
102
+ def _field_line(name: str, field_type: str, pk: bool = False, uk: bool = False, fk: bool = False) -> str:
103
+ indicators = ""
104
+ if pk:
105
+ indicators += "🔑"
106
+ if uk:
107
+ indicators += "🔒"
108
+ if fk:
109
+ indicators += "⌘"
110
+ return f"\t{name}{indicators} {field_type}\n"
@@ -1,3 +1,5 @@
1
+ import json
2
+
1
3
  from pyspark.sql import types
2
4
 
3
5
  from datacontract.export.exporter import Exporter
@@ -104,7 +106,8 @@ def to_struct_field(field: Field, field_name: str) -> types.StructField:
104
106
  types.StructField: The corresponding Spark StructField.
105
107
  """
106
108
  data_type = to_spark_data_type(field)
107
- return types.StructField(name=field_name, dataType=data_type, nullable=not field.required)
109
+ metadata = to_spark_metadata(field)
110
+ return types.StructField(name=field_name, dataType=data_type, nullable=not field.required, metadata=metadata)
108
111
 
109
112
 
110
113
  def to_spark_data_type(field: Field) -> types.DataType:
@@ -152,7 +155,25 @@ def to_spark_data_type(field: Field) -> types.DataType:
152
155
  return types.DateType()
153
156
  if field_type == "bytes":
154
157
  return types.BinaryType()
155
- return types.StringType() # default if no condition is met
158
+ return types.StringType() # default if no condition is met
159
+
160
+
161
+ def to_spark_metadata(field: Field) -> dict[str, str]:
162
+ """
163
+ Convert a field to a Spark metadata dictonary.
164
+
165
+ Args:
166
+ field (Field): The field to convert.
167
+
168
+ Returns:
169
+ dict: dictionary that can be supplied to Spark as metadata for a StructField
170
+ """
171
+
172
+ metadata = {}
173
+ if field.description:
174
+ metadata["comment"] = field.description
175
+
176
+ return metadata
156
177
 
157
178
 
158
179
  def print_schema(dtype: types.DataType) -> str:
@@ -192,7 +213,11 @@ def print_schema(dtype: types.DataType) -> str:
192
213
  name = f'"{column.name}"'
193
214
  data_type = indent(print_schema(column.dataType), 1)
194
215
  nullable = indent(f"{column.nullable}", 1)
195
- return f"StructField({name},\n{data_type},\n{nullable}\n)"
216
+ if column.metadata:
217
+ metadata = indent(f"{json.dumps(column.metadata)}", 1)
218
+ return f"StructField({name},\n{data_type},\n{nullable},\n{metadata}\n)"
219
+ else:
220
+ return f"StructField({name},\n{data_type},\n{nullable}\n)"
196
221
 
197
222
  def format_struct_type(struct_type: types.StructType) -> str:
198
223
  """