datacontract-cli 0.11.4__tar.gz → 0.11.6__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 (220) hide show
  1. {datacontract_cli-0.11.4/datacontract_cli.egg-info → datacontract_cli-0.11.6}/PKG-INFO +28 -24
  2. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/README.md +16 -12
  3. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/catalog/catalog.py +7 -1
  4. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/cli.py +6 -5
  5. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/data_contract.py +1 -3
  6. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/data_contract_checks.py +84 -69
  7. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/data_contract_test.py +1 -5
  8. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/soda/check_soda_execute.py +2 -3
  9. datacontract_cli-0.11.6/datacontract/engines/soda/connections/bigquery.py +24 -0
  10. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/soda/connections/duckdb_connection.py +14 -9
  11. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/soda/connections/impala.py +1 -3
  12. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/soda/connections/sqlserver.py +23 -2
  13. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/avro_exporter.py +5 -1
  14. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/avro_idl_exporter.py +3 -1
  15. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/bigquery_exporter.py +31 -15
  16. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/data_caterer_exporter.py +1 -0
  17. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/dbml_exporter.py +4 -6
  18. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/dbt_exporter.py +4 -7
  19. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/dcs_exporter.py +4 -4
  20. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/dqx_exporter.py +3 -1
  21. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/excel_exporter.py +13 -5
  22. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/exporter.py +3 -1
  23. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/great_expectations_exporter.py +8 -2
  24. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/iceberg_exporter.py +1 -1
  25. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/markdown_exporter.py +6 -6
  26. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/mermaid_exporter.py +4 -2
  27. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/odcs_export_helper.py +6 -11
  28. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/protobuf_exporter.py +3 -1
  29. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/rdf_exporter.py +1 -1
  30. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/sql_exporter.py +1 -4
  31. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/sql_type_converter.py +12 -8
  32. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/sqlalchemy_exporter.py +5 -4
  33. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/avro_importer.py +8 -9
  34. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/bigquery_importer.py +75 -30
  35. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/csv_importer.py +3 -6
  36. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/dbml_importer.py +2 -3
  37. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/dbt_importer.py +1 -0
  38. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/excel_importer.py +1 -1
  39. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/glue_importer.py +1 -3
  40. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/iceberg_importer.py +4 -5
  41. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/json_importer.py +1 -3
  42. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/jsonschema_importer.py +3 -9
  43. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/odcs_helper.py +1 -3
  44. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/odcs_importer.py +1 -3
  45. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/parquet_importer.py +1 -3
  46. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/sql_importer.py +2 -6
  47. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/lint/resolve.py +1 -3
  48. datacontract_cli-0.11.6/datacontract/output/json_test_results.py +13 -0
  49. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/output/junit_test_results.py +10 -7
  50. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/output/output_format.py +1 -1
  51. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/output/test_results_writer.py +9 -2
  52. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/templates/partials/model_field.html +1 -1
  53. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6/datacontract_cli.egg-info}/PKG-INFO +28 -24
  54. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract_cli.egg-info/SOURCES.txt +4 -0
  55. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract_cli.egg-info/requires.txt +11 -11
  56. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/pyproject.toml +12 -12
  57. datacontract_cli-0.11.6/tests/test_bigquery_soda_connection.py +61 -0
  58. datacontract_cli-0.11.6/tests/test_data_contract_checks.py +222 -0
  59. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_dbml.py +1 -1
  60. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_dbt_models.py +9 -4
  61. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_great_expectations.py +1 -3
  62. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_odcs_v3.py +0 -1
  63. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_sodacl.py +52 -0
  64. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_sql_query.py +3 -1
  65. datacontract_cli-0.11.6/tests/test_sqlserver_soda_connection.py +126 -0
  66. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_kafka.py +1 -3
  67. datacontract_cli-0.11.6/tests/test_test_output_json.py +30 -0
  68. datacontract_cli-0.11.6/tests/test_test_output_junit.py +57 -0
  69. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_schema_evolution.py +29 -9
  70. datacontract_cli-0.11.4/datacontract/engines/soda/connections/bigquery.py +0 -27
  71. datacontract_cli-0.11.4/tests/test_data_contract_checks.py +0 -74
  72. datacontract_cli-0.11.4/tests/test_test_output_junit.py +0 -22
  73. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/LICENSE +0 -0
  74. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/MANIFEST.in +0 -0
  75. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/__init__.py +0 -0
  76. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/api.py +0 -0
  77. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/__init__.py +0 -0
  78. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/datacontract/check_that_datacontract_contains_valid_servers_configuration.py +0 -0
  79. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/datacontract/check_that_datacontract_file_exists.py +0 -0
  80. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/fastjsonschema/check_jsonschema.py +0 -0
  81. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/fastjsonschema/s3/s3_read_files.py +0 -0
  82. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/soda/__init__.py +0 -0
  83. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/soda/connections/athena.py +0 -0
  84. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/soda/connections/databricks.py +0 -0
  85. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/soda/connections/kafka.py +0 -0
  86. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/soda/connections/oracle.py +0 -0
  87. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/soda/connections/postgres.py +0 -0
  88. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/soda/connections/snowflake.py +0 -0
  89. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/engines/soda/connections/trino.py +0 -0
  90. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/__init__.py +0 -0
  91. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/custom_exporter.py +0 -0
  92. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/duckdb_type_converter.py +0 -0
  93. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/exporter_factory.py +0 -0
  94. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/go_exporter.py +0 -0
  95. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/html_exporter.py +0 -0
  96. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/jsonschema_exporter.py +0 -0
  97. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/odcs_v3_exporter.py +0 -0
  98. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/pandas_type_converter.py +0 -0
  99. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/pydantic_exporter.py +0 -0
  100. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/sodacl_exporter.py +0 -0
  101. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/export/spark_exporter.py +0 -0
  102. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/dcs_importer.py +0 -0
  103. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/importer.py +0 -0
  104. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/importer_factory.py +0 -0
  105. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/protobuf_importer.py +0 -0
  106. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/spark_importer.py +0 -0
  107. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/imports/unity_importer.py +0 -0
  108. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/init/init_template.py +0 -0
  109. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/integration/entropy_data.py +0 -0
  110. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/lint/files.py +0 -0
  111. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/lint/resources.py +0 -0
  112. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/lint/schema.py +0 -0
  113. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/lint/urls.py +0 -0
  114. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/model/exceptions.py +0 -0
  115. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/model/odcs.py +0 -0
  116. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/model/run.py +0 -0
  117. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/output/__init__.py +0 -0
  118. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/py.typed +0 -0
  119. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/schemas/datacontract-1.1.0.init.yaml +0 -0
  120. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/schemas/datacontract-1.1.0.schema.json +0 -0
  121. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/schemas/datacontract-1.2.0.init.yaml +0 -0
  122. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/schemas/datacontract-1.2.0.schema.json +0 -0
  123. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/schemas/datacontract-1.2.1.init.yaml +0 -0
  124. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/schemas/datacontract-1.2.1.schema.json +0 -0
  125. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/schemas/odcs-3.0.1.schema.json +0 -0
  126. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/schemas/odcs-3.0.2.schema.json +0 -0
  127. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/schemas/odcs-3.1.0.init.yaml +0 -0
  128. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/schemas/odcs-3.1.0.schema.json +0 -0
  129. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/templates/datacontract.html +0 -0
  130. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/templates/datacontract_odcs.html +0 -0
  131. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/templates/index.html +0 -0
  132. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/templates/partials/datacontract_information.html +0 -0
  133. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/templates/partials/datacontract_servicelevels.html +0 -0
  134. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/templates/partials/datacontract_terms.html +0 -0
  135. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/templates/partials/definition.html +0 -0
  136. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/templates/partials/example.html +0 -0
  137. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/templates/partials/quality.html +0 -0
  138. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/templates/partials/server.html +0 -0
  139. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract/templates/style/output.css +0 -0
  140. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract_cli.egg-info/dependency_links.txt +0 -0
  141. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract_cli.egg-info/entry_points.txt +0 -0
  142. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/datacontract_cli.egg-info/top_level.txt +0 -0
  143. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/setup.cfg +0 -0
  144. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_api.py +0 -0
  145. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_catalog.py +0 -0
  146. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_cli.py +0 -0
  147. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_data_contract_specification.py +0 -0
  148. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_description_linter.py +0 -0
  149. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_download_datacontract_file.py +0 -0
  150. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_duckdb_json.py +0 -0
  151. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_avro.py +0 -0
  152. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_avro_idl.py +0 -0
  153. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_bigquery.py +0 -0
  154. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_complex_data_contract.py +0 -0
  155. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_custom.py +0 -0
  156. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_custom_exporter.py +0 -0
  157. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_data_caterer.py +0 -0
  158. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_dbt_sources.py +0 -0
  159. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_dbt_staging_sql.py +0 -0
  160. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_dqx.py +0 -0
  161. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_excel.py +0 -0
  162. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_go.py +0 -0
  163. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_html.py +0 -0
  164. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_iceberg.py +0 -0
  165. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_jsonschema.py +0 -0
  166. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_markdown.py +0 -0
  167. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_mermaid.py +0 -0
  168. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_protobuf.py +0 -0
  169. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_pydantic.py +0 -0
  170. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_rdf.py +0 -0
  171. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_spark.py +0 -0
  172. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_sql.py +0 -0
  173. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_export_sqlalchemy.py +0 -0
  174. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_avro.py +0 -0
  175. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_bigquery.py +0 -0
  176. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_csv.py +0 -0
  177. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_dbml.py +0 -0
  178. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_dbt.py +0 -0
  179. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_excel.py +0 -0
  180. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_glue.py +0 -0
  181. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_iceberg.py +0 -0
  182. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_json.py +0 -0
  183. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_jsonschema.py +0 -0
  184. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_odcs_v3.py +0 -0
  185. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_parquet.py +0 -0
  186. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_protobuf.py +0 -0
  187. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_spark.py +0 -0
  188. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_sql_oracle.py +0 -0
  189. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_sql_postgres.py +0 -0
  190. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_sql_sqlserver.py +0 -0
  191. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_import_unity_file.py +0 -0
  192. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_integration_entropydata.py +0 -0
  193. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_lint.py +0 -0
  194. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_resolve.py +0 -0
  195. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_roundtrip_jsonschema.py +0 -0
  196. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_api.py +0 -0
  197. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_athena_iceberg.py +0 -0
  198. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_azure_remote.py +0 -0
  199. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_bigquery.py +0 -0
  200. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_databricks.py +0 -0
  201. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_dataframe.py +0 -0
  202. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_delta.py +0 -0
  203. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_gcs_csv_remote.py +0 -0
  204. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_gcs_json_remote.py +0 -0
  205. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_kafka_remote.py +0 -0
  206. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_local_json.py +0 -0
  207. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_local_json_nd.py +0 -0
  208. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_oracle.py +0 -0
  209. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_parquet.py +0 -0
  210. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_postgres.py +0 -0
  211. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_quality.py +0 -0
  212. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_s3_csv.py +0 -0
  213. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_s3_delta.py +0 -0
  214. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_s3_json.py +0 -0
  215. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_s3_json_complex.py +0 -0
  216. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_s3_json_multiple_models.py +0 -0
  217. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_s3_json_remote.py +0 -0
  218. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_snowflake.py +0 -0
  219. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/tests/test_test_sqlserver.py +0 -0
  220. {datacontract_cli-0.11.4 → datacontract_cli-0.11.6}/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.11.4
3
+ Version: 0.11.6
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
@@ -11,7 +11,7 @@ Classifier: Operating System :: OS Independent
11
11
  Requires-Python: <3.13,>=3.10
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
- Requires-Dist: typer<0.22,>=0.15.1
14
+ Requires-Dist: typer<0.25,>=0.18.0
15
15
  Requires-Dist: pydantic<2.13.0,>=2.8.2
16
16
  Requires-Dist: pyyaml~=6.0.1
17
17
  Requires-Dist: requests<2.33,>=2.31
@@ -39,7 +39,7 @@ Provides-Extra: databricks
39
39
  Requires-Dist: soda-core-spark-df<3.6.0,>=3.3.20; extra == "databricks"
40
40
  Requires-Dist: soda-core-spark[databricks]<3.6.0,>=3.3.20; extra == "databricks"
41
41
  Requires-Dist: databricks-sql-connector<4.3.0,>=3.7.0; extra == "databricks"
42
- Requires-Dist: databricks-sdk<0.78.0; extra == "databricks"
42
+ Requires-Dist: databricks-sdk<0.86.0; extra == "databricks"
43
43
  Requires-Dist: pyspark<5.0.0,>=3.5.0; extra == "databricks"
44
44
  Requires-Dist: numpy<2.0.0,>=1.26.4; extra == "databricks"
45
45
  Provides-Extra: iceberg
@@ -52,10 +52,10 @@ Requires-Dist: numpy<2.0.0,>=1.26.4; extra == "kafka"
52
52
  Provides-Extra: postgres
53
53
  Requires-Dist: soda-core-postgres<3.6.0,>=3.3.20; extra == "postgres"
54
54
  Provides-Extra: s3
55
- Requires-Dist: s3fs<2026.0.0,>=2025.2.0; extra == "s3"
55
+ Requires-Dist: s3fs<2027.0.0,>=2025.2.0; extra == "s3"
56
56
  Requires-Dist: aiobotocore<3.2.0,>=2.17.0; extra == "s3"
57
57
  Provides-Extra: snowflake
58
- Requires-Dist: snowflake-connector-python[pandas]<4.2,>=3.6; extra == "snowflake"
58
+ Requires-Dist: snowflake-connector-python[pandas]<4.3,>=3.6; extra == "snowflake"
59
59
  Requires-Dist: soda-core-snowflake<3.6.0,>=3.3.20; extra == "snowflake"
60
60
  Provides-Extra: sqlserver
61
61
  Requires-Dist: soda-core-sqlserver<3.6.0,>=3.3.20; extra == "sqlserver"
@@ -75,9 +75,9 @@ Requires-Dist: soda-core-duckdb<3.6.0,>=3.3.20; extra == "duckdb"
75
75
  Provides-Extra: parquet
76
76
  Requires-Dist: pyarrow>=18.1.0; extra == "parquet"
77
77
  Provides-Extra: rdf
78
- Requires-Dist: rdflib==7.5.0; extra == "rdf"
78
+ Requires-Dist: rdflib==7.6.0; extra == "rdf"
79
79
  Provides-Extra: api
80
- Requires-Dist: fastapi==0.121.2; extra == "api"
80
+ Requires-Dist: fastapi==0.129.0; extra == "api"
81
81
  Requires-Dist: uvicorn==0.40.0; extra == "api"
82
82
  Provides-Extra: protobuf
83
83
  Requires-Dist: grpcio-tools>=1.53; extra == "protobuf"
@@ -87,15 +87,15 @@ Provides-Extra: dev
87
87
  Requires-Dist: datacontract-cli[all]; extra == "dev"
88
88
  Requires-Dist: httpx==0.28.1; extra == "dev"
89
89
  Requires-Dist: kafka-python; extra == "dev"
90
- Requires-Dist: minio==7.2.17; extra == "dev"
91
- Requires-Dist: moto==5.1.18; extra == "dev"
90
+ Requires-Dist: minio==7.2.20; extra == "dev"
91
+ Requires-Dist: moto==5.1.21; extra == "dev"
92
92
  Requires-Dist: pandas>=2.1.0; extra == "dev"
93
93
  Requires-Dist: pre-commit<4.6.0,>=3.7.1; extra == "dev"
94
94
  Requires-Dist: pytest; extra == "dev"
95
95
  Requires-Dist: pytest-xdist; extra == "dev"
96
- Requires-Dist: pymssql==2.3.9; extra == "dev"
97
- Requires-Dist: ruff; extra == "dev"
98
- Requires-Dist: testcontainers[kafka,minio,mssql,postgres]==4.13.3; extra == "dev"
96
+ Requires-Dist: pymssql==2.3.13; extra == "dev"
97
+ Requires-Dist: ruff==0.14.3; extra == "dev"
98
+ Requires-Dist: testcontainers[kafka,minio,mssql,postgres]==4.14.1; extra == "dev"
99
99
  Requires-Dist: trino==0.336.0; extra == "dev"
100
100
  Dynamic: license-file
101
101
 
@@ -622,10 +622,11 @@ servers:
622
622
 
623
623
  #### BigQuery
624
624
 
625
- We support authentication to BigQuery using Service Account Key. The used Service Account should include the roles:
625
+ We support authentication to BigQuery using Service Account Key or Application Default Credentials (ADC). ADC supports Workload Identity Federation (WIF), GCE metadata server, and `gcloud auth application-default login`. The used Service Account should include the roles:
626
626
  * BigQuery Job User
627
627
  * BigQuery Data Viewer
628
628
 
629
+ When no `DATACONTRACT_BIGQUERY_ACCOUNT_INFO_JSON_PATH` is set, the CLI falls back to ADC/WIF automatically via Soda's `use_context_auth`.
629
630
 
630
631
  ##### Example
631
632
 
@@ -646,7 +647,8 @@ models:
646
647
 
647
648
  | Environment Variable | Example | Description |
648
649
  |----------------------------------------------|---------------------------|---------------------------------------------------------|
649
- | `DATACONTRACT_BIGQUERY_ACCOUNT_INFO_JSON_PATH` | `~/service-access-key.json` | Service Access key as saved on key creation by BigQuery. If this environment variable isn't set, the cli tries to use `GOOGLE_APPLICATION_CREDENTIALS` as a fallback, so if you have that set for using their Python library anyway, it should work seamlessly. |
650
+ | `DATACONTRACT_BIGQUERY_ACCOUNT_INFO_JSON_PATH` | `~/service-access-key.json` | Service Account key JSON file. If not set, ADC/WIF is used automatically. |
651
+ | `DATACONTRACT_BIGQUERY_IMPERSONATION_ACCOUNT` | `sa@project.iam.gserviceaccount.com` | Optional. Service account to impersonate. Works with both key file and ADC auth. |
650
652
 
651
653
 
652
654
  #### Azure
@@ -678,7 +680,7 @@ Authentication works with an Azure Service Principal (SPN) aka App Registration
678
680
 
679
681
  #### Sqlserver
680
682
 
681
- Data Contract CLI can test data in MS SQL Server (including Azure SQL, Synapse Analytics SQL Pool).
683
+ Data Contract CLI can test data in MS SQL Server (including Azure SQL, Synapse Analytics SQL Pool, and Microsoft Fabric).
682
684
 
683
685
  ##### Example
684
686
 
@@ -702,14 +704,17 @@ models:
702
704
 
703
705
  ##### Environment Variables
704
706
 
705
- | Environment Variable | Example| Description |
706
- |---------------------------------------------------|--------|----------------------------------------------|
707
- | `DATACONTRACT_SQLSERVER_USERNAME` | `root` | Username |
708
- | `DATACONTRACT_SQLSERVER_PASSWORD` | `toor` | Password |
709
- | `DATACONTRACT_SQLSERVER_TRUSTED_CONNECTION` | `True` | Use windows authentication, instead of login |
710
- | `DATACONTRACT_SQLSERVER_TRUST_SERVER_CERTIFICATE` | `True` | Trust self-signed certificate |
711
- | `DATACONTRACT_SQLSERVER_ENCRYPTED_CONNECTION` | `True` | Use SSL |
712
- | `DATACONTRACT_SQLSERVER_DRIVER` | `ODBC Driver 18 for SQL Server` | ODBC driver name |
707
+ | Environment Variable | Example | Description |
708
+ |---------------------------------------------------|---------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
709
+ | `DATACONTRACT_SQLSERVER_AUTHENTICATION` | `sql` | Supported: `sql` (default), `cli` (uses `az login` session), `windows`, `ActiveDirectoryPassword`, `ActiveDirectoryServicePrincipal`, `ActiveDirectoryInteractive` |
710
+ | `DATACONTRACT_SQLSERVER_USERNAME` | `root` | Username (for `sql`, `ActiveDirectoryPassword`, `ActiveDirectoryInteractive`) |
711
+ | `DATACONTRACT_SQLSERVER_PASSWORD` | `toor` | Password (for `sql` and `ActiveDirectoryPassword`) |
712
+ | `DATACONTRACT_SQLSERVER_CLIENT_ID` | `a3cf5d29-b1a7-...` | Application/Client ID (for `ActiveDirectoryServicePrincipal`) |
713
+ | `DATACONTRACT_SQLSERVER_CLIENT_SECRET` | `kX9~Qr2Lm.Tz4W...` | Client secret (for `ActiveDirectoryServicePrincipal`) |
714
+ | `DATACONTRACT_SQLSERVER_TRUST_SERVER_CERTIFICATE` | `True` | Trust self-signed certificate |
715
+ | `DATACONTRACT_SQLSERVER_ENCRYPTED_CONNECTION` | `True` | Use SSL |
716
+ | `DATACONTRACT_SQLSERVER_DRIVER` | `ODBC Driver 18 for SQL Server` | ODBC driver name |
717
+ | `DATACONTRACT_SQLSERVER_TRUSTED_CONNECTION` | `True` | Deprecated. Equivalent to `AUTHENTICATION=windows` |
713
718
 
714
719
 
715
720
 
@@ -1636,7 +1641,6 @@ Available import options:
1636
1641
  | `spark` | Import from Spark StructTypes, Variant | ✅ |
1637
1642
  | `sql` | Import from SQL DDL | ✅ |
1638
1643
  | `unity` | Import from Databricks Unity Catalog | partial |
1639
- | `excel` | Import from ODCS Excel Template | ✅ |
1640
1644
  | Missing something? | Please create an issue on GitHub | TBD |
1641
1645
 
1642
1646
 
@@ -521,10 +521,11 @@ servers:
521
521
 
522
522
  #### BigQuery
523
523
 
524
- We support authentication to BigQuery using Service Account Key. The used Service Account should include the roles:
524
+ We support authentication to BigQuery using Service Account Key or Application Default Credentials (ADC). ADC supports Workload Identity Federation (WIF), GCE metadata server, and `gcloud auth application-default login`. The used Service Account should include the roles:
525
525
  * BigQuery Job User
526
526
  * BigQuery Data Viewer
527
527
 
528
+ When no `DATACONTRACT_BIGQUERY_ACCOUNT_INFO_JSON_PATH` is set, the CLI falls back to ADC/WIF automatically via Soda's `use_context_auth`.
528
529
 
529
530
  ##### Example
530
531
 
@@ -545,7 +546,8 @@ models:
545
546
 
546
547
  | Environment Variable | Example | Description |
547
548
  |----------------------------------------------|---------------------------|---------------------------------------------------------|
548
- | `DATACONTRACT_BIGQUERY_ACCOUNT_INFO_JSON_PATH` | `~/service-access-key.json` | Service Access key as saved on key creation by BigQuery. If this environment variable isn't set, the cli tries to use `GOOGLE_APPLICATION_CREDENTIALS` as a fallback, so if you have that set for using their Python library anyway, it should work seamlessly. |
549
+ | `DATACONTRACT_BIGQUERY_ACCOUNT_INFO_JSON_PATH` | `~/service-access-key.json` | Service Account key JSON file. If not set, ADC/WIF is used automatically. |
550
+ | `DATACONTRACT_BIGQUERY_IMPERSONATION_ACCOUNT` | `sa@project.iam.gserviceaccount.com` | Optional. Service account to impersonate. Works with both key file and ADC auth. |
549
551
 
550
552
 
551
553
  #### Azure
@@ -577,7 +579,7 @@ Authentication works with an Azure Service Principal (SPN) aka App Registration
577
579
 
578
580
  #### Sqlserver
579
581
 
580
- Data Contract CLI can test data in MS SQL Server (including Azure SQL, Synapse Analytics SQL Pool).
582
+ Data Contract CLI can test data in MS SQL Server (including Azure SQL, Synapse Analytics SQL Pool, and Microsoft Fabric).
581
583
 
582
584
  ##### Example
583
585
 
@@ -601,14 +603,17 @@ models:
601
603
 
602
604
  ##### Environment Variables
603
605
 
604
- | Environment Variable | Example| Description |
605
- |---------------------------------------------------|--------|----------------------------------------------|
606
- | `DATACONTRACT_SQLSERVER_USERNAME` | `root` | Username |
607
- | `DATACONTRACT_SQLSERVER_PASSWORD` | `toor` | Password |
608
- | `DATACONTRACT_SQLSERVER_TRUSTED_CONNECTION` | `True` | Use windows authentication, instead of login |
609
- | `DATACONTRACT_SQLSERVER_TRUST_SERVER_CERTIFICATE` | `True` | Trust self-signed certificate |
610
- | `DATACONTRACT_SQLSERVER_ENCRYPTED_CONNECTION` | `True` | Use SSL |
611
- | `DATACONTRACT_SQLSERVER_DRIVER` | `ODBC Driver 18 for SQL Server` | ODBC driver name |
606
+ | Environment Variable | Example | Description |
607
+ |---------------------------------------------------|---------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
608
+ | `DATACONTRACT_SQLSERVER_AUTHENTICATION` | `sql` | Supported: `sql` (default), `cli` (uses `az login` session), `windows`, `ActiveDirectoryPassword`, `ActiveDirectoryServicePrincipal`, `ActiveDirectoryInteractive` |
609
+ | `DATACONTRACT_SQLSERVER_USERNAME` | `root` | Username (for `sql`, `ActiveDirectoryPassword`, `ActiveDirectoryInteractive`) |
610
+ | `DATACONTRACT_SQLSERVER_PASSWORD` | `toor` | Password (for `sql` and `ActiveDirectoryPassword`) |
611
+ | `DATACONTRACT_SQLSERVER_CLIENT_ID` | `a3cf5d29-b1a7-...` | Application/Client ID (for `ActiveDirectoryServicePrincipal`) |
612
+ | `DATACONTRACT_SQLSERVER_CLIENT_SECRET` | `kX9~Qr2Lm.Tz4W...` | Client secret (for `ActiveDirectoryServicePrincipal`) |
613
+ | `DATACONTRACT_SQLSERVER_TRUST_SERVER_CERTIFICATE` | `True` | Trust self-signed certificate |
614
+ | `DATACONTRACT_SQLSERVER_ENCRYPTED_CONNECTION` | `True` | Use SSL |
615
+ | `DATACONTRACT_SQLSERVER_DRIVER` | `ODBC Driver 18 for SQL Server` | ODBC driver name |
616
+ | `DATACONTRACT_SQLSERVER_TRUSTED_CONNECTION` | `True` | Deprecated. Equivalent to `AUTHENTICATION=windows` |
612
617
 
613
618
 
614
619
 
@@ -1535,7 +1540,6 @@ Available import options:
1535
1540
  | `spark` | Import from Spark StructTypes, Variant | ✅ |
1536
1541
  | `sql` | Import from SQL DDL | ✅ |
1537
1542
  | `unity` | Import from Databricks Unity Catalog | partial |
1538
- | `excel` | Import from ODCS Excel Template | ✅ |
1539
1543
  | Missing something? | Please create an issue on GitHub | TBD |
1540
1544
 
1541
1545
 
@@ -32,7 +32,11 @@ def create_data_contract_html(contracts, file: Path, path: Path, schema: str):
32
32
  odcs = data_contract.get_data_contract()
33
33
  file_without_suffix = file.with_suffix(".html")
34
34
  html_filepath = path / file_without_suffix
35
- html_filepath.parent.mkdir(parents=True, exist_ok=True)
35
+ try:
36
+ html_filepath.parent.mkdir(parents=True, exist_ok=True)
37
+ except FileExistsError:
38
+ if not html_filepath.parent.is_dir():
39
+ raise
36
40
  with open(html_filepath, "w", encoding="utf-8") as f:
37
41
  f.write(html)
38
42
  contracts.append(
@@ -48,6 +52,7 @@ def create_data_contract_html(contracts, file: Path, path: Path, schema: str):
48
52
  @dataclass
49
53
  class _InfoView:
50
54
  """Unified info view for templates."""
55
+
51
56
  title: str
52
57
  version: str
53
58
  owner: Optional[str]
@@ -57,6 +62,7 @@ class _InfoView:
57
62
  @dataclass
58
63
  class _SpecView:
59
64
  """Unified spec view for templates, compatible with DCS template structure."""
65
+
60
66
  info: _InfoView
61
67
  models: dict
62
68
 
@@ -199,10 +199,7 @@ def export(
199
199
  server: Annotated[str, typer.Option(help="The server name to export.")] = None,
200
200
  schema_name: Annotated[
201
201
  str,
202
- typer.Option(
203
- help="The name of the schema to export, e.g., `orders`, or `all` for all "
204
- "schemas (default)."
205
- ),
202
+ typer.Option(help="The name of the schema to export, e.g., `orders`, or `all` for all schemas (default)."),
206
203
  ] = "all",
207
204
  # TODO: this should be a subcommand
208
205
  rdf_base: Annotated[
@@ -431,7 +428,11 @@ def catalog(
431
428
  enable_debug_logging(debug)
432
429
 
433
430
  path = Path(output)
434
- path.mkdir(parents=True, exist_ok=True)
431
+ try:
432
+ path.mkdir(parents=True, exist_ok=True)
433
+ except FileExistsError:
434
+ if not path.is_dir():
435
+ raise
435
436
  console.print(f"Created {output}")
436
437
 
437
438
  contracts = []
@@ -202,9 +202,7 @@ class DataContract:
202
202
  id = kwargs.get("id")
203
203
  owner = kwargs.get("owner")
204
204
 
205
- odcs_imported = importer_factory.create(format).import_source(
206
- source=source, import_args=kwargs
207
- )
205
+ odcs_imported = importer_factory.create(format).import_source(source=source, import_args=kwargs)
208
206
 
209
207
  cls._overwrite_id_in_odcs(odcs_imported, id)
210
208
  cls._overwrite_owner_in_odcs(odcs_imported, owner)
@@ -20,10 +20,22 @@ from datacontract.model.run import Check
20
20
  @dataclass
21
21
  class QuotingConfig:
22
22
  quote_field_name: bool = False
23
+ quote_field_name_with_backticks: bool = False
23
24
  quote_model_name: bool = False
24
25
  quote_model_name_with_backticks: bool = False
25
26
 
26
27
 
28
+ def _quote_field_name(field_name: str, quoting_config: QuotingConfig) -> str:
29
+ """Quote a field name according to the quoting configuration."""
30
+ if field_name is None:
31
+ return field_name
32
+ if quoting_config.quote_field_name:
33
+ return f'"{field_name}"'
34
+ elif quoting_config.quote_field_name_with_backticks:
35
+ return f"`{field_name}`"
36
+ return field_name
37
+
38
+
27
39
  def _get_logical_type_option(prop: SchemaProperty, key: str):
28
40
  """Get a logical type option value."""
29
41
  if prop.logicalTypeOptions is None:
@@ -72,8 +84,9 @@ def to_schema_checks(schema_object: SchemaObject, server: Server) -> List[Check]
72
84
 
73
85
  type1 = server.type if server and server.type else None
74
86
  config = QuotingConfig(
75
- quote_field_name=type1 in ["postgres", "sqlserver"],
76
- quote_model_name=type1 in ["postgres", "sqlserver"],
87
+ quote_field_name=type1 in ["postgres", "sqlserver", "snowflake", "azure", "s3", "gcs", "local"],
88
+ quote_field_name_with_backticks=type1 in ["databricks"],
89
+ quote_model_name=type1 in ["postgres", "sqlserver", "snowflake", "azure", "s3", "gcs", "local"],
77
90
  quote_model_name_with_backticks=type1 == "bigquery",
78
91
  )
79
92
  quoting_config = config
@@ -165,7 +178,6 @@ def to_schema_name(schema_object: SchemaObject, server_type: str) -> str:
165
178
  return schema_object.name
166
179
 
167
180
 
168
-
169
181
  def check_property_is_present(model_name, field_name, quoting_config: QuotingConfig = QuotingConfig()) -> Check:
170
182
  check_type = "field_is_present"
171
183
  check_key = f"{model_name}__{field_name}__{check_type}"
@@ -229,10 +241,7 @@ def check_property_type(
229
241
 
230
242
 
231
243
  def check_property_required(model_name: str, field_name: str, quoting_config: QuotingConfig = QuotingConfig()):
232
- if quoting_config.quote_field_name:
233
- field_name_for_soda = f'"{field_name}"'
234
- else:
235
- field_name_for_soda = field_name
244
+ field_name_for_soda = _quote_field_name(field_name, quoting_config)
236
245
 
237
246
  check_type = "field_required"
238
247
  check_key = f"{model_name}__{field_name}__{check_type}"
@@ -260,10 +269,7 @@ def check_property_required(model_name: str, field_name: str, quoting_config: Qu
260
269
 
261
270
 
262
271
  def check_property_unique(model_name: str, field_name: str, quoting_config: QuotingConfig = QuotingConfig()):
263
- if quoting_config.quote_field_name:
264
- field_name_for_soda = f'"{field_name}"'
265
- else:
266
- field_name_for_soda = field_name
272
+ field_name_for_soda = _quote_field_name(field_name, quoting_config)
267
273
 
268
274
  check_type = "field_unique"
269
275
  check_key = f"{model_name}__{field_name}__{check_type}"
@@ -293,10 +299,7 @@ def check_property_unique(model_name: str, field_name: str, quoting_config: Quot
293
299
  def check_property_min_length(
294
300
  model_name: str, field_name: str, min_length: int, quoting_config: QuotingConfig = QuotingConfig()
295
301
  ):
296
- if quoting_config.quote_field_name:
297
- field_name_for_soda = f'"{field_name}"'
298
- else:
299
- field_name_for_soda = field_name
302
+ field_name_for_soda = _quote_field_name(field_name, quoting_config)
300
303
 
301
304
  check_type = "field_min_length"
302
305
  check_key = f"{model_name}__{field_name}__{check_type}"
@@ -327,10 +330,7 @@ def check_property_min_length(
327
330
  def check_property_max_length(
328
331
  model_name: str, field_name: str, max_length: int, quoting_config: QuotingConfig = QuotingConfig()
329
332
  ):
330
- if quoting_config.quote_field_name:
331
- field_name_for_soda = f'"{field_name}"'
332
- else:
333
- field_name_for_soda = field_name
333
+ field_name_for_soda = _quote_field_name(field_name, quoting_config)
334
334
 
335
335
  check_type = "field_max_length"
336
336
  check_key = f"{model_name}__{field_name}__{check_type}"
@@ -361,10 +361,7 @@ def check_property_max_length(
361
361
  def check_property_minimum(
362
362
  model_name: str, field_name: str, minimum: int, quoting_config: QuotingConfig = QuotingConfig()
363
363
  ):
364
- if quoting_config.quote_field_name:
365
- field_name_for_soda = f'"{field_name}"'
366
- else:
367
- field_name_for_soda = field_name
364
+ field_name_for_soda = _quote_field_name(field_name, quoting_config)
368
365
 
369
366
  check_type = "field_minimum"
370
367
  check_key = f"{model_name}__{field_name}__{check_type}"
@@ -395,10 +392,7 @@ def check_property_minimum(
395
392
  def check_property_maximum(
396
393
  model_name: str, field_name: str, maximum: int, quoting_config: QuotingConfig = QuotingConfig()
397
394
  ):
398
- if quoting_config.quote_field_name:
399
- field_name_for_soda = f'"{field_name}"'
400
- else:
401
- field_name_for_soda = field_name
395
+ field_name_for_soda = _quote_field_name(field_name, quoting_config)
402
396
 
403
397
  check_type = "field_maximum"
404
398
  check_key = f"{model_name}__{field_name}__{check_type}"
@@ -429,10 +423,7 @@ def check_property_maximum(
429
423
  def check_property_not_equal(
430
424
  model_name: str, field_name: str, value: int, quoting_config: QuotingConfig = QuotingConfig()
431
425
  ):
432
- if quoting_config.quote_field_name:
433
- field_name_for_soda = f'"{field_name}"'
434
- else:
435
- field_name_for_soda = field_name
426
+ field_name_for_soda = _quote_field_name(field_name, quoting_config)
436
427
 
437
428
  check_type = "field_not_equal"
438
429
  check_key = f"{model_name}__{field_name}__{check_type}"
@@ -461,10 +452,7 @@ def check_property_not_equal(
461
452
 
462
453
 
463
454
  def check_property_enum(model_name: str, field_name: str, enum: list, quoting_config: QuotingConfig = QuotingConfig()):
464
- if quoting_config.quote_field_name:
465
- field_name_for_soda = f'"{field_name}"'
466
- else:
467
- field_name_for_soda = field_name
455
+ field_name_for_soda = _quote_field_name(field_name, quoting_config)
468
456
 
469
457
  check_type = "field_enum"
470
458
  check_key = f"{model_name}__{field_name}__{check_type}"
@@ -492,11 +480,10 @@ def check_property_enum(model_name: str, field_name: str, enum: list, quoting_co
492
480
  )
493
481
 
494
482
 
495
- def check_property_regex(model_name: str, field_name: str, pattern: str, quoting_config: QuotingConfig = QuotingConfig()):
496
- if quoting_config.quote_field_name:
497
- field_name_for_soda = f'"{field_name}"'
498
- else:
499
- field_name_for_soda = field_name
483
+ def check_property_regex(
484
+ model_name: str, field_name: str, pattern: str, quoting_config: QuotingConfig = QuotingConfig()
485
+ ):
486
+ field_name_for_soda = _quote_field_name(field_name, quoting_config)
500
487
 
501
488
  check_type = "field_regex"
502
489
  check_key = f"{model_name}__{field_name}__{check_type}"
@@ -553,7 +540,7 @@ def check_model_duplicate_values(
553
540
  ):
554
541
  check_type = "model_duplicate_values"
555
542
  check_key = f"{model_name}__{check_type}"
556
- col_joined = ", ".join(cols)
543
+ col_joined = ", ".join(_quote_field_name(col, quoting_config) for col in cols)
557
544
  sodacl_check_dict = {
558
545
  checks_for(model_name, quoting_config, check_type): [
559
546
  {
@@ -578,10 +565,7 @@ def check_model_duplicate_values(
578
565
  def check_property_duplicate_values(
579
566
  model_name: str, field_name: str, threshold: str, quoting_config: QuotingConfig = QuotingConfig()
580
567
  ):
581
- if quoting_config.quote_field_name:
582
- field_name_for_soda = f'"{field_name}"'
583
- else:
584
- field_name_for_soda = field_name
568
+ field_name_for_soda = _quote_field_name(field_name, quoting_config)
585
569
 
586
570
  check_type = "field_duplicate_values"
587
571
  check_key = f"{model_name}__{field_name}__{check_type}"
@@ -611,10 +595,7 @@ def check_property_duplicate_values(
611
595
  def check_property_null_values(
612
596
  model_name: str, field_name: str, threshold: str, quoting_config: QuotingConfig = QuotingConfig()
613
597
  ):
614
- if quoting_config.quote_field_name:
615
- field_name_for_soda = f'"{field_name}"'
616
- else:
617
- field_name_for_soda = field_name
598
+ field_name_for_soda = _quote_field_name(field_name, quoting_config)
618
599
 
619
600
  check_type = "field_null_values"
620
601
  check_key = f"{model_name}__{field_name}__{check_type}"
@@ -648,10 +629,7 @@ def check_property_invalid_values(
648
629
  valid_values: list = None,
649
630
  quoting_config: QuotingConfig = QuotingConfig(),
650
631
  ):
651
- if quoting_config.quote_field_name:
652
- field_name_for_soda = f'"{field_name}"'
653
- else:
654
- field_name_for_soda = field_name
632
+ field_name_for_soda = _quote_field_name(field_name, quoting_config)
655
633
 
656
634
  check_type = "field_invalid_values"
657
635
  check_key = f"{model_name}__{field_name}__{check_type}"
@@ -691,10 +669,7 @@ def check_property_missing_values(
691
669
  missing_values: list = None,
692
670
  quoting_config: QuotingConfig = QuotingConfig(),
693
671
  ):
694
- if quoting_config.quote_field_name:
695
- field_name_for_soda = f'"{field_name}"'
696
- else:
697
- field_name_for_soda = field_name
672
+ field_name_for_soda = _quote_field_name(field_name, quoting_config)
698
673
 
699
674
  check_type = "field_missing_values"
700
675
  check_key = f"{model_name}__{field_name}__{check_type}"
@@ -819,7 +794,9 @@ def check_quality_list(
819
794
  )
820
795
  )
821
796
  else:
822
- checks.append(check_property_duplicate_values(schema_name, property_name, threshold, quoting_config))
797
+ checks.append(
798
+ check_property_duplicate_values(schema_name, property_name, threshold, quoting_config)
799
+ )
823
800
  elif quality.metric == "nullValues":
824
801
  if property_name is not None:
825
802
  checks.append(check_property_null_values(schema_name, property_name, threshold, quoting_config))
@@ -829,7 +806,9 @@ def check_quality_list(
829
806
  if property_name is not None:
830
807
  valid_values = quality.arguments.get("validValues") if quality.arguments else None
831
808
  checks.append(
832
- check_property_invalid_values(schema_name, property_name, threshold, valid_values, quoting_config)
809
+ check_property_invalid_values(
810
+ schema_name, property_name, threshold, valid_values, quoting_config
811
+ )
833
812
  )
834
813
  else:
835
814
  logger.warning("Quality check invalidValues is only supported at field level")
@@ -837,7 +816,9 @@ def check_quality_list(
837
816
  if property_name is not None:
838
817
  missing_values = quality.arguments.get("missingValues") if quality.arguments else None
839
818
  checks.append(
840
- check_property_missing_values(schema_name, property_name, threshold, missing_values, quoting_config)
819
+ check_property_missing_values(
820
+ schema_name, property_name, threshold, missing_values, quoting_config
821
+ )
841
822
  )
842
823
  else:
843
824
  logger.warning("Quality check missingValues is only supported at field level")
@@ -863,10 +844,7 @@ def prepare_query(
863
844
 
864
845
  query = quality.query
865
846
 
866
- if quoting_config.quote_field_name:
867
- field_name_for_soda = f'"{field_name}"'
868
- else:
869
- field_name_for_soda = field_name
847
+ field_name_for_soda = _quote_field_name(field_name, quoting_config)
870
848
 
871
849
  if quoting_config.quote_model_name:
872
850
  model_name_for_soda = f'"{model_name}"'
@@ -1050,11 +1028,12 @@ def to_servicelevel_retention_check(data_contract: OpenDataContractStandard, sla
1050
1028
  logger.info(f"Model {model_name} not found in schema, skipping retention check")
1051
1029
  return None
1052
1030
 
1053
- # Parse ISO 8601 duration to seconds
1054
- retention_period = sla.value
1055
- seconds = _parse_iso8601_to_seconds(retention_period)
1031
+ # Convert retention value to seconds
1032
+ # Supports both numeric value + unit (ODCS style: value=3, unit=y)
1033
+ # and ISO 8601 duration strings (e.g., "P1Y")
1034
+ seconds = _retention_value_to_seconds(sla.value, sla.unit)
1056
1035
  if seconds is None:
1057
- logger.info(f"Could not parse retention period {retention_period}, skipping retention check")
1036
+ logger.info(f"Could not parse retention period (value={sla.value}, unit={sla.unit}), skipping retention check")
1058
1037
  return None
1059
1038
 
1060
1039
  check_type = "servicelevel_retention"
@@ -1083,6 +1062,43 @@ def to_servicelevel_retention_check(data_contract: OpenDataContractStandard, sla
1083
1062
  )
1084
1063
 
1085
1064
 
1065
+ def _retention_value_to_seconds(value, unit: str | None) -> int | None:
1066
+ """Convert a retention value to seconds.
1067
+
1068
+ Supports:
1069
+ - Numeric value with unit (ODCS style): value=3, unit="y"
1070
+ - ISO 8601 duration string: value="P1Y"
1071
+ """
1072
+ if value is None:
1073
+ return None
1074
+
1075
+ # If value is numeric, use the unit to convert to seconds
1076
+ if isinstance(value, (int, float)):
1077
+ numeric_value = int(value)
1078
+ unit_lower = unit.lower() if unit else "d"
1079
+ if unit_lower in ("y", "yr", "year", "years"):
1080
+ return numeric_value * 365 * 24 * 60 * 60
1081
+ elif unit_lower in ("m", "mo", "month", "months"):
1082
+ return numeric_value * 30 * 24 * 60 * 60
1083
+ elif unit_lower in ("d", "day", "days"):
1084
+ return numeric_value * 24 * 60 * 60
1085
+ elif unit_lower in ("h", "hr", "hour", "hours"):
1086
+ return numeric_value * 60 * 60
1087
+ elif unit_lower in ("min", "minute", "minutes"):
1088
+ return numeric_value * 60
1089
+ elif unit_lower in ("s", "sec", "second", "seconds"):
1090
+ return numeric_value
1091
+ else:
1092
+ logger.info(f"Unsupported retention unit: {unit}")
1093
+ return None
1094
+
1095
+ # If value is a string, try ISO 8601 parsing
1096
+ if isinstance(value, str):
1097
+ return _parse_iso8601_to_seconds(value)
1098
+
1099
+ return None
1100
+
1101
+
1086
1102
  def _parse_iso8601_to_seconds(duration: str) -> int | None:
1087
1103
  """Parse ISO 8601 duration to seconds."""
1088
1104
  if not duration:
@@ -1125,4 +1141,3 @@ def _parse_iso8601_to_seconds(duration: str) -> int | None:
1125
1141
  return int(match.group(1))
1126
1142
 
1127
1143
  return None
1128
-
@@ -36,11 +36,7 @@ def execute_data_contract_test(
36
36
  reason="Schema block is missing. Skip executing tests.",
37
37
  engine="datacontract",
38
38
  )
39
- if (
40
- server_name is None
41
- and data_contract.servers is not None
42
- and len(data_contract.servers) > 0
43
- ):
39
+ if server_name is None and data_contract.servers is not None and len(data_contract.servers) > 0:
44
40
  server_name = data_contract.servers[0].server
45
41
  server = get_server(data_contract, server_name)
46
42
  run.log_info(f"Running tests for data contract {data_contract.id} with server {server_name}")
@@ -96,7 +96,7 @@ def check_soda_execute(
96
96
  logging.info("Use Spark to connect to data source")
97
97
  scan.add_spark_session(spark, data_source_name="datacontract-cli")
98
98
  scan.set_data_source_name("datacontract-cli")
99
-
99
+
100
100
  # ------------------------------------------------------------------
101
101
  # NEW: native Impala server type
102
102
  # ------------------------------------------------------------------
@@ -107,7 +107,6 @@ def check_soda_execute(
107
107
  # data source name must match what we configure in to_impala_soda_configuration
108
108
  scan.set_data_source_name("impala")
109
109
 
110
-
111
110
  elif server.type == "kafka":
112
111
  if spark is None:
113
112
  spark = create_spark_session()
@@ -231,4 +230,4 @@ def update_reason(check, c):
231
230
  # print(check.reason)
232
231
  break # Exit the loop once the desired block is found
233
232
  if "fail" in c["diagnostics"]:
234
- check.reason = f"Value: {c['diagnostics']['value']} Fail: {c['diagnostics']['fail']}"
233
+ check.reason = f"Value: {c['diagnostics']['value']} Fail: {c['diagnostics']['fail']}"
@@ -0,0 +1,24 @@
1
+ import os
2
+
3
+ import yaml
4
+
5
+
6
+ # https://docs.soda.io/soda/connect-bigquery.html#authentication-methods
7
+ def to_bigquery_soda_configuration(server):
8
+ data_source = {
9
+ "type": "bigquery",
10
+ "project_id": server.project,
11
+ "dataset": server.dataset,
12
+ }
13
+
14
+ if "DATACONTRACT_BIGQUERY_ACCOUNT_INFO_JSON_PATH" in os.environ:
15
+ data_source["account_info_json_path"] = os.environ["DATACONTRACT_BIGQUERY_ACCOUNT_INFO_JSON_PATH"]
16
+ data_source["auth_scopes"] = ["https://www.googleapis.com/auth/bigquery"]
17
+ else:
18
+ data_source["use_context_auth"] = True
19
+
20
+ if "DATACONTRACT_BIGQUERY_IMPERSONATION_ACCOUNT" in os.environ:
21
+ data_source["impersonation_account"] = os.environ["DATACONTRACT_BIGQUERY_IMPERSONATION_ACCOUNT"]
22
+
23
+ soda_configuration_str = yaml.dump({f"data_source {server.type}": data_source})
24
+ return soda_configuration_str