datacontract-cli 0.9.6.post2__py3-none-any.whl → 0.9.8__py3-none-any.whl

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 (60) hide show
  1. datacontract/breaking/breaking.py +139 -63
  2. datacontract/breaking/breaking_rules.py +71 -54
  3. datacontract/cli.py +138 -45
  4. datacontract/data_contract.py +316 -78
  5. datacontract/engines/datacontract/check_that_datacontract_contains_valid_servers_configuration.py +5 -1
  6. datacontract/engines/datacontract/check_that_datacontract_file_exists.py +9 -8
  7. datacontract/engines/datacontract/check_that_datacontract_str_is_valid.py +26 -22
  8. datacontract/engines/fastjsonschema/check_jsonschema.py +31 -25
  9. datacontract/engines/fastjsonschema/s3/s3_read_files.py +8 -6
  10. datacontract/engines/soda/check_soda_execute.py +46 -35
  11. datacontract/engines/soda/connections/bigquery.py +5 -3
  12. datacontract/engines/soda/connections/dask.py +0 -1
  13. datacontract/engines/soda/connections/databricks.py +2 -2
  14. datacontract/engines/soda/connections/duckdb.py +4 -4
  15. datacontract/engines/soda/connections/kafka.py +36 -17
  16. datacontract/engines/soda/connections/postgres.py +3 -3
  17. datacontract/engines/soda/connections/snowflake.py +4 -4
  18. datacontract/export/avro_converter.py +3 -7
  19. datacontract/export/avro_idl_converter.py +280 -0
  20. datacontract/export/dbt_converter.py +55 -80
  21. datacontract/export/great_expectations_converter.py +141 -0
  22. datacontract/export/jsonschema_converter.py +3 -1
  23. datacontract/export/odcs_converter.py +10 -12
  24. datacontract/export/protobuf_converter.py +99 -0
  25. datacontract/export/pydantic_converter.py +140 -0
  26. datacontract/export/rdf_converter.py +35 -12
  27. datacontract/export/sodacl_converter.py +24 -24
  28. datacontract/export/sql_converter.py +93 -0
  29. datacontract/export/sql_type_converter.py +131 -0
  30. datacontract/export/terraform_converter.py +71 -0
  31. datacontract/imports/avro_importer.py +106 -0
  32. datacontract/imports/sql_importer.py +0 -2
  33. datacontract/init/download_datacontract_file.py +2 -2
  34. datacontract/integration/publish_datamesh_manager.py +4 -9
  35. datacontract/integration/publish_opentelemetry.py +107 -0
  36. datacontract/lint/files.py +2 -2
  37. datacontract/lint/lint.py +46 -31
  38. datacontract/lint/linters/description_linter.py +34 -0
  39. datacontract/lint/linters/example_model_linter.py +67 -43
  40. datacontract/lint/linters/field_pattern_linter.py +34 -0
  41. datacontract/lint/linters/field_reference_linter.py +38 -0
  42. datacontract/lint/linters/notice_period_linter.py +55 -0
  43. datacontract/lint/linters/primary_field_linter.py +28 -0
  44. datacontract/lint/linters/quality_schema_linter.py +52 -0
  45. datacontract/lint/linters/valid_constraints_linter.py +99 -0
  46. datacontract/lint/resolve.py +53 -8
  47. datacontract/lint/schema.py +2 -3
  48. datacontract/lint/urls.py +4 -5
  49. datacontract/model/breaking_change.py +27 -5
  50. datacontract/model/data_contract_specification.py +45 -25
  51. datacontract/model/exceptions.py +13 -2
  52. datacontract/model/run.py +1 -1
  53. datacontract/web.py +5 -8
  54. {datacontract_cli-0.9.6.post2.dist-info → datacontract_cli-0.9.8.dist-info}/METADATA +207 -35
  55. datacontract_cli-0.9.8.dist-info/RECORD +63 -0
  56. {datacontract_cli-0.9.6.post2.dist-info → datacontract_cli-0.9.8.dist-info}/WHEEL +1 -1
  57. datacontract_cli-0.9.6.post2.dist-info/RECORD +0 -47
  58. {datacontract_cli-0.9.6.post2.dist-info → datacontract_cli-0.9.8.dist-info}/LICENSE +0 -0
  59. {datacontract_cli-0.9.6.post2.dist-info → datacontract_cli-0.9.8.dist-info}/entry_points.txt +0 -0
  60. {datacontract_cli-0.9.6.post2.dist-info → datacontract_cli-0.9.8.dist-info}/top_level.txt +0 -0
@@ -1,33 +1,75 @@
1
1
  import json
2
2
  import logging
3
3
  import tempfile
4
+ import typing
4
5
 
5
6
  import yaml
6
7
 
7
- from datacontract.breaking.breaking import models_breaking_changes
8
- from datacontract.engines.datacontract.check_that_datacontract_contains_valid_servers_configuration import \
9
- check_that_datacontract_contains_valid_server_configuration
8
+ from datacontract.breaking.breaking import models_breaking_changes, \
9
+ quality_breaking_changes
10
+ from datacontract.engines.datacontract.check_that_datacontract_contains_valid_servers_configuration import (
11
+ check_that_datacontract_contains_valid_server_configuration,
12
+ )
10
13
  from datacontract.engines.fastjsonschema.check_jsonschema import \
11
14
  check_jsonschema
12
15
  from datacontract.engines.soda.check_soda_execute import check_soda_execute
13
- from datacontract.export.avro_converter import to_avro_schema, to_avro_schema_json
16
+ from datacontract.export.avro_converter import to_avro_schema_json
17
+ from datacontract.export.avro_idl_converter import to_avro_idl
14
18
  from datacontract.export.dbt_converter import to_dbt_models_yaml, \
15
19
  to_dbt_sources_yaml, to_dbt_staging_sql
16
- from datacontract.export.jsonschema_converter import to_jsonschema, to_jsonschema_json
20
+ from datacontract.export.great_expectations_converter import \
21
+ to_great_expectations
22
+ from datacontract.export.jsonschema_converter import to_jsonschema_json
17
23
  from datacontract.export.odcs_converter import to_odcs_yaml
18
- from datacontract.export.rdf_converter import to_rdf, to_rdf_n3
24
+ from datacontract.export.protobuf_converter import to_protobuf
25
+ from datacontract.export.pydantic_converter import to_pydantic_model_str
26
+ from datacontract.export.rdf_converter import to_rdf_n3
19
27
  from datacontract.export.sodacl_converter import to_sodacl_yaml
28
+ from datacontract.export.sql_converter import to_sql_ddl, to_sql_query
29
+ from datacontract.export.terraform_converter import to_terraform
30
+ from datacontract.imports.avro_importer import import_avro
20
31
  from datacontract.imports.sql_importer import import_sql
21
32
  from datacontract.integration.publish_datamesh_manager import \
22
33
  publish_datamesh_manager
34
+ from datacontract.integration.publish_opentelemetry import publish_opentelemetry
23
35
  from datacontract.lint import resolve
36
+ from datacontract.lint.linters.description_linter import DescriptionLinter
24
37
  from datacontract.lint.linters.example_model_linter import ExampleModelLinter
25
- from datacontract.model.breaking_change import BreakingChanges
38
+ from datacontract.lint.linters.field_pattern_linter import FieldPatternLinter
39
+ from datacontract.lint.linters.field_reference_linter import \
40
+ FieldReferenceLinter
41
+ from datacontract.lint.linters.notice_period_linter import NoticePeriodLinter
42
+ from datacontract.lint.linters.primary_field_linter import \
43
+ PrimaryFieldUniqueRequired
44
+ from datacontract.lint.linters.quality_schema_linter import \
45
+ QualityUsesSchemaLinter
46
+ from datacontract.lint.linters.valid_constraints_linter import \
47
+ ValidFieldConstraintsLinter
48
+ from datacontract.model.breaking_change import BreakingChanges, BreakingChange, \
49
+ Severity
26
50
  from datacontract.model.data_contract_specification import \
27
51
  DataContractSpecification, Server
28
52
  from datacontract.model.exceptions import DataContractException
29
- from datacontract.model.run import \
30
- Run, Check
53
+ from datacontract.model.run import Run, Check
54
+
55
+
56
+ def _determine_sql_server_type(data_contract, sql_server_type):
57
+ if sql_server_type == "auto":
58
+ if data_contract.servers is None or len(data_contract.servers) == 0:
59
+ raise RuntimeError("Export with server_type='auto' requires servers in the data contract.")
60
+
61
+ server_types = set([server.type for server in data_contract.servers.values()])
62
+ if "snowflake" in server_types:
63
+ return "snowflake"
64
+ elif "postgres" in server_types:
65
+ return "postgres"
66
+ elif "databricks" in server_types:
67
+ return "databricks"
68
+ else:
69
+ # default to snowflake dialect
70
+ return "snowflake"
71
+ else:
72
+ return sql_server_type
31
73
 
32
74
 
33
75
  class DataContract:
@@ -40,7 +82,9 @@ class DataContract:
40
82
  server: str = None,
41
83
  examples: bool = False,
42
84
  publish_url: str = None,
85
+ publish_to_opentelemetry: bool = False,
43
86
  spark: str = None,
87
+ inline_definitions: bool = False,
44
88
  ):
45
89
  self._data_contract_file = data_contract_file
46
90
  self._data_contract_str = data_contract_str
@@ -49,45 +93,80 @@ class DataContract:
49
93
  self._server = server
50
94
  self._examples = examples
51
95
  self._publish_url = publish_url
96
+ self._publish_to_opentelemetry = publish_to_opentelemetry
52
97
  self._spark = spark
98
+ self._inline_definitions = inline_definitions
99
+ self.all_linters = {
100
+ ExampleModelLinter(),
101
+ QualityUsesSchemaLinter(),
102
+ FieldPatternLinter(),
103
+ FieldReferenceLinter(),
104
+ NoticePeriodLinter(),
105
+ PrimaryFieldUniqueRequired(),
106
+ ValidFieldConstraintsLinter(),
107
+ DescriptionLinter(),
108
+ }
53
109
 
54
110
  @classmethod
55
111
  def init(cls, template: str = "https://datacontract.com/datacontract.init.yaml") -> DataContractSpecification:
56
112
  return resolve.resolve_data_contract(data_contract_location=template)
57
113
 
58
- def lint(self) -> Run:
114
+ def lint(self, enabled_linters: typing.Union[str, set[str]] = "all") -> Run:
115
+ """Lint the data contract by deserializing the contract and checking the schema, as well as calling the configured linters.
116
+
117
+ enabled_linters can be either "all" or "none", or a set of linter IDs. The "schema" linter is always enabled, even with enabled_linters="none".
118
+ """
59
119
  run = Run.create_run()
60
120
  try:
61
121
  run.log_info("Linting data contract")
62
- data_contract = resolve.resolve_data_contract(self._data_contract_file, self._data_contract_str,
63
- self._data_contract, self._schema_location)
64
- run.checks.append(Check(
65
- type="lint",
66
- result="passed",
67
- name="Data contract is syntactically valid",
68
- engine="datacontract"
69
- ))
70
- run.checks.extend(ExampleModelLinter().lint(data_contract))
122
+ data_contract = resolve.resolve_data_contract(
123
+ self._data_contract_file,
124
+ self._data_contract_str,
125
+ self._data_contract,
126
+ self._schema_location,
127
+ inline_definitions=True,
128
+ )
129
+ run.checks.append(
130
+ Check(type="lint", result="passed", name="Data contract is syntactically valid", engine="datacontract")
131
+ )
132
+ if enabled_linters == "none":
133
+ linters_to_check = set()
134
+ elif enabled_linters == "all":
135
+ linters_to_check = self.all_linters
136
+ elif isinstance(enabled_linters, set):
137
+ linters_to_check = {linter for linter in self.all_linters if linter.id in enabled_linters}
138
+ else:
139
+ raise RuntimeError(f"Unknown argument enabled_linters={enabled_linters} for lint()")
140
+ for linter in linters_to_check:
141
+ try:
142
+ run.checks.extend(linter.lint(data_contract))
143
+ except Exception as e:
144
+ run.checks.append(
145
+ Check(
146
+ type="general",
147
+ result="error",
148
+ name=f"Linter '{linter.name}'",
149
+ reason=str(e),
150
+ engine="datacontract",
151
+ )
152
+ )
71
153
  run.dataContractId = data_contract.id
72
154
  run.dataContractVersion = data_contract.info.version
73
155
  except DataContractException as e:
74
- run.checks.append(Check(
75
- type=e.type,
76
- result=e.result,
77
- name=e.name,
78
- reason=e.reason,
79
- engine=e.engine,
80
- details=""
81
- ))
156
+ run.checks.append(
157
+ Check(type=e.type, result=e.result, name=e.name, reason=e.reason, engine=e.engine, details="")
158
+ )
82
159
  run.log_error(str(e))
83
160
  except Exception as e:
84
- run.checks.append(Check(
85
- type="general",
86
- result="error",
87
- name="Check Data Contract",
88
- reason=str(e),
89
- engine="datacontract",
90
- ))
161
+ run.checks.append(
162
+ Check(
163
+ type="general",
164
+ result="error",
165
+ name="Check Data Contract",
166
+ reason=str(e),
167
+ engine="datacontract",
168
+ )
169
+ )
91
170
  run.log_error(str(e))
92
171
  run.finish()
93
172
  return run
@@ -95,9 +174,10 @@ class DataContract:
95
174
  def test(self) -> Run:
96
175
  run = Run.create_run()
97
176
  try:
98
- run.log_info(f"Testing data contract")
99
- data_contract = resolve.resolve_data_contract(self._data_contract_file, self._data_contract_str,
100
- self._data_contract, self._schema_location)
177
+ run.log_info("Testing data contract")
178
+ data_contract = resolve.resolve_data_contract(
179
+ self._data_contract_file, self._data_contract_str, self._data_contract, self._schema_location
180
+ )
101
181
 
102
182
  if data_contract.models is None or len(data_contract.models) == 0:
103
183
  raise DataContractException(
@@ -143,51 +223,105 @@ class DataContract:
143
223
  check_soda_execute(run, data_contract, server, self._spark, tmp_dir)
144
224
 
145
225
  except DataContractException as e:
146
- run.checks.append(Check(
147
- type=e.type,
148
- result=e.result,
149
- name=e.name,
150
- reason=e.reason,
151
- engine=e.engine,
152
- details=""
153
- ))
226
+ run.checks.append(
227
+ Check(type=e.type, result=e.result, name=e.name, reason=e.reason, engine=e.engine, details="")
228
+ )
154
229
  run.log_error(str(e))
155
230
  except Exception as e:
156
- run.checks.append(Check(
157
- type="general",
158
- result="error",
159
- name="Test Data Contract",
160
- reason=str(e),
161
- engine="datacontract",
162
- ))
231
+ run.checks.append(
232
+ Check(
233
+ type="general",
234
+ result="error",
235
+ name="Test Data Contract",
236
+ reason=str(e),
237
+ engine="datacontract",
238
+ )
239
+ )
163
240
  logging.exception("Exception occurred")
164
241
  run.log_error(str(e))
165
242
 
166
243
  run.finish()
167
244
 
168
245
  if self._publish_url is not None:
169
- publish_datamesh_manager(run, self._publish_url)
246
+ try:
247
+ publish_datamesh_manager(run, self._publish_url)
248
+ except Exception:
249
+ logging.error("Failed to publish to datamesh manager")
250
+ if self._publish_to_opentelemetry:
251
+ try:
252
+ publish_opentelemetry(run)
253
+ except Exception:
254
+ logging.error("Failed to publish to opentelemetry")
170
255
 
171
256
  return run
172
257
 
173
- def breaking(self, other: 'DataContract') -> BreakingChanges:
258
+ def breaking(self, other: "DataContract") -> BreakingChanges:
259
+ return self.changelog(other, include_severities=[Severity.ERROR, Severity.WARNING])
260
+
261
+ def changelog(
262
+ self, other: "DataContract", include_severities: [Severity] = (Severity.ERROR, Severity.WARNING, Severity.INFO)
263
+ ) -> BreakingChanges:
174
264
  old = self.get_data_contract_specification()
175
265
  new = other.get_data_contract_specification()
176
- return models_breaking_changes(old_models=old.models, new_models=new.models, new_path=other._data_contract_file)
177
266
 
178
- def get_data_contract_specification(self):
179
- return resolve.resolve_data_contract(self._data_contract_file, self._data_contract_str,
180
- self._data_contract, self._schema_location)
267
+ breaking_changes = list[BreakingChange]()
181
268
 
182
- def export(self, export_format, rdf_base: str = None) -> str:
183
- data_contract = resolve.resolve_data_contract(self._data_contract_file, self._data_contract_str,
184
- self._data_contract)
269
+ breaking_changes.extend(
270
+ quality_breaking_changes(
271
+ old_quality=old.quality,
272
+ new_quality=new.quality,
273
+ new_path=other._data_contract_file,
274
+ include_severities=include_severities,
275
+ )
276
+ )
277
+
278
+ breaking_changes.extend(
279
+ models_breaking_changes(
280
+ old_models=old.models,
281
+ new_models=new.models,
282
+ new_path=other._data_contract_file,
283
+ include_severities=include_severities,
284
+ )
285
+ )
286
+
287
+ return BreakingChanges(breaking_changes=breaking_changes)
288
+
289
+ def get_data_contract_specification(self) -> DataContractSpecification:
290
+ return resolve.resolve_data_contract(
291
+ data_contract_location=self._data_contract_file,
292
+ data_contract_str=self._data_contract_str,
293
+ data_contract=self._data_contract,
294
+ schema_location=self._schema_location,
295
+ inline_definitions=self._inline_definitions,
296
+ )
297
+
298
+ def export(self, export_format, model: str = "all", rdf_base: str = None, sql_server_type: str = "auto") -> str:
299
+ data_contract = resolve.resolve_data_contract(
300
+ self._data_contract_file, self._data_contract_str, self._data_contract, inline_definitions=True
301
+ )
185
302
  if export_format == "jsonschema":
186
- if data_contract.models is None or len(data_contract.models.items()) != 1:
187
- print(f"Export to {export_format} currently only works with exactly one model in the data contract.")
188
- return ""
189
- model_name, model = next(iter(data_contract.models.items()))
190
- return to_jsonschema_json(model_name, model)
303
+ if data_contract.models is None:
304
+ raise RuntimeError(f"Export to {export_format} requires models in the data contract.")
305
+
306
+ model_names = list(data_contract.models.keys())
307
+
308
+ if model == "all":
309
+ if len(data_contract.models.items()) != 1:
310
+ raise RuntimeError(
311
+ f"Export to {export_format} is model specific. Specify the model via --model $MODEL_NAME. Available models: {model_names}"
312
+ )
313
+
314
+ model_name, model_value = next(iter(data_contract.models.items()))
315
+ return to_jsonschema_json(model_name, model_value)
316
+ else:
317
+ model_name = model
318
+ model_value = data_contract.models.get(model_name)
319
+ if model_value is None:
320
+ raise RuntimeError(
321
+ f"Model {model_name} not found in the data contract. Available models: {model_names}"
322
+ )
323
+
324
+ return to_jsonschema_json(model_name, model_value)
191
325
  if export_format == "sodacl":
192
326
  return to_sodacl_yaml(data_contract)
193
327
  if export_format == "dbt":
@@ -195,17 +329,116 @@ class DataContract:
195
329
  if export_format == "dbt-sources":
196
330
  return to_dbt_sources_yaml(data_contract, self._server)
197
331
  if export_format == "dbt-staging-sql":
198
- return to_dbt_staging_sql(data_contract)
332
+ if data_contract.models is None:
333
+ raise RuntimeError(f"Export to {export_format} requires models in the data contract.")
334
+
335
+ model_names = list(data_contract.models.keys())
336
+
337
+ if model == "all":
338
+ if len(data_contract.models.items()) != 1:
339
+ raise RuntimeError(
340
+ f"Export to {export_format} is model specific. Specify the model via --model $MODEL_NAME. Available models: {model_names}"
341
+ )
342
+
343
+ model_name, model_value = next(iter(data_contract.models.items()))
344
+ return to_dbt_staging_sql(data_contract, model_name, model_value)
345
+ else:
346
+ model_name = model
347
+ model_value = data_contract.models.get(model_name)
348
+ if model_value is None:
349
+ raise RuntimeError(
350
+ f"Model {model_name} not found in the data contract. Available models: {model_names}"
351
+ )
352
+
353
+ return to_dbt_staging_sql(data_contract, model_name, model_value)
199
354
  if export_format == "odcs":
200
355
  return to_odcs_yaml(data_contract)
201
356
  if export_format == "rdf":
202
357
  return to_rdf_n3(data_contract, rdf_base)
358
+ if export_format == "protobuf":
359
+ return to_protobuf(data_contract)
203
360
  if export_format == "avro":
204
- if data_contract.models is None or len(data_contract.models.items()) != 1:
205
- print(f"Export to {export_format} currently only works with exactly one model in the data contract.")
206
- return ""
207
- model_name, model = next(iter(data_contract.models.items()))
208
- return to_avro_schema_json(model_name, model)
361
+ if data_contract.models is None:
362
+ raise RuntimeError(f"Export to {export_format} requires models in the data contract.")
363
+
364
+ model_names = list(data_contract.models.keys())
365
+
366
+ if model == "all":
367
+ if len(data_contract.models.items()) != 1:
368
+ raise RuntimeError(
369
+ f"Export to {export_format} is model specific. Specify the model via --model $MODEL_NAME. Available models: {model_names}"
370
+ )
371
+
372
+ model_name, model_value = next(iter(data_contract.models.items()))
373
+ return to_avro_schema_json(model_name, model_value)
374
+ else:
375
+ model_name = model
376
+ model_value = data_contract.models.get(model_name)
377
+ if model_value is None:
378
+ raise RuntimeError(
379
+ f"Model {model_name} not found in the data contract. Available models: {model_names}"
380
+ )
381
+
382
+ return to_avro_schema_json(model_name, model_value)
383
+ if export_format == "avro-idl":
384
+ return to_avro_idl(data_contract)
385
+ if export_format == "terraform":
386
+ return to_terraform(data_contract)
387
+ if export_format == "sql":
388
+ server_type = _determine_sql_server_type(data_contract, sql_server_type)
389
+ return to_sql_ddl(data_contract, server_type=server_type)
390
+ if export_format == "sql-query":
391
+ if data_contract.models is None:
392
+ raise RuntimeError(f"Export to {export_format} requires models in the data contract.")
393
+
394
+ server_type = _determine_sql_server_type(data_contract, sql_server_type)
395
+
396
+ model_names = list(data_contract.models.keys())
397
+
398
+ if model == "all":
399
+ if len(data_contract.models.items()) != 1:
400
+ raise RuntimeError(
401
+ f"Export to {export_format} is model specific. Specify the model via --model $MODEL_NAME. Available models: {model_names}"
402
+ )
403
+
404
+ model_name, model_value = next(iter(data_contract.models.items()))
405
+ return to_sql_query(data_contract, model_name, model_value, server_type)
406
+ else:
407
+ model_name = model
408
+ model_value = data_contract.models.get(model_name)
409
+ if model_value is None:
410
+ raise RuntimeError(
411
+ f"Model {model_name} not found in the data contract. Available models: {model_names}"
412
+ )
413
+
414
+ return to_sql_query(data_contract, model_name, model_value, server_type)
415
+
416
+ if export_format == "great-expectations":
417
+ if data_contract.models is None:
418
+ raise RuntimeError(f"Export to {export_format} requires models in the data contract.")
419
+
420
+ model_names = list(data_contract.models.keys())
421
+
422
+ if model == "all":
423
+ if len(data_contract.models.items()) != 1:
424
+ raise RuntimeError(
425
+ f"Export to {export_format} is model specific. Specify the model via --model "
426
+ f"$MODEL_NAME. Available models: {model_names}"
427
+ )
428
+
429
+ model_name, model_value = next(iter(data_contract.models.items()))
430
+ return to_great_expectations(data_contract, model_name)
431
+ else:
432
+ model_name = model
433
+ model_value = data_contract.models.get(model_name)
434
+ if model_value is None:
435
+ raise RuntimeError(
436
+ f"Model {model_name} not found in the data contract. " f"Available models: {model_names}"
437
+ )
438
+
439
+ return to_great_expectations(data_contract, model_name)
440
+ if export_format == "pydantic-model":
441
+ return to_pydantic_model_str(data_contract)
209
442
  else:
210
443
  print(f"Export format {export_format} not supported.")
211
444
  return ""
@@ -219,13 +452,13 @@ class DataContract:
219
452
  run.log_info(f"Creating example file {p}")
220
453
  with open(p, "w") as f:
221
454
  content = ""
222
- if format == "json" and type(example.data) is list:
455
+ if format == "json" and isinstance(example.data, list):
223
456
  content = json.dumps(example.data)
224
- elif format == "json" and type(example.data) is str:
457
+ elif format == "json" and isinstance(example.data, str):
225
458
  content = example.data
226
- elif format == "yaml" and type(example.data) is list:
227
- content = yaml.dump(example.data)
228
- elif format == "yaml" and type(example.data) is str:
459
+ elif format == "yaml" and isinstance(example.data, list):
460
+ content = yaml.dump(example.data, allow_unicode=True)
461
+ elif format == "yaml" and isinstance(example.data, str):
229
462
  content = example.data
230
463
  elif format == "csv":
231
464
  content = example.data
@@ -245,6 +478,11 @@ class DataContract:
245
478
  def import_from_source(self, format: str, source: str) -> DataContractSpecification:
246
479
  data_contract_specification = DataContract.init()
247
480
 
248
- data_contract_specification = import_sql(data_contract_specification, format, source)
481
+ if format == "sql":
482
+ data_contract_specification = import_sql(data_contract_specification, format, source)
483
+ elif format == "avro":
484
+ data_contract_specification = import_avro(data_contract_specification, source)
485
+ else:
486
+ print(f"Import format {format} not supported.")
249
487
 
250
488
  return data_contract_specification
@@ -3,7 +3,9 @@ from datacontract.model.exceptions import DataContractException
3
3
  from datacontract.model.run import Run
4
4
 
5
5
 
6
- def check_that_datacontract_contains_valid_server_configuration(run: Run, data_contract: DataContractSpecification, server_name: str):
6
+ def check_that_datacontract_contains_valid_server_configuration(
7
+ run: Run, data_contract: DataContractSpecification, server_name: str
8
+ ):
7
9
  if data_contract.servers is None:
8
10
  raise DataContractException(
9
11
  type="lint",
@@ -28,4 +30,6 @@ def check_that_datacontract_contains_valid_server_configuration(run: Run, data_c
28
30
  reason=f"Cannot find server '{server_name}' in the data contract servers configuration. Skip executing tests.",
29
31
  engine="datacontract",
30
32
  )
33
+
34
+
31
35
  # TODO check for server.type, if all required fields are present
@@ -9,12 +9,13 @@ def check_that_datacontract_file_exists(run: Run, file_path: str):
9
9
  if file_path.startswith("http://") or file_path.startswith("https://"):
10
10
  return
11
11
  if not os.path.exists(file_path):
12
- run.checks.append(Check(
13
- type="lint",
14
- name="Check that data contract file exists",
15
- result="failed",
16
- reason=f"The file '{file_path}' does not exist.",
17
- engine="datacontract-cli",
18
- ))
12
+ run.checks.append(
13
+ Check(
14
+ type="lint",
15
+ name="Check that data contract file exists",
16
+ result="failed",
17
+ reason=f"The file '{file_path}' does not exist.",
18
+ engine="datacontract-cli",
19
+ )
20
+ )
19
21
  raise Exception(f"The file '{file_path}' does not exist.")
20
-
@@ -14,31 +14,35 @@ def check_that_datacontract_str_is_valid(run: Run, data_contract_str: str):
14
14
  try:
15
15
  fastjsonschema.validate(schema, data_contract_yaml)
16
16
  logging.debug("YAML data is valid.")
17
- run.checks.append(Check(
18
- type="lint",
19
- result="passed",
20
- name="Check that data contract YAML is valid",
21
- engine="datacontract",
22
- ))
17
+ run.checks.append(
18
+ Check(
19
+ type="lint",
20
+ result="passed",
21
+ name="Check that data contract YAML is valid",
22
+ engine="datacontract",
23
+ )
24
+ )
23
25
  except JsonSchemaValueException as e:
24
26
  logging.warning("YAML data is invalid.")
25
27
  logging.warning(f"Validation error: {e.message}")
26
- run.checks.append(Check(
27
- type="lint",
28
- result="failed",
29
- name="Check that data contract YAML is valid",
30
- reason=e.message,
31
- engine="datacontract",
32
- ))
28
+ run.checks.append(
29
+ Check(
30
+ type="lint",
31
+ result="failed",
32
+ name="Check that data contract YAML is valid",
33
+ reason=e.message,
34
+ engine="datacontract",
35
+ )
36
+ )
33
37
  except Exception as e:
34
38
  logging.warning("YAML data is invalid.")
35
39
  logging.warning(f"Validation error: {str(e)}")
36
- run.checks.append(Check(
37
- type="lint",
38
- result="failed",
39
- name="Check that data contract YAML is valid",
40
- reason=str(e),
41
- engine="datacontract",
42
- ))
43
-
44
-
40
+ run.checks.append(
41
+ Check(
42
+ type="lint",
43
+ result="failed",
44
+ name="Check that data contract YAML is valid",
45
+ reason=str(e),
46
+ engine="datacontract",
47
+ )
48
+ )