+ Data Contracts
+Title | +ID | +Version | +Owner | +
---|---|---|---|
+ {{contract.spec.info.title}} + | ++ {{contract.spec.id}} + | ++ {{contract.spec.info.version}} + | ++ {{contract.spec.info.owner}} + | +
diff --git a/CHANGELOG.md b/CHANGELOG.md index fa889e6c..4fc25f9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added test support for `azure` (#146) - Added support for `delta` tables on S3 (#24) - +- Added new command `datacontract catalog` that generates a data contract catalog with an `index.html` file. ## [0.10.1] - 2024-04-19 diff --git a/README.md b/README.md index 0d427226..3b12f673 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,7 @@ Commands - [breaking](#breaking) - [changelog](#changelog) - [diff](#diff) +- [catalog](#catalog) ### init @@ -725,6 +726,21 @@ Available import options: ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ``` +### catalog + +``` + + Usage: datacontract catalog [OPTIONS] + + Create a html catalog of data contracts. + +╭─ Options ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --files TEXT Glob pattern for the data contract files to include in the catalog. [default: *.yaml] │ +│ --output TEXT Output directory for the catalog html files. [default: catalog/] │ +│ --help Show this message and exit. │ +╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +``` + ## Integrations diff --git a/datacontract/catalog/catalog.py b/datacontract/catalog/catalog.py new file mode 100644 index 00000000..74703e60 --- /dev/null +++ b/datacontract/catalog/catalog.py @@ -0,0 +1,70 @@ +from dataclasses import dataclass + +from jinja2 import PackageLoader, Environment, select_autoescape +import pytz +from datetime import datetime + +from datacontract.export.html_export import get_version +from datacontract.data_contract import DataContract +from datacontract.model.data_contract_specification import DataContractSpecification + + +def create_data_contract_html(contracts, file, path): + data_contract = DataContract(data_contract_file=f"{file.absolute()}", inline_definitions=True) + html = data_contract.export(export_format="html") + spec = data_contract.get_data_contract_specification() + # html_filename = f"dc-{spec.id}-{spec.info.version}.html" + file_without_suffix = file.name.removesuffix(".yaml").removesuffix(".yml") + html_filename = f"{file_without_suffix}.html" + html_filepath = path / html_filename + with open(html_filepath, "w") as f: + f.write(html) + contracts.append(DataContractView( + html_filepath=html_filepath, + html_filename=html_filename, + spec=spec, + )) + print(f"Created {html_filepath}") + + +@dataclass +class DataContractView: + """Class for keeping track of an item in inventory.""" + html_filepath: str + html_filename: str + spec: DataContractSpecification + + +def create_index_html(contracts, path): + index_filepath = path / "index.html" + with open(index_filepath, "w") as f: + # Load templates from templates folder + package_loader = PackageLoader("datacontract", "templates") + env = Environment( + loader=package_loader, + autoescape=select_autoescape( + enabled_extensions="html", + default_for_string=True, + ), + ) + + # Load the required template + template = env.get_template("index.html") + + style_content, _, _ = package_loader.get_source(env, "style/output.css") + + tz = pytz.timezone('UTC') + now = datetime.now(tz) + formatted_date = now.strftime('%d %b %Y %H:%M:%S UTC') + datacontract_cli_version = get_version() + + # Render the template with necessary data + html_string = template.render( + style=style_content, + formatted_date=formatted_date, + datacontract_cli_version=datacontract_cli_version, + contracts=contracts, + contracts_size=len(contracts), + ) + f.write(html_string) + print(f"Created {index_filepath}") \ No newline at end of file diff --git a/datacontract/cli.py b/datacontract/cli.py index 7173399a..6233508f 100644 --- a/datacontract/cli.py +++ b/datacontract/cli.py @@ -1,17 +1,22 @@ from enum import Enum from importlib import metadata +from pathlib import Path from typing import Iterable, Optional + import typer from click import Context + from rich import box from rich.console import Console from rich.table import Table from typer.core import TyperGroup from typing_extensions import Annotated +from datacontract.catalog.catalog import create_index_html, create_data_contract_html from datacontract.data_contract import DataContract -from datacontract.init.download_datacontract_file import download_datacontract_file, FileExistsException +from datacontract.init.download_datacontract_file import \ + download_datacontract_file, FileExistsException console = Console() @@ -214,6 +219,25 @@ def import_( console.print(result.to_yaml()) +@app.command(name="catalog") +def catalog( + files: Annotated[Optional[str], typer.Option(help="Glob pattern for the data contract files to include in the catalog.")] = "*.yaml", + output: Annotated[Optional[str], typer.Option(help="Output directory for the catalog html files.")] = "catalog/", +): + """ + Create a html catalog of data contracts. + """ + path = Path(output) + path.mkdir(parents=True, exist_ok=True) + console.print(f"Created {output}") + + contracts = [] + for file in Path().glob(files): + create_data_contract_html(contracts, file, path) + + create_index_html(contracts, path) + + @app.command() def breaking( location_old: Annotated[str, typer.Argument(help="The location (url or path) of the old data contract yaml.")], diff --git a/datacontract/templates/index.html b/datacontract/templates/index.html new file mode 100644 index 00000000..b4b89fff --- /dev/null +++ b/datacontract/templates/index.html @@ -0,0 +1,161 @@ + + +
+Title | +ID | +Version | +Owner | +
---|---|---|---|
+ {{contract.spec.info.title}} + | ++ {{contract.spec.id}} + | ++ {{contract.spec.info.version}} + | ++ {{contract.spec.info.owner}} + | +
Information about the data contract
+Servers of the data contract
+Terms and conditions of the data contract
+The logical data model
+
+ orders
+ table
+ The orders model
+ |
+
+ |||
---|---|---|---|
+
+ order_id
+
+
+ |
+ + + R + + + U + + | ++ + varchar + + | +
+ No description
+
+ |
+
+
+
+ order_total
+
+
+ |
+ + + R + + + | ++ + bigint + + | +
+ The order_total field
+
+ |
+
+
+
+ order_status
+
+
+ |
+ + + R + + + | ++ + text + + | +
+ No description
+
+ |
+
+