datacontract-cli 0.10.7__py3-none-any.whl → 0.10.9__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.
- datacontract/catalog/catalog.py +4 -2
- datacontract/cli.py +44 -15
- datacontract/data_contract.py +52 -206
- datacontract/engines/fastjsonschema/s3/s3_read_files.py +13 -1
- datacontract/engines/soda/check_soda_execute.py +9 -2
- datacontract/engines/soda/connections/bigquery.py +8 -1
- datacontract/engines/soda/connections/duckdb.py +28 -12
- datacontract/engines/soda/connections/trino.py +26 -0
- datacontract/export/__init__.py +0 -0
- datacontract/export/avro_converter.py +15 -3
- datacontract/export/avro_idl_converter.py +29 -22
- datacontract/export/bigquery_converter.py +15 -0
- datacontract/export/dbml_converter.py +9 -0
- datacontract/export/dbt_converter.py +26 -1
- datacontract/export/exporter.py +88 -0
- datacontract/export/exporter_factory.py +145 -0
- datacontract/export/go_converter.py +6 -0
- datacontract/export/great_expectations_converter.py +10 -0
- datacontract/export/html_export.py +6 -0
- datacontract/export/jsonschema_converter.py +31 -23
- datacontract/export/odcs_converter.py +24 -1
- datacontract/export/protobuf_converter.py +6 -0
- datacontract/export/pydantic_converter.py +6 -0
- datacontract/export/rdf_converter.py +9 -0
- datacontract/export/sodacl_converter.py +23 -12
- datacontract/export/spark_converter.py +211 -0
- datacontract/export/sql_converter.py +32 -2
- datacontract/export/sql_type_converter.py +32 -5
- datacontract/export/terraform_converter.py +6 -0
- datacontract/imports/avro_importer.py +8 -0
- datacontract/imports/bigquery_importer.py +47 -4
- datacontract/imports/glue_importer.py +122 -30
- datacontract/imports/importer.py +29 -0
- datacontract/imports/importer_factory.py +72 -0
- datacontract/imports/jsonschema_importer.py +8 -0
- datacontract/imports/odcs_importer.py +200 -0
- datacontract/imports/sql_importer.py +8 -0
- datacontract/imports/unity_importer.py +152 -0
- datacontract/lint/resolve.py +22 -1
- datacontract/model/data_contract_specification.py +36 -4
- datacontract/templates/datacontract.html +17 -2
- datacontract/templates/partials/datacontract_information.html +20 -0
- datacontract/templates/partials/datacontract_terms.html +7 -0
- datacontract/templates/partials/definition.html +9 -1
- datacontract/templates/partials/model_field.html +23 -6
- datacontract/templates/partials/server.html +113 -48
- datacontract/templates/style/output.css +51 -0
- datacontract/web.py +17 -0
- {datacontract_cli-0.10.7.dist-info → datacontract_cli-0.10.9.dist-info}/METADATA +298 -59
- datacontract_cli-0.10.9.dist-info/RECORD +93 -0
- {datacontract_cli-0.10.7.dist-info → datacontract_cli-0.10.9.dist-info}/WHEEL +1 -1
- datacontract_cli-0.10.7.dist-info/RECORD +0 -84
- {datacontract_cli-0.10.7.dist-info → datacontract_cli-0.10.9.dist-info}/LICENSE +0 -0
- {datacontract_cli-0.10.7.dist-info → datacontract_cli-0.10.9.dist-info}/entry_points.txt +0 -0
- {datacontract_cli-0.10.7.dist-info → datacontract_cli-0.10.9.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import json
|
|
2
2
|
|
|
3
|
+
from datacontract.export.exporter import Exporter, _check_models_for_export
|
|
3
4
|
from datacontract.model.data_contract_specification import Field
|
|
4
5
|
|
|
5
6
|
|
|
7
|
+
class AvroExporter(Exporter):
|
|
8
|
+
def export(self, data_contract, model, server, sql_server_type, export_args) -> dict:
|
|
9
|
+
model_name, model_value = _check_models_for_export(data_contract, model, self.export_format)
|
|
10
|
+
return to_avro_schema_json(model_name, model_value)
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
def to_avro_schema(model_name, model) -> dict:
|
|
7
14
|
return to_avro_record(model_name, model.fields, model.description, model.namespace)
|
|
8
15
|
|
|
@@ -47,7 +54,13 @@ def to_avro_type(field: Field, field_name: str) -> str | dict:
|
|
|
47
54
|
if "avroLogicalType" in field.config and "avroType" in field.config:
|
|
48
55
|
return {"type": field.config["avroType"], "logicalType": field.config["avroLogicalType"]}
|
|
49
56
|
if "avroLogicalType" in field.config:
|
|
50
|
-
if field.config["avroLogicalType"] in [
|
|
57
|
+
if field.config["avroLogicalType"] in [
|
|
58
|
+
"timestamp-millis",
|
|
59
|
+
"timestamp-micros",
|
|
60
|
+
"local-timestamp-millis",
|
|
61
|
+
"local-timestamp-micros",
|
|
62
|
+
"time-micros",
|
|
63
|
+
]:
|
|
51
64
|
return {"type": "long", "logicalType": field.config["avroLogicalType"]}
|
|
52
65
|
if field.config["avroLogicalType"] in ["time-millis", "date"]:
|
|
53
66
|
return {"type": "int", "logicalType": field.config["avroLogicalType"]}
|
|
@@ -82,8 +95,7 @@ def to_avro_type(field: Field, field_name: str) -> str | dict:
|
|
|
82
95
|
elif field.type in ["binary"]:
|
|
83
96
|
return "bytes"
|
|
84
97
|
elif field.type in ["array"]:
|
|
85
|
-
|
|
86
|
-
return "array"
|
|
98
|
+
return {"type": "array", "items": to_avro_type(field.items, field_name)}
|
|
87
99
|
elif field.type in ["null"]:
|
|
88
100
|
return "null"
|
|
89
101
|
else:
|
|
@@ -7,28 +7,7 @@ from datacontract.lint.resolve import inline_definitions_into_data_contract
|
|
|
7
7
|
from datacontract.model.data_contract_specification import DataContractSpecification, Field
|
|
8
8
|
from datacontract.model.exceptions import DataContractException
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
def to_avro_idl(contract: DataContractSpecification) -> str:
|
|
12
|
-
"""Serialize the provided data contract specification into an Avro IDL string.
|
|
13
|
-
|
|
14
|
-
The data contract will be serialized as a protocol, with one record type
|
|
15
|
-
for each contained model. Model fields are mapped one-to-one to Avro IDL
|
|
16
|
-
record fields.
|
|
17
|
-
"""
|
|
18
|
-
stream = StringIO()
|
|
19
|
-
to_avro_idl_stream(contract, stream)
|
|
20
|
-
return stream.getvalue()
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def to_avro_idl_stream(contract: DataContractSpecification, stream: typing.TextIO):
|
|
24
|
-
"""Serialize the provided data contract specification into Avro IDL."""
|
|
25
|
-
ir = _contract_to_avro_idl_ir(contract)
|
|
26
|
-
if ir.description:
|
|
27
|
-
stream.write(f"/** {contract.info.description} */\n")
|
|
28
|
-
stream.write(f"protocol {ir.name or 'Unnamed'} {{\n")
|
|
29
|
-
for model_type in ir.model_types:
|
|
30
|
-
_write_model_type(model_type, stream)
|
|
31
|
-
stream.write("}\n")
|
|
10
|
+
from datacontract.export.exporter import Exporter
|
|
32
11
|
|
|
33
12
|
|
|
34
13
|
class AvroPrimitiveType(Enum):
|
|
@@ -107,6 +86,34 @@ avro_primitive_types = set(
|
|
|
107
86
|
)
|
|
108
87
|
|
|
109
88
|
|
|
89
|
+
class AvroIdlExporter(Exporter):
|
|
90
|
+
def export(self, data_contract, model, server, sql_server_type, export_args) -> dict:
|
|
91
|
+
return to_avro_idl(data_contract)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def to_avro_idl(contract: DataContractSpecification) -> str:
|
|
95
|
+
"""Serialize the provided data contract specification into an Avro IDL string.
|
|
96
|
+
|
|
97
|
+
The data contract will be serialized as a protocol, with one record type
|
|
98
|
+
for each contained model. Model fields are mapped one-to-one to Avro IDL
|
|
99
|
+
record fields.
|
|
100
|
+
"""
|
|
101
|
+
stream = StringIO()
|
|
102
|
+
to_avro_idl_stream(contract, stream)
|
|
103
|
+
return stream.getvalue()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def to_avro_idl_stream(contract: DataContractSpecification, stream: typing.TextIO):
|
|
107
|
+
"""Serialize the provided data contract specification into Avro IDL."""
|
|
108
|
+
ir = _contract_to_avro_idl_ir(contract)
|
|
109
|
+
if ir.description:
|
|
110
|
+
stream.write(f"/** {contract.info.description} */\n")
|
|
111
|
+
stream.write(f"protocol {ir.name or 'Unnamed'} {{\n")
|
|
112
|
+
for model_type in ir.model_types:
|
|
113
|
+
_write_model_type(model_type, stream)
|
|
114
|
+
stream.write("}\n")
|
|
115
|
+
|
|
116
|
+
|
|
110
117
|
def _to_avro_primitive_logical_type(field_name: str, field: Field) -> AvroPrimitiveField:
|
|
111
118
|
result = AvroPrimitiveField(field_name, field.required, field.description, AvroPrimitiveType.string)
|
|
112
119
|
match field.type:
|
|
@@ -5,6 +5,21 @@ from typing import Dict, List
|
|
|
5
5
|
from datacontract.model.data_contract_specification import Model, Field, Server
|
|
6
6
|
from datacontract.model.exceptions import DataContractException
|
|
7
7
|
|
|
8
|
+
from datacontract.export.exporter import Exporter, _check_models_for_export
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BigQueryExporter(Exporter):
|
|
12
|
+
def export(self, data_contract, model, server, sql_server_type, export_args) -> dict:
|
|
13
|
+
self.dict_args = export_args
|
|
14
|
+
model_name, model_value = _check_models_for_export(data_contract, model, self.export_format)
|
|
15
|
+
found_server = data_contract.servers.get(server)
|
|
16
|
+
if found_server is None:
|
|
17
|
+
raise RuntimeError("Export to bigquery requires selecting a bigquery server from the data contract.")
|
|
18
|
+
if found_server.type != "bigquery":
|
|
19
|
+
raise RuntimeError("Export to bigquery requires selecting a bigquery server from the data contract.")
|
|
20
|
+
|
|
21
|
+
return to_bigquery_json(model_name, model_value, found_server)
|
|
22
|
+
|
|
8
23
|
|
|
9
24
|
def to_bigquery_json(model_name: str, model_value: Model, server: Server) -> str:
|
|
10
25
|
bigquery_table = to_bigquery_schema(model_name, model_value, server)
|
|
@@ -8,6 +8,15 @@ import datacontract.model.data_contract_specification as spec
|
|
|
8
8
|
from datacontract.export.sql_type_converter import convert_to_sql_type
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
from datacontract.export.exporter import Exporter
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DbmlExporter(Exporter):
|
|
15
|
+
def export(self, data_contract, model, server, sql_server_type, export_args) -> dict:
|
|
16
|
+
found_server = data_contract.servers.get(server)
|
|
17
|
+
return to_dbml_diagram(data_contract, found_server)
|
|
18
|
+
|
|
19
|
+
|
|
11
20
|
def to_dbml_diagram(contract: spec.DataContractSpecification, server: spec.Server) -> str:
|
|
12
21
|
result = ""
|
|
13
22
|
result += add_generated_info(contract, server) + "\n"
|
|
@@ -5,6 +5,28 @@ import yaml
|
|
|
5
5
|
from datacontract.export.sql_type_converter import convert_to_sql_type
|
|
6
6
|
from datacontract.model.data_contract_specification import DataContractSpecification, Model, Field
|
|
7
7
|
|
|
8
|
+
from datacontract.export.exporter import Exporter, _check_models_for_export
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DbtExporter(Exporter):
|
|
12
|
+
def export(self, data_contract, model, server, sql_server_type, export_args) -> dict:
|
|
13
|
+
return to_dbt_models_yaml(data_contract)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DbtSourceExporter(Exporter):
|
|
17
|
+
def export(self, data_contract, model, server, sql_server_type, export_args) -> dict:
|
|
18
|
+
return to_dbt_sources_yaml(data_contract, server)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DbtStageExporter(Exporter):
|
|
22
|
+
def export(self, data_contract, model, server, sql_server_type, export_args) -> dict:
|
|
23
|
+
model_name, model_value = _check_models_for_export(data_contract, model, self.export_format)
|
|
24
|
+
return to_dbt_staging_sql(
|
|
25
|
+
data_contract,
|
|
26
|
+
model_name,
|
|
27
|
+
model_value,
|
|
28
|
+
)
|
|
29
|
+
|
|
8
30
|
|
|
9
31
|
def to_dbt_models_yaml(data_contract_spec: DataContractSpecification):
|
|
10
32
|
dbt = {
|
|
@@ -19,7 +41,10 @@ def to_dbt_models_yaml(data_contract_spec: DataContractSpecification):
|
|
|
19
41
|
|
|
20
42
|
def to_dbt_staging_sql(data_contract_spec: DataContractSpecification, model_name: str, model_value: Model) -> str:
|
|
21
43
|
if data_contract_spec.models is None or len(data_contract_spec.models.items()) != 1:
|
|
22
|
-
print(
|
|
44
|
+
print(
|
|
45
|
+
"Export to dbt-staging-sql currently only works with exactly one model in the data contract."
|
|
46
|
+
"Please specify the model name."
|
|
47
|
+
)
|
|
23
48
|
return ""
|
|
24
49
|
|
|
25
50
|
id = data_contract_spec.id
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from enum import Enum
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from datacontract.model.data_contract_specification import DataContractSpecification
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Exporter(ABC):
|
|
9
|
+
def __init__(self, export_format) -> None:
|
|
10
|
+
self.export_format = export_format
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def export(self, data_contract, model, server, sql_server_type, export_args) -> dict:
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ExportFormat(str, Enum):
|
|
18
|
+
jsonschema = "jsonschema"
|
|
19
|
+
pydantic_model = "pydantic-model"
|
|
20
|
+
sodacl = "sodacl"
|
|
21
|
+
dbt = "dbt"
|
|
22
|
+
dbt_sources = "dbt-sources"
|
|
23
|
+
dbt_staging_sql = "dbt-staging-sql"
|
|
24
|
+
odcs = "odcs"
|
|
25
|
+
rdf = "rdf"
|
|
26
|
+
avro = "avro"
|
|
27
|
+
protobuf = "protobuf"
|
|
28
|
+
great_expectations = "great-expectations"
|
|
29
|
+
terraform = "terraform"
|
|
30
|
+
avro_idl = "avro-idl"
|
|
31
|
+
sql = "sql"
|
|
32
|
+
sql_query = "sql-query"
|
|
33
|
+
html = "html"
|
|
34
|
+
go = "go"
|
|
35
|
+
bigquery = "bigquery"
|
|
36
|
+
dbml = "dbml"
|
|
37
|
+
spark = "spark"
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def get_suported_formats(cls):
|
|
41
|
+
return list(map(lambda c: c.value, cls))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _check_models_for_export(
|
|
45
|
+
data_contract: DataContractSpecification, model: str, export_format: str
|
|
46
|
+
) -> typing.Tuple[str, str]:
|
|
47
|
+
if data_contract.models is None:
|
|
48
|
+
raise RuntimeError(f"Export to {export_format} requires models in the data contract.")
|
|
49
|
+
|
|
50
|
+
model_names = list(data_contract.models.keys())
|
|
51
|
+
|
|
52
|
+
if model == "all":
|
|
53
|
+
if len(data_contract.models.items()) != 1:
|
|
54
|
+
raise RuntimeError(
|
|
55
|
+
f"Export to {export_format} is model specific. Specify the model via --model $MODEL_NAME. Available models: {model_names}"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
model_name, model_value = next(iter(data_contract.models.items()))
|
|
59
|
+
else:
|
|
60
|
+
model_name = model
|
|
61
|
+
model_value = data_contract.models.get(model_name)
|
|
62
|
+
if model_value is None:
|
|
63
|
+
raise RuntimeError(f"Model {model_name} not found in the data contract. Available models: {model_names}")
|
|
64
|
+
|
|
65
|
+
return model_name, model_value
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _determine_sql_server_type(data_contract: DataContractSpecification, sql_server_type: str, server: str = None):
|
|
69
|
+
if sql_server_type == "auto":
|
|
70
|
+
if data_contract.servers is None or len(data_contract.servers) == 0:
|
|
71
|
+
raise RuntimeError("Export with server_type='auto' requires servers in the data contract.")
|
|
72
|
+
|
|
73
|
+
if server is None:
|
|
74
|
+
server_types = set([server.type for server in data_contract.servers.values()])
|
|
75
|
+
else:
|
|
76
|
+
server_types = {data_contract.servers[server].type}
|
|
77
|
+
|
|
78
|
+
if "snowflake" in server_types:
|
|
79
|
+
return "snowflake"
|
|
80
|
+
elif "postgres" in server_types:
|
|
81
|
+
return "postgres"
|
|
82
|
+
elif "databricks" in server_types:
|
|
83
|
+
return "databricks"
|
|
84
|
+
else:
|
|
85
|
+
# default to snowflake dialect
|
|
86
|
+
return "snowflake"
|
|
87
|
+
else:
|
|
88
|
+
return sql_server_type
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import sys
|
|
3
|
+
from datacontract.export.exporter import ExportFormat, Exporter
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ExporterFactory:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.dict_exporter = {}
|
|
9
|
+
self.dict_lazy_exporter = {}
|
|
10
|
+
|
|
11
|
+
def register_exporter(self, name: str, exporter: Exporter):
|
|
12
|
+
self.dict_exporter.update({name: exporter})
|
|
13
|
+
|
|
14
|
+
def register_lazy_exporter(self, name: str, module_path: str, class_name: str):
|
|
15
|
+
self.dict_lazy_exporter.update({name: (module_path, class_name)})
|
|
16
|
+
|
|
17
|
+
def create(self, name) -> Exporter:
|
|
18
|
+
exporters = self.dict_exporter.copy()
|
|
19
|
+
exporters.update(self.dict_lazy_exporter.copy())
|
|
20
|
+
if name not in exporters.keys():
|
|
21
|
+
raise ValueError(f"The '{name}' format is not supported.")
|
|
22
|
+
exporter_class = exporters[name]
|
|
23
|
+
if type(exporters[name]) is tuple:
|
|
24
|
+
exporter_class = load_module_class(module_path=exporters[name][0], class_name=exporters[name][1])
|
|
25
|
+
if not exporter_class:
|
|
26
|
+
raise ValueError(f"Module {name} could not be loaded.")
|
|
27
|
+
return exporter_class(name)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def import_module(module_path):
|
|
31
|
+
if importlib.util.find_spec(module_path) is not None:
|
|
32
|
+
try:
|
|
33
|
+
module = importlib.import_module(module_path)
|
|
34
|
+
except ModuleNotFoundError:
|
|
35
|
+
return None
|
|
36
|
+
sys.modules[module_path] = module
|
|
37
|
+
return module
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def load_module_class(module_path, class_name):
|
|
41
|
+
module = import_module(module_path)
|
|
42
|
+
if not module:
|
|
43
|
+
return None
|
|
44
|
+
return getattr(module, class_name)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
exporter_factory = ExporterFactory()
|
|
48
|
+
|
|
49
|
+
exporter_factory.register_lazy_exporter(
|
|
50
|
+
name=ExportFormat.avro, module_path="datacontract.export.avro_converter", class_name="AvroExporter"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
exporter_factory.register_lazy_exporter(
|
|
54
|
+
name=ExportFormat.avro_idl,
|
|
55
|
+
module_path="datacontract.export.avro_idl_converter",
|
|
56
|
+
class_name="AvroIdlExporter",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
exporter_factory.register_lazy_exporter(
|
|
60
|
+
name=ExportFormat.bigquery,
|
|
61
|
+
module_path="datacontract.export.bigquery_converter",
|
|
62
|
+
class_name="BigQueryExporter",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
exporter_factory.register_lazy_exporter(
|
|
66
|
+
name=ExportFormat.dbml, module_path="datacontract.export.dbml_converter", class_name="DbmlExporter"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
exporter_factory.register_lazy_exporter(
|
|
70
|
+
name=ExportFormat.rdf, module_path="datacontract.export.rdf_converter", class_name="RdfExporter"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
exporter_factory.register_lazy_exporter(
|
|
74
|
+
name=ExportFormat.dbt, module_path="datacontract.export.dbt_converter", class_name="DbtExporter"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
exporter_factory.register_lazy_exporter(
|
|
78
|
+
name=ExportFormat.dbt_sources,
|
|
79
|
+
module_path="datacontract.export.dbt_converter",
|
|
80
|
+
class_name="DbtSourceExporter",
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
exporter_factory.register_lazy_exporter(
|
|
84
|
+
name=ExportFormat.dbt_staging_sql,
|
|
85
|
+
module_path="datacontract.export.dbt_converter",
|
|
86
|
+
class_name="DbtStageExporter",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
exporter_factory.register_lazy_exporter(
|
|
90
|
+
name=ExportFormat.jsonschema,
|
|
91
|
+
module_path="datacontract.export.jsonschema_converter",
|
|
92
|
+
class_name="JsonSchemaExporter",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
exporter_factory.register_lazy_exporter(
|
|
96
|
+
name=ExportFormat.odcs, module_path="datacontract.export.odcs_converter", class_name="OdcsExporter"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
exporter_factory.register_lazy_exporter(
|
|
100
|
+
name=ExportFormat.go, module_path="datacontract.export.go_converter", class_name="GoExporter"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
exporter_factory.register_lazy_exporter(
|
|
104
|
+
name=ExportFormat.great_expectations,
|
|
105
|
+
module_path="datacontract.export.great_expectations_converter",
|
|
106
|
+
class_name="GreateExpectationsExporter",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
exporter_factory.register_lazy_exporter(
|
|
110
|
+
name=ExportFormat.html, module_path="datacontract.export.html_export", class_name="HtmlExporter"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
exporter_factory.register_lazy_exporter(
|
|
114
|
+
name=ExportFormat.protobuf,
|
|
115
|
+
module_path="datacontract.export.protobuf_converter",
|
|
116
|
+
class_name="ProtoBufExporter",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
exporter_factory.register_lazy_exporter(
|
|
120
|
+
name=ExportFormat.pydantic_model,
|
|
121
|
+
module_path="datacontract.export.pydantic_converter",
|
|
122
|
+
class_name="PydanticExporter",
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
exporter_factory.register_lazy_exporter(
|
|
126
|
+
name=ExportFormat.sodacl, module_path="datacontract.export.sodacl_converter", class_name="SodaExporter"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
exporter_factory.register_lazy_exporter(
|
|
130
|
+
name=ExportFormat.sql, module_path="datacontract.export.sql_converter", class_name="SqlExporter"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
exporter_factory.register_lazy_exporter(
|
|
134
|
+
name=ExportFormat.sql_query, module_path="datacontract.export.sql_converter", class_name="SqlQueryExporter"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
exporter_factory.register_lazy_exporter(
|
|
138
|
+
name=ExportFormat.terraform,
|
|
139
|
+
module_path="datacontract.export.terraform_converter",
|
|
140
|
+
class_name="TerraformExporter",
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
exporter_factory.register_lazy_exporter(
|
|
144
|
+
name=ExportFormat.spark, module_path="datacontract.export.spark_converter", class_name="SparkExporter"
|
|
145
|
+
)
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import datacontract.model.data_contract_specification as spec
|
|
2
2
|
from typing import List
|
|
3
3
|
import re
|
|
4
|
+
from datacontract.export.exporter import Exporter
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class GoExporter(Exporter):
|
|
8
|
+
def export(self, data_contract, model, server, sql_server_type, export_args) -> dict:
|
|
9
|
+
return to_go_types(data_contract)
|
|
4
10
|
|
|
5
11
|
|
|
6
12
|
def to_go_types(contract: spec.DataContractSpecification) -> str:
|
|
@@ -4,6 +4,16 @@ from typing import Dict, List, Any
|
|
|
4
4
|
import yaml
|
|
5
5
|
|
|
6
6
|
from datacontract.model.data_contract_specification import DataContractSpecification, Field, Quality
|
|
7
|
+
from datacontract.export.exporter import Exporter, _check_models_for_export
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class GreateExpectationsExporter(Exporter):
|
|
11
|
+
def export(self, data_contract, model, server, sql_server_type, export_args) -> dict:
|
|
12
|
+
model_name, model_value = _check_models_for_export(data_contract, model, self.export_format)
|
|
13
|
+
return to_great_expectations(
|
|
14
|
+
data_contract,
|
|
15
|
+
model_name,
|
|
16
|
+
)
|
|
7
17
|
|
|
8
18
|
|
|
9
19
|
def to_great_expectations(data_contract_spec: DataContractSpecification, model_key: str) -> str:
|
|
@@ -8,6 +8,12 @@ import yaml
|
|
|
8
8
|
from jinja2 import Environment, PackageLoader, select_autoescape
|
|
9
9
|
|
|
10
10
|
from datacontract.model.data_contract_specification import DataContractSpecification
|
|
11
|
+
from datacontract.export.exporter import Exporter
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class HtmlExporter(Exporter):
|
|
15
|
+
def export(self, data_contract, model, server, sql_server_type, export_args) -> dict:
|
|
16
|
+
return to_html(data_contract)
|
|
11
17
|
|
|
12
18
|
|
|
13
19
|
def to_html(data_contract_spec: DataContractSpecification) -> str:
|
|
@@ -3,6 +3,14 @@ from typing import Dict
|
|
|
3
3
|
|
|
4
4
|
from datacontract.model.data_contract_specification import DataContractSpecification, Model, Field
|
|
5
5
|
|
|
6
|
+
from datacontract.export.exporter import Exporter, _check_models_for_export
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class JsonSchemaExporter(Exporter):
|
|
10
|
+
def export(self, data_contract, model, server, sql_server_type, export_args) -> dict:
|
|
11
|
+
model_name, model_value = _check_models_for_export(data_contract, model, self.export_format)
|
|
12
|
+
return to_jsonschema_json(model_name, model_value)
|
|
13
|
+
|
|
6
14
|
|
|
7
15
|
def to_jsonschemas(data_contract_spec: DataContractSpecification):
|
|
8
16
|
jsonschmemas = {}
|
|
@@ -17,21 +25,6 @@ def to_jsonschema_json(model_key, model_value: Model) -> str:
|
|
|
17
25
|
return json.dumps(jsonschema, indent=2)
|
|
18
26
|
|
|
19
27
|
|
|
20
|
-
def to_jsonschema(model_key, model_value: Model) -> dict:
|
|
21
|
-
model = {
|
|
22
|
-
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
23
|
-
"type": "object",
|
|
24
|
-
"properties": to_properties(model_value.fields),
|
|
25
|
-
"required": to_required(model_value.fields),
|
|
26
|
-
}
|
|
27
|
-
if model_value.title:
|
|
28
|
-
model["title"] = model_value.title
|
|
29
|
-
if model_value.description:
|
|
30
|
-
model["description"] = model_value.description
|
|
31
|
-
|
|
32
|
-
return model
|
|
33
|
-
|
|
34
|
-
|
|
35
28
|
def to_properties(fields: Dict[str, Field]) -> dict:
|
|
36
29
|
properties = {}
|
|
37
30
|
for field_name, field in fields.items():
|
|
@@ -65,27 +58,27 @@ def to_property(field: Field) -> dict:
|
|
|
65
58
|
property["pattern"] = field.pattern
|
|
66
59
|
if field.enum:
|
|
67
60
|
property["enum"] = field.enum
|
|
68
|
-
if field.minLength:
|
|
61
|
+
if field.minLength is not None:
|
|
69
62
|
property["minLength"] = field.minLength
|
|
70
|
-
if field.maxLength:
|
|
63
|
+
if field.maxLength is not None:
|
|
71
64
|
property["maxLength"] = field.maxLength
|
|
72
65
|
if field.title:
|
|
73
66
|
property["title"] = field.title
|
|
74
67
|
if field.description:
|
|
75
68
|
property["description"] = field.description
|
|
76
|
-
if field.exclusiveMinimum:
|
|
69
|
+
if field.exclusiveMinimum is not None:
|
|
77
70
|
property["exclusiveMinimum"] = field.exclusiveMinimum
|
|
78
|
-
if field.exclusiveMaximum:
|
|
71
|
+
if field.exclusiveMaximum is not None:
|
|
79
72
|
property["exclusiveMaximum"] = field.exclusiveMaximum
|
|
80
|
-
if field.minimum:
|
|
73
|
+
if field.minimum is not None:
|
|
81
74
|
property["minimum"] = field.minimum
|
|
82
|
-
if field.maximum:
|
|
75
|
+
if field.maximum is not None:
|
|
83
76
|
property["maximum"] = field.maximum
|
|
84
77
|
if field.tags:
|
|
85
78
|
property["tags"] = field.tags
|
|
86
79
|
if field.pii:
|
|
87
80
|
property["pii"] = field.pii
|
|
88
|
-
if field.classification:
|
|
81
|
+
if field.classification is not None:
|
|
89
82
|
property["classification"] = field.classification
|
|
90
83
|
|
|
91
84
|
# TODO: all constraints
|
|
@@ -126,7 +119,7 @@ def convert_type_format(type, format) -> (str, str):
|
|
|
126
119
|
return None, None
|
|
127
120
|
|
|
128
121
|
|
|
129
|
-
def convert_format(format):
|
|
122
|
+
def convert_format(self, format):
|
|
130
123
|
if format is None:
|
|
131
124
|
return None
|
|
132
125
|
if format.lower() in ["uri"]:
|
|
@@ -138,3 +131,18 @@ def convert_format(format):
|
|
|
138
131
|
if format.lower() in ["boolean"]:
|
|
139
132
|
return "boolean"
|
|
140
133
|
return None
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def to_jsonschema(model_key, model_value: Model) -> dict:
|
|
137
|
+
model = {
|
|
138
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
139
|
+
"type": "object",
|
|
140
|
+
"properties": to_properties(model_value.fields),
|
|
141
|
+
"required": to_required(model_value.fields),
|
|
142
|
+
}
|
|
143
|
+
if model_value.title:
|
|
144
|
+
model["title"] = model_value.title
|
|
145
|
+
if model_value.description:
|
|
146
|
+
model["description"] = model_value.description
|
|
147
|
+
|
|
148
|
+
return model
|
|
@@ -3,6 +3,12 @@ from typing import Dict
|
|
|
3
3
|
import yaml
|
|
4
4
|
|
|
5
5
|
from datacontract.model.data_contract_specification import DataContractSpecification, Model, Field
|
|
6
|
+
from datacontract.export.exporter import Exporter
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class OdcsExporter(Exporter):
|
|
10
|
+
def export(self, data_contract, model, server, sql_server_type, export_args) -> dict:
|
|
11
|
+
return to_odcs_yaml(data_contract)
|
|
6
12
|
|
|
7
13
|
|
|
8
14
|
def to_odcs_yaml(data_contract_spec: DataContractSpecification):
|
|
@@ -24,13 +30,30 @@ def to_odcs_yaml(data_contract_spec: DataContractSpecification):
|
|
|
24
30
|
|
|
25
31
|
if data_contract_spec.terms is not None:
|
|
26
32
|
odcs["description"] = {
|
|
27
|
-
"purpose":
|
|
33
|
+
"purpose": data_contract_spec.terms.description.strip()
|
|
34
|
+
if data_contract_spec.terms.description is not None
|
|
35
|
+
else None,
|
|
28
36
|
"usage": data_contract_spec.terms.usage.strip() if data_contract_spec.terms.usage is not None else None,
|
|
29
37
|
"limitations": data_contract_spec.terms.limitations.strip()
|
|
30
38
|
if data_contract_spec.terms.limitations is not None
|
|
31
39
|
else None,
|
|
32
40
|
}
|
|
33
41
|
|
|
42
|
+
if data_contract_spec.servicelevels is not None:
|
|
43
|
+
slas = []
|
|
44
|
+
if data_contract_spec.servicelevels.availability is not None:
|
|
45
|
+
slas.append(
|
|
46
|
+
{
|
|
47
|
+
"property": "generalAvailability",
|
|
48
|
+
"value": data_contract_spec.servicelevels.availability.description,
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
if data_contract_spec.servicelevels.retention is not None:
|
|
52
|
+
slas.append({"property": "retention", "value": data_contract_spec.servicelevels.retention.period})
|
|
53
|
+
|
|
54
|
+
if len(slas) > 0:
|
|
55
|
+
odcs["slaProperties"] = slas
|
|
56
|
+
|
|
34
57
|
odcs["type"] = "tables" # required, TODO read from models.type?
|
|
35
58
|
odcs["dataset"] = []
|
|
36
59
|
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
from datacontract.model.data_contract_specification import DataContractSpecification
|
|
2
|
+
from datacontract.export.exporter import Exporter
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ProtoBufExporter(Exporter):
|
|
6
|
+
def export(self, data_contract, model, server, sql_server_type, export_args) -> dict:
|
|
7
|
+
return to_protobuf(data_contract)
|
|
2
8
|
|
|
3
9
|
|
|
4
10
|
def to_protobuf(data_contract_spec: DataContractSpecification):
|
|
@@ -2,6 +2,12 @@ import ast
|
|
|
2
2
|
import typing
|
|
3
3
|
|
|
4
4
|
import datacontract.model.data_contract_specification as spec
|
|
5
|
+
from datacontract.export.exporter import Exporter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PydanticExporter(Exporter):
|
|
9
|
+
def export(self, data_contract, model, server, sql_server_type, export_args) -> dict:
|
|
10
|
+
return to_pydantic_model_str(data_contract)
|
|
5
11
|
|
|
6
12
|
|
|
7
13
|
def to_pydantic_model_str(contract: spec.DataContractSpecification) -> str:
|
|
@@ -3,6 +3,15 @@ from rdflib import Graph, Literal, BNode, RDF, URIRef, Namespace
|
|
|
3
3
|
|
|
4
4
|
from datacontract.model.data_contract_specification import DataContractSpecification
|
|
5
5
|
|
|
6
|
+
from datacontract.export.exporter import Exporter
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RdfExporter(Exporter):
|
|
10
|
+
def export(self, data_contract, model, server, sql_server_type, export_args) -> dict:
|
|
11
|
+
self.dict_args = export_args
|
|
12
|
+
rdf_base = self.dict_args.get("rdf_base")
|
|
13
|
+
return to_rdf_n3(data_contract_spec=data_contract, base=rdf_base)
|
|
14
|
+
|
|
6
15
|
|
|
7
16
|
def is_literal(property_name):
|
|
8
17
|
return property_name in [
|