# Data Contract CLI > An open-source command-line tool for working with data contracts based on the Open Data Contract Standard (ODCS). This file contains all documentation content in a single document following the llmstxt.org standard. ## What is Data Contract CLI? ![PyPI Monthly Downloads](https://img.shields.io/badge/downloads-1.4M%2Fmonth-brightgreen) ![GitHub Stars](https://img.shields.io/github/stars/datacontract/datacontract-cli) ![GitHub Contributors](https://img.shields.io/github/contributors/datacontract/datacontract-cli) ![Docker Pulls](https://img.shields.io/docker/pulls/datacontract/cli) The `datacontract` CLI is an open-source command-line tool for working with [data contracts](https://datacontract.com). It natively supports the [Open Data Contract Standard (ODCS)](https://bitol-io.github.io/open-data-contract-standard/latest/) to: - **Lint** data contracts and validate them against the ODCS JSON Schema. - **Connect** to data sources such as Snowflake, BigQuery, Databricks, Postgres, Kafka, S3, and many more. - **Test** that the actual data complies with the schema and quality expectations defined in the contract. - **Export** a contract to 25+ formats (SQL DDL, dbt, Avro, JSON Schema, HTML, Protobuf, …). - **Import** an existing schema (SQL, dbt, BigQuery, Glue, Excel, …) into a data contract. 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. Three commands take you from an existing warehouse table to a tested data contract: ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[snowflake]' datacontract import snowflake --source --database ORDER_DB --schema PUBLIC --output datacontract.yaml datacontract test datacontract.yaml ``` ``` 🟢 Data contract is valid. Ran 24 checks. Took 5.2 seconds. ``` The same flow works for [BigQuery](./testing/bigquery.md), [Databricks](./testing/databricks.md), [Postgres](./testing/postgres.md), and [15+ other sources](./testing/index.md) — or [try it on a local CSV file](./testing/local.md) without any credentials. ![Overview of the Data Contract CLI: schemas from SQL DDL, JSON Schema, Iceberg, Protobuf, BigQuery, Unity Catalog, AWS Glue, and Excel are imported into an ODCS data contract, which is linted, tested against S3, BigQuery, Azure, Databricks, Snowflake, and Kafka, and exported to SQL DDL, HTML, dbt, Entropy Data, Avro, SodaCL, Pydantic, and Excel.](/img/datacontractcli.webp) ### Why data contracts? A data contract is a machine-readable, versioned agreement that describes the **structure**, **semantics**, **quality**, and **service levels** of a data set. Because the contract is a single file, it can be: - stored in Git next to your code, - reviewed in pull requests, - linted and tested automatically in CI/CD, and - used to generate downstream artifacts (DDL, dbt models, schemas, documentation). A typical contract has a `servers` section with endpoint details, a `schema` describing the structure and semantics of the data, plus `service levels` and `quality` attributes that describe expectations such as freshness and number of rows. This is enough information to connect to the data source and check that the actual data product is compliant. ### How it works When you run `datacontract test`, the CLI connects to a data source and runs schema and quality tests to verify that the actual data complies with the contract. It picks a connection engine based on the server `type` — see **[Test your Data](./testing/index.md#how-it-works)**. ### Next steps - New here? Start with the **[Quickstart](./quickstart.md)**. - Test your own warehouse in 5 minutes: **[Snowflake](./testing/snowflake.md)**, **[BigQuery](./testing/bigquery.md)**, **[Databricks](./testing/databricks.md)**, **[Amazon Redshift](./testing/redshift.md)**, or **[any other source](./testing/index.md)**. - Learn the underlying format in **[Open Data Contract Standard](./open-data-contract-standard.md)**. - Author contracts visually with the **[Data Contract Editor](./editor.md)**. - Let your coding agent (Claude Code, Codex, GitHub Copilot) create and test contracts with the **[Data Contract Skills](./ai-agents.md)**. - Run checks against real data with **[Test your Data](./testing/index.md)**, then keep them running with **[Scheduling](./scheduling/index.md)**. - Roll it out across a team with **[Adopting Data Contracts](./best-practices.md)**. - See every command in the **[Commands reference](./commands/index.md)**. - Evaluating options? See **[Comparison with Other Tools](./comparison.md)**. - Coming from the older `datacontract.yaml` format? **[Migrate from DCS to ODCS](./migrate-dcs-to-odcs.md)**. - Upgrading? See what changed in **[Release Notes](./release-notes.md)**. ### Frequently asked questions #### What is the Data Contract CLI? The Data Contract CLI (`datacontract`) is a free and open-source command-line tool, written in Python and published under the MIT license, for linting data contracts, testing real data against them, and importing or exporting them to other schema formats. #### Which data contract format does the Data Contract CLI use? It natively uses the [Open Data Contract Standard (ODCS)](./open-data-contract-standard.md), an open specification governed by Bitol under the Linux Foundation AI & Data. The legacy Data Contract Specification (DCS) is still supported for reading and as an [export target](./exports/dcs.md). #### How do I install the Data Contract CLI? Run `pip install 'datacontract-cli[postgres]'` (or the extra matching your data source), or use `uv tool install`. `datacontract-cli[all]` installs every optional dependency. A Docker image is published as `datacontract/cli`. See [Installation](./installation.md) for all options. #### Which data sources can the Data Contract CLI test? Snowflake, Databricks, Google BigQuery, Amazon Athena, Amazon Redshift, Amazon S3, Azure Blob Storage and ADLS, Google Cloud Storage, Postgres, MySQL, Microsoft SQL Server, Oracle, Trino, ClickHouse, Apache Hive, Apache Impala, Kafka, Spark DataFrames, JSON HTTP APIs, and local Parquet, JSON, CSV, or Delta files. See [Test your Data](./testing/index.md). #### Is the Data Contract CLI free to use? Yes. It is open source under the MIT license and free for commercial use. [Entropy Data](./entropy-data.md) offers a commercial platform that the CLI can publish test results to, but the CLI itself does not require it. More answers — credentials, check selection, type failures, CI behaviour — are in the **[FAQ](./faq.md)**. ### Related links - Website: [datacontract.com](https://datacontract.com) - Source code: [github.com/datacontract/datacontract-cli](https://github.com/datacontract/datacontract-cli) - Community Slack: [datacontract.com/slack](https://datacontract.com/slack) - GitHub Action: [datacontract/datacontract-action](https://github.com/datacontract/datacontract-action/) - Agent skills: [datacontract/datacontract-skills](https://github.com/datacontract/datacontract-skills) --- ## Quickstart This guide gets you from zero to a tested data contract in a few minutes — first against a hosted demo database (60 seconds, nothing to set up), then against your own data warehouse (about 5 minutes). ### Install The preferred way to install is [uv](https://docs.astral.sh/uv/). Add the extra for the data source you want to test — the demo below uses Postgres: ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[postgres]' ``` Every source has its own extra (`snowflake`, `bigquery`, `databricks`, `s3`, `duckdb` for local files, …), so you only install what you need. `datacontract-cli[all]` pulls in every optional dependency, including Spark — convenient, but a much larger download. See [Installation options](#installation-options) below for `pip`, `pipx`, and Docker. Verify the installation: ```bash datacontract --version ``` ### Test your first data contract (60 seconds) Let's use the example contract published at [`https://datacontract.com/orders-v1.odcs.yaml`](https://datacontract.com/orders-v1.odcs.yaml). It contains a `servers` section pointing at a Postgres database, a `schema`, and `quality` attributes. Provide credentials as environment variables and run `test`: ```bash export DATACONTRACT_POSTGRES_USERNAME=datacontract_cli.egzhawjonpfweuutedfy export DATACONTRACT_POSTGRES_PASSWORD=jio10JuQfDfl9JCCPdaCCpuZ1YO datacontract test https://datacontract.com/orders-v1.odcs.yaml ``` ``` Testing https://datacontract.com/orders-v1.odcs.yaml Server: production (type=postgres, host=..., port=6543, database=postgres, schema=dp_orders_v1) ╭────────┬──────────────────────────────────────────────────┬─────────────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼──────────────────────────────────────────────────┼─────────────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ orders.order_id │ │ │ passed │ Check that field order_id has physical type UUID │ orders.order_id │ │ │ ... │ │ │ │ ╰────────┴──────────────────────────────────────────────────┴─────────────────┴─────────╯ 🟢 Data contract is valid. Ran 31 checks. Took 2.436205 seconds. ``` The CLI verified that the YAML itself is valid, that all records comply with the schema, and that all quality attributes are met. ### Test your own data (5 minutes) The real magic moment is when a contract catches drift in *your* data. Import a contract straight from an existing table — the import generates the schema and a ready-to-test `servers` block — then test the actual data against it: ```bash datacontract import postgres --source localhost --database mydb --output datacontract.yaml datacontract test datacontract.yaml ``` That example uses Postgres because it matches the extra installed above. For another source, install its extra first — `uv tool install --python python3.11 --upgrade 'datacontract-cli[snowflake]'` — and use the matching import, e.g. `datacontract import snowflake`. Follow the copy-paste guide for your data source, including credentials setup and troubleshooting: - **[Snowflake](./testing/snowflake.md)** — import from your tables, test in 5 minutes - **[Google BigQuery](./testing/bigquery.md)** — import from your tables, test in 5 minutes - **[Databricks](./testing/databricks.md)** — import from Unity Catalog, test in 5 minutes - **[Amazon Redshift](./testing/redshift.md)** — import from your tables, test in 5 minutes - **[Postgres](./testing/postgres.md)**, **[Amazon S3](./testing/s3.md)**, and [15+ other sources](./testing/index.md) - **[Local files](./testing/local.md)** — no warehouse or credentials needed, works offline Each guide ends with the same payoff: tighten an expectation, rerun `datacontract test`, and watch the contract catch the violation with exit code `1` — the exact behavior you'll later use [in CI/CD](./scheduling/github-actions.md). ### Export to another format You can use the contract metadata to generate downstream artifacts. For example, a SQL DDL: ```bash datacontract export sql https://datacontract.com/orders-v1.odcs.yaml ``` ```sql -- Data Contract: orders -- SQL Dialect: postgres CREATE TABLE orders ( order_id UUID not null primary key, customer_id text not null, order_total integer not null, order_timestamp TIMESTAMPTZ, order_status text ); CREATE TABLE line_items ( line_item_id UUID not null primary key, sku text not null, price integer not null, order_id UUID ); ``` Or an HTML page: ```bash datacontract export html --output orders-v1.odcs.html https://datacontract.com/orders-v1.odcs.yaml ``` See **[Exports](./exports/index.md)** for all 25+ target formats. ### The typical workflow ```bash # Create a new data contract from a template and write it to odcs.yaml datacontract init odcs.yaml # Edit the data contract in the Data Contract Editor (web UI) datacontract edit odcs.yaml # Lint the odcs.yaml and stop after the first validation error (default) datacontract lint odcs.yaml # Show a changelog between two data contracts datacontract changelog v1.odcs.yaml v2.odcs.yaml # Execute schema and quality checks (credentials via environment variables) datacontract test odcs.yaml # Generate dbt tests from a contract into your dbt project and run `dbt test` datacontract dbt sync orders.odcs.yaml --project-dir ./warehouse # Export to HTML (and many other formats) datacontract export html odcs.yaml --output odcs.html # Import from an existing SQL DDL datacontract import sql --source my-ddl.sql --dialect postgres --output odcs.yaml ``` ### Use it as a Python library ```python from datacontract.data_contract import DataContract data_contract = DataContract(data_contract_file="odcs.yaml") run = data_contract.test() if not run.has_passed(): print("Data quality validation failed.") # Abort pipeline, alert, or take corrective actions... ``` ### Next steps - Keep the tests running automatically: **[Scheduling](./scheduling/index.md)** with GitHub Actions, Airflow, cron, or your orchestrator. - Roll data contracts out across a team: **[Adopting Data Contracts](./best-practices.md)**. - Browse everything the CLI can do: **[Commands](./commands/index.md)**. ### Installation options See the dedicated **[Installation](./installation.md)** page for all options (uv, uvx, pip, venv, pipx, Docker) and the full list of optional dependencies (extras). --- ## Open Data Contract Standard The Data Contract CLI natively uses the **[Open Data Contract Standard (ODCS)](https://bitol-io.github.io/open-data-contract-standard/latest/)** as its data contract format. ODCS is an open, vendor-neutral standard maintained by the [Bitol](https://bitol.io/) project under the Linux Foundation's AI & Data umbrella. A data contract written in ODCS is a single YAML file that describes a data set's structure, semantics, quality, ownership, and the servers where the data lives. ### A minimal contract ```yaml apiVersion: v3.2.0 kind: DataContract id: urn:datacontract:checkout:orders-latest name: orders version: 1.0.0 status: active description: purpose: One record per order. Includes cancelled and deleted orders. team: name: Checkout members: - username: dataeng@example.com role: Owner servers: - server: production type: postgres host: localhost port: 5432 database: orders schema: public schema: - name: orders physicalName: orders properties: - name: order_id logicalType: string physicalType: uuid primaryKey: true required: true - name: order_total logicalType: integer physicalType: integer required: true quality: - type: sql description: 95% of order totals are between 10 and 499 EUR query: SELECT quantile_cont(order_total, 0.95) FROM orders mustBeBetween: [1000, 99900] ``` `v3.2.0` is the current version of the standard and the one [`datacontract init`](./commands/init.md) writes. The CLI also validates contracts declaring `v3.1.0`, `v3.0.2`, `v3.0.1`, and `v3.0.0`. New in ODCS v3.2.0 (see the [ODCS changelog](https://github.com/bitol-io/open-data-contract-standard/blob/main/CHANGELOG.md)): - `enum` on properties: allowed values as objects with `value`, `label`, `description`, and more - `logicalType: map` with a `map` block defining `key` and `value`, and `logicalType: vector` with `dimensions` and other embedding options in `logicalTypeOptions` - `semanticType` on properties (`column`, `measure`, `dimension`), `synonyms` and `deprecated` on schema objects and properties - `context` on the contract and on schema objects, with `instructions`, `verifiedStatements`, and `constraints` for AI agents and semantic tools - Variables: `${VAR_NAME}` and `${VAR_NAME:-default}` in any string value, and a server `port` that may hold such a reference - New server types `hana`, `iceberg`, `exasol`, `teradata`, `ingres`, `vectorwise`, `versant`, and `poet`, an `encoding` on file and stream servers, and an Athena `workgroup` - `vendor` on custom properties, `id` on relationships, and `customProperties` and `authoritativeDefinitions` on `slaProperties` The CLI validates and round-trips all of these. Interpretation of the new types and fields by `test`, `export`, and `import` is added step by step; see the [release notes](./release-notes.md). :::note The CLI also accepts the older Data Contract Specification format (which uses `models`/`fields` instead of ODCS `schema`/`properties`), but new contracts should follow ODCS — all examples in this documentation use ODCS. To convert an existing one, see [Migrate from DCS to ODCS](./migrate-dcs-to-odcs.md). ::: ### Key sections | Section | Purpose | |---|---| | `apiVersion` / `kind` | Identifies the document as an ODCS data contract and its version. | | `id`, `name`, `version`, `status` | Identity and lifecycle of the contract. | | `description` | Human-readable purpose, usage, and limitations. | | `servers` | Where the data physically lives — the connection details used by [`test`](./commands/test.md). One contract can have several servers. | | `schema` | The logical structure: schemas (tables/objects) and their properties (columns/fields), types, constraints, and semantics. See [Define your Schema](./schema.md). | | `quality` | Data quality rules, attached to the schema or to individual properties. See [Quality Rules](./quality-rules/index.md). | | `slaProperties` | Service-level expectations such as freshness, retention, and frequency. See [Service Levels](./service-levels.md). | | `team` / `roles` | Ownership and access information. `team` is an object with a `members` array. | | `customProperties` | Extension point for backend-specific settings (for example `clickhouseType`, `trinoType`, `avroLogicalType`). | | `authoritativeDefinitions` | Links from a property to a shared semantic concept or reusable definition, by URL or IRI. See [Link your Semantics](./semantics.md). | ### Logical vs. physical types ODCS separates the **logical type** (`logicalType`, e.g. `string`, `integer`, `number`, `boolean`, `date`, `timestamp`) from the **physical type** (`physicalType`, e.g. `varchar`, `uuid`, `INT64`). - The CLI uses the logical type as the portable, server-independent description. - When you select a server (via `--server` or the server `type`), the CLI maps logical types to that backend's physical types for [exports](./exports/index.md) and [tests](./testing/index.md). - You can always override the physical type per field, or pin a backend-specific type via `customProperties` / `config` (for example `clickhouseType`, `trinoType`). ### Working with ODCS in the CLI - **Author** contracts with [`datacontract init`](./commands/init.md) and the [Data Contract Editor](./editor.md). - **Validate** the structure against the ODCS JSON Schema with [`datacontract lint`](./commands/lint.md). Pass `--json-schema` to use a specific schema version. - **Compare** two versions of a contract with [`datacontract changelog`](./commands/changelog.md). - **Generate** a contract from an existing system using the [importers](./imports/index.md). ### Learn more - ODCS specification: [bitol-io.github.io/open-data-contract-standard](https://bitol-io.github.io/open-data-contract-standard/latest/) - Data contracts overview: [datacontract.com](https://datacontract.com) --- ## Installation Python 3.10–3.14 are supported. We recommend Python 3.11. The `[all]` extra installs every optional data-source dependency. To keep the install small, replace it with just the [extras](#optional-dependencies-extras) you need. ### uv (recommended) The preferred way to install is [uv](https://docs.astral.sh/uv/): ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[all]' datacontract --version ``` ### uvx (run without installing) If you have [uv](https://docs.astral.sh/uv/) installed, you can run the CLI directly without installing it: ```bash uvx --from 'datacontract-cli[all]' datacontract --version ``` ### pip ```bash python3 -m pip install 'datacontract-cli[all]' datacontract --version ``` ### pip with venv Typically it's better to install into a virtual environment for your project: ```bash cd my-project python3.11 -m venv venv source venv/bin/activate pip install 'datacontract-cli[all]' datacontract --version ``` ### pipx `pipx` installs into an isolated environment: ```bash pipx install 'datacontract-cli[all]' datacontract --version ``` ### Docker Use the Docker image to run the CLI without a local Python install — convenient for CI/CD: ```bash docker pull datacontract/cli docker run --rm -v "${PWD}:/home/datacontract" datacontract/cli ``` Create an alias to make it easier to use: ```bash alias datacontract='docker run --rm -v "${PWD}:/home/datacontract" datacontract/cli:latest' ``` :::note The output of Docker command line messages is limited to 80 columns and may include line breaks. Don't pipe Docker output to files if you want to export code — use the `--output` option instead. ::: The image is also mirrored to Amazon ECR Public: ```bash docker pull public.ecr.aws/s4e5k7s9/datacontract-cli ``` #### Verifying the image Released images are signed with [cosign](https://docs.sigstore.dev/cosign/signing/overview/) keyless signing, using the GitHub Actions OIDC identity of the release workflow. There is no public key to distribute — verification checks that the image was built and signed by that workflow, in this repository, from a release tag. Install [cosign](https://docs.sigstore.dev/cosign/system_config/installation/) v2.6.3 or later (v3 or later recommended), then: ```bash cosign verify datacontract/cli:latest \ --certificate-identity-regexp '^https://github\.com/datacontract/datacontract-cli/\.github/workflows/release\.yaml@refs/tags/v' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com ``` Both `--certificate-identity-regexp` and `--certificate-oidc-issuer` are required. Without them, `cosign verify` accepts any valid Sigstore signature — including one produced by someone else. The same command works for the ECR mirror, which carries the signature and attestations along with the image: ```bash cosign verify public.ecr.aws/s4e5k7s9/datacontract-cli:latest \ --certificate-identity-regexp '^https://github\.com/datacontract/datacontract-cli/\.github/workflows/release\.yaml@refs/tags/v' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com ``` Each image also ships an SBOM and SLSA provenance attestation, which you can inspect with: ```bash docker buildx imagetools inspect datacontract/cli:latest --format '{{ json .SBOM }}' docker buildx imagetools inspect datacontract/cli:latest --format '{{ json .Provenance }}' ``` Signatures are attached to images released after version 1.1.0. Older tags, 1.1.0 included, are unsigned. ### Optional dependencies (extras) The CLI defines several optional dependencies (extras) for specific server types. With `all`, every server dependency except `hana` is included. ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[all]' ``` Available extras: | Dependency | Installation command | |---|---| | API (run as web server) | `pip install datacontract-cli[api]` | | Amazon Athena | `pip install datacontract-cli[athena]` | | Avro | `pip install datacontract-cli[avro]` | | Azure | `pip install datacontract-cli[azure]` | | Google BigQuery | `pip install datacontract-cli[bigquery]` | | ClickHouse | `pip install datacontract-cli[clickhouse]` | | Databricks | `pip install datacontract-cli[databricks]` (also inside a Databricks Runtime, using the cluster's own Spark session — see [Databricks Notebooks and Jobs](./databricks.md)) | | DataFrame (Spark) | `pip install datacontract-cli[dataframe]` (PySpark not included — you supply the Spark session) | | DBML | `pip install datacontract-cli[dbml]` | | DuckDB (local file, CSV import and API response testing) | `pip install datacontract-cli[duckdb]` | | Exasol | `pip install datacontract-cli[exasol]` | | Excel (import / export) | `pip install datacontract-cli[excel]` | | Excel workbooks (testing) | `pip install datacontract-cli[xlsx]` | | GCS | `pip install datacontract-cli[gcs]` | | Apache Hive | `pip install datacontract-cli[hive]` | | SAP HANA / Datasphere | `pip install datacontract-cli[hana]` (not part of `all`: `hdbcli` is proprietary) | | Apache Iceberg (schema import and export, REST catalog testing) | `pip install datacontract-cli[iceberg]` | | Impala | `pip install datacontract-cli[impala]` | | Kafka | `pip install datacontract-cli[kafka]` | | MySQL | `pip install datacontract-cli[mysql]` | | Oracle | `pip install datacontract-cli[oracle]` | | Parquet | `pip install datacontract-cli[parquet]` | | PostgreSQL | `pip install datacontract-cli[postgres]` | | Protobuf | `pip install datacontract-cli[protobuf]` | | RDF | `pip install datacontract-cli[rdf]` | | Amazon Redshift | `pip install datacontract-cli[redshift]` | | S3 | `pip install datacontract-cli[s3]` | | Snowflake | `pip install datacontract-cli[snowflake]` | | Microsoft SQL Server | `pip install datacontract-cli[sqlserver]` | | Trino | `pip install datacontract-cli[trino]` | | XML Schema (import) | `pip install datacontract-cli[xml]` | Each [data source](./testing/index.md) lists the extra it needs. --- ## Test your Data `datacontract test` connects to the data source defined in the contract's `servers` block and runs **schema** and **quality** tests to verify that the actual data complies with the data contract. ```bash datacontract test --server production datacontract.yaml ``` ### Supported connections ![](/img/icons/snowflake.svg) SnowflakeImport a contract from your tables and test in 5 minutes ![](/img/icons/bigquery.svg) Google BigQueryImport a contract from your tables and test in 5 minutes ![](/img/icons/databricks.svg) DatabricksImport a contract from Unity Catalog and test in 5 minutes ![](/img/icons/redshift.svg) Amazon RedshiftImport a contract from your tables and test in 5 minutes ![](/img/icons/postgres.svg) PostgresPostgres and Postgres-compatible (e.g. RisingWave) ![](/img/icons/s3.svg) Amazon S3CSV, JSON, Delta, Parquet on S3 / S3-compatible storage ![](/img/icons/s3.svg) Amazon S3 TablesImport and test Iceberg tables using AWS credentials ![](/img/icons/duckdb.svg) DuckDBTables inside a DuckDB database file ![](/img/icons/local.svg) Local filesTry it in 60 seconds — no credentials needed ![](/img/icons/athena.svg) Amazon AthenaAthena over data in S3 ![](/img/icons/database.svg) Apache HiveTables in a HiveServer2 ![](/img/icons/iceberg.svg) Apache IcebergTables in a REST catalog (Polaris, Nessie, Unity, Glue, S3 Tables) ![](/img/icons/impala.svg) Apache ImpalaImpala ![](/img/icons/azure.svg) Azure Blob / ADLSFiles on Azure Blob storage or ADLS Gen2 ![](/img/icons/clickhouse.svg) ClickHouseClickHouse databases ![](/img/icons/exasol.svg) ExasolExasol databases ![](/img/icons/gcs.svg) Google Cloud StorageFiles on GCS via S3 interoperability ![](/img/icons/api.svg) HTTP APIJSON HTTP APIs (GET only) ![](/img/icons/kafka.svg) KafkaKafka topics (experimental) ![](/img/icons/sqlserver.svg) Microsoft SQL ServerSQL Server, Azure SQL, Synapse, Fabric ![](/img/icons/mysql.svg) MySQLMySQL / MariaDB ![](/img/icons/oracle.svg) OracleOracle Database ![](/img/icons/sap.svg) SAP HANASAP HANA Cloud and SAP Datasphere Open SQL schemas ![](/img/icons/spark.svg) Spark DataFrameIn-memory Spark DataFrames (programmatic) ![](/img/icons/trino.svg) TrinoTrino (basic, JWT, OAuth2) ![](/img/icons/custom.svg) XML filesXML documents, with a contract from their XML Schema :::tip Missing a source? [Open an issue on GitHub](https://github.com/datacontract/datacontract-cli/issues). ::: Each connection requires the matching [optional dependency (extra)](../installation.md#optional-dependencies-extras), or install everything with `datacontract-cli[all]`. Every other server `type` in ODCS, including the ones added in v3.2.0 (`teradata`, `ingres`, `vectorwise`, `versant`, `poet`), lints and exports, but `test` reports a warning that it cannot connect. `fastobjects` and `btrieve` are the ODCS synonyms of `poet` and `zen`, and `postgresql` of `postgres`. ### How it works The CLI uses different engines based on the server `type`. Internally it connects with **DuckDB**, **Spark**, or a native connection, executes most checks with [_ibis_](https://ibis-project.org/) (compiling dialect-specific SQL per backend), and validates JSON with [_fastjsonschema_](https://pypi.org/project/fastjsonschema/). Checks fall into categories you can select with `--checks`: - `properties` — the [schema](../schema.md) attributes: presence, types, `required`, `unique`, primary keys, and `logicalTypeOptions`. `schema` is kept as a legacy alias. Not every ODCS attribute produces a check — see [What is not checked](../schema.md#what-is-not-checked) if one you declared appears to have no effect. - `quality` — the [quality rules](../quality-rules/index.md) defined in the contract. - `slaProperties` — the [service levels](../service-levels.md) defined in the contract. `servicelevel` is kept as a legacy alias. - `custom` — custom checks. Omit `--checks` to run all of them. :::note Selecting `properties` (or its legacy alias `schema`) is not metadata-only. The category says where a rule is defined in the contract, not how the check reads the database: constraints such as `required`, `unique`, enum, pattern, and ranges read column values and may scan data. Use `--metadata-only` to skip these value-level checks: only the schema-reading checks (field presence and types) run, and the rest show as `skipped`. One exception: on JSON file and API sources, record validation still reads every record; `--metadata-only` does not disable it. ::: `--dimension` cuts across those categories instead: it selects every check that measures one aspect of data quality — the [quality rules](../quality-rules/index.md#quality-dimensions) tagged with that `dimension` plus the schema and service level checks that measure the same thing. `--quality-id` and `--tag` go the other way and narrow the run to individual [quality rules](../quality-rules/index.md#identifying-rules): `--quality-id` runs the one rule declaring that `id`, `--tag` runs every rule declaring that tag, and neither runs any schema or service level check. ### Dry Run `--dry-run` reports the checks a run would execute and stops there. No data is read from the server, so every reported check has the result `skipped` (or `warning` if they cannot be planned). ```bash datacontract test datacontract.yaml --dry-run ``` A dry run needs no server credentials, which makes it usable on a pull request build that has no warehouse access. A dry run exits `0`: it is a plan, not a verdict. :::note A dry run is not offline in general: a contract that references [external semantics](../semantics.md) still fetches them. This is necessary to build all of its checks. ::: :::caution Contracts with `logicalType: blob` schemas on Azure do not support dry runs. ::: ### Configuring the connection The connection details (host, catalog, location, …) live in the contract's `servers` block; **credentials are provided as environment variables**. ```yaml servers: - server: production type: postgres # selects the connection engine host: localhost port: 5432 database: postgres schema: public ``` Environment variables are also loaded from a `.env` file in the current working directory (or the nearest parent directory containing one). Already-set environment variables take precedence over values from `.env`. ```bash # .env DATACONTRACT_POSTGRES_USERNAME=postgres DATACONTRACT_POSTGRES_PASSWORD=postgres ``` Server fields may also hold `${VAR}` or `${VAR:-default}` references (ODCS v3.2.0), which `test` resolves from the environment when it connects; see [Variables in the data contract](../configuration.md#variables-in-the-data-contract). The page for each source above lists its `servers` fields and the environment variables it expects. Credentials can also come from a YAML config file (`--config-file`), the Python `Config` class, or per-request API headers; see [Configuration](../configuration.md) for all mechanisms and their precedence. ### Options `--server`, `--schema-name`, `--checks`, `--dimension`, `--quality-id`, and `--tag` narrow down what runs. `--output` with `--output-format` writes the results to a file as `json` or `junit`, `--publish` sends them to a URL, and `--include-failed-samples` collects a small sample of the offending rows. See the full [`test` command reference](../commands/test.md). For CI/CD pipelines, use the [`ci`](../commands/ci.md) command. It runs the same checks as `test`, takes several contracts at once, and adds annotations, summaries, and exit-code control. ### Testing only a subset of rows On large tables, a full scan for every test run is slow and expensive. `--filter` restricts the checks to the rows matching a SQL predicate, written in the dialect of the server: ```bash # Test only the rows ingested in the last 24 hours datacontract test datacontract.yaml --server production --filter "ingested_at >= CURRENT_TIMESTAMP - INTERVAL '24 hours'" # Test a specific partition datacontract test datacontract.yaml --server production --filter "batch_id = '2026-07-31'" ``` `--filter` requires that a single schema is tested. If the contract defines several schemas, either select one with `--schema-name` or pass `--filters` with a JSON object mapping schema name to predicate: ```bash datacontract test datacontract.yaml --server production \ --filters '{"orders": "ingested_at >= CURRENT_DATE - 1", "line_items": "ingested_at >= CURRENT_DATE - 1"}' ``` The predicate references the columns of the schema unqualified. It applies to all row-based checks: row counts, missing/invalid values, duplicates, freshness, retention, and failed-row samples. Schema checks (column presence and types) read metadata, not rows, so a filter does not change them. Custom SQL quality checks (`quality.type: sql`) and JSON schema validation of files run the query or file as-is and are not filtered. The [API server](../api.md)'s `POST /test` accepts the same `filter` and `filters` as query parameters. A filtered run only makes a statement about the tested subset. So the applied filters are recorded in the test results: the `Run` carries a `filters` field (per schema), the console output prints a `Row filter:` line, and the recorded SQL of each check contains the `WHERE` clause. ### Next steps - Run the tests from Python instead of the CLI: **[Python Library](../python-library.md)**. - Keep the tests running automatically: **[Scheduling](../scheduling/index.md)**. - Roll data contracts out across a team: **[Adopting Data Contracts](../best-practices.md)**. --- ## HTTP API ## ![](/img/icons/api.svg) HTTP API Test the response of a REST API's GET or POST endpoint. `datacontract test` calls the endpoint and checks the response, JSON or YAML, against the contract. ### 1. Install The response is tested with duckdb, so the `duckdb` extra is required: ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[duckdb]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate If the API requires authentication, set the value for the `Authorization` header, or the API key for the `X-API-Key` header: ```bash # .env DATACONTRACT_API_HEADER_AUTHORIZATION="Bearer " DATACONTRACT_API_HEADER_X_API_KEY="" ``` For public endpoints, skip this step. ### 3. Create a contract #### From the OpenAPI document If the API has an OpenAPI 3.x document, import the operation you want to test, by its `operationId` or its method and path (`POST /orders/search`): ```bash datacontract import openapi --source openapi.yaml --operation listOrders --output datacontract.yaml ``` The contract gets one schema for the operation's response, and an `api` server for each server in the document, located at the endpoint: ```yaml servers: - server: production type: api description: Production customProperties: - property: params value: limit: ${limit:-100} location: https://api.example.com/v1/orders - server: staging type: api description: Staging customProperties: - property: params value: limit: ${limit:-100} location: https://staging.api.example.com/v1/orders schema: - name: listOrders # ... ``` Path parameters become [variables](../configuration.md#variables-in-the-data-contract) in the `location`, and required query parameters variables in `params`, defaulting to the parameter's `example` or `default`. Set them in the environment to call the endpoint with other values, for example `limit=10 datacontract test datacontract.yaml`. See [Import: OpenAPI](../imports/openapi.md) for what is imported. #### From a response Without an OpenAPI document, fetch one response and import its schema: ```bash curl -o response.json https://api.example.com/orders datacontract import json --source response.json --output datacontract.yaml ``` The import generates a `servers` entry of `type: local`. Replace it with the API endpoint: ```yaml servers: - server: api type: api location: "https://api.example.com/orders" ``` #### Testing POST requests Custom properties of the server set the method, the query parameters and the JSON body of the request. For a search API that only accepts POST: ```yaml servers: - server: search type: api location: "https://api.example.com/orders/search" customProperties: - property: method value: POST - property: params value: limit: ${limit:-100} status: [placed, shipped] - property: body value: query: customer_id: ${customer_id:-C-1} size: 100 ``` This sends a POST request with the query parameters `limit=100`, `status=placed` and `status=shipped`, and the body `{"query": {"customer_id": "C-1"}, "size": 100}`. See the [reference](../reference/api.md#request) for the rules. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` The first server is tested unless you select one with `--server`, e.g. `--server staging`. ``` Testing datacontract.yaml Server: production (type=api, location=https://api.example.com/v1/orders) ╭────────┬─────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼─────────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has no missing values │ order_id │ │ │ ... │ │ │ │ ╰────────┴─────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 10 checks. Took 1.2 seconds. ``` The request asks for JSON or YAML (`Accept: application/json, application/yaml`). How the response is read depends on its `Content-Type`: - **JSON**: a single object, an array of records, or one record per line (JSON Lines) is detected automatically. - **YAML** (`application/yaml`, `application/x-yaml`, `text/yaml`): the YAML is read as the JSON it holds and then tested the same way. A stream of several YAML documents (separated by `---`) counts as one record per document. ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, mark a field as `required: true`, restrict a status field to its allowed values, or add a quality rule. Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference Authentication options and data type handling: **[HTTP API Reference](../reference/api.md)**. ### Troubleshooting - **`401` / `403`**: set `DATACONTRACT_API_HEADER_AUTHORIZATION` including the scheme (e.g. `Bearer eyJ...`), not just the raw token, or `DATACONTRACT_API_HEADER_X_API_KEY` if the API expects an `X-API-Key` header. - **`Variable orderId referenced in ... is not set`**: the endpoint has a path or query parameter without an example or default. Set it in the environment or a `.env` file, or write a default into the `location` (`${orderId:-ORD-1001}`). - **Schema checks fail on a wrapped response**: if the API returns `{"data": [...]}` instead of a plain array, model the wrapper object in the schema, or set `delimiter` accordingly (`array` for a JSON array of records, `none` for a single object). --- ## Amazon Athena ## ![](/img/icons/athena.svg) Amazon Athena Go from an existing Athena table to a tested data contract in about five minutes: import the schema straight from the catalog, then test the actual data against it. Athena reads data in S3, so this covers formats such as Iceberg, Parquet, JSON, and CSV. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[athena]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate The easiest way is to sign in to AWS once — the CLI picks the session up, so no key is stored anywhere and nothing else has to be configured: ```bash aws sso login # or any other way of getting an AWS session ``` Prefer static keys? Set them directly and they are used instead: ```bash # .env DATACONTRACT_S3_REGION=eu-central-1 DATACONTRACT_S3_ACCESS_KEY_ID=AKIAXV5Q5QABCDEFGH DATACONTRACT_S3_SECRET_ACCESS_KEY=93S7LRrJcqLaaaa/XXXXXXXXXXXXX ``` Either way, your AWS identity needs `glue:GetTables` to import, and `athena:StartQueryExecution` plus read access to the data and write access to the staging directory to test. `import` and `test` use the same setup. ### 3. Create a contract from your tables Import the table metadata directly from the catalog. This also generates a ready-to-test `servers` block: ```bash datacontract import athena \ --schema my_database \ --staging-dir s3://my-bucket/athena-results/ \ --region eu-central-1 \ --table orders \ --output datacontract.yaml ``` `--schema` is the Athena database. Repeat `--table` for multiple tables, or omit it to import every table in the database. `--catalog` defaults to `awsdatacatalog`. `--staging-dir` is where Athena writes query results; `datacontract test` needs it, so the import asks for it up front and writes it into the contract. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: athena (type=athena, schema=my_database, catalog=awsdatacatalog) ╭────────┬────────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼────────────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has physical type bigint │ order_id │ │ │ ... │ │ │ │ ╰────────┴────────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 24 checks. Took 7.8 seconds. ``` ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule to a schema in `datacontract.yaml`: ```yaml schema: - name: orders # ... quality: - type: sql description: No order has a negative total query: SELECT COUNT(*) FROM orders WHERE order_total < 0 mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options and the data type mappings: **[Athena Reference](../reference/athena.md)**. Since ODCS v3.2.0 the server may name a `workgroup` (override: `DATACONTRACT_ATHENA_WORKGROUP`). A workgroup can enforce the query result location, so `stagingDir` is optional when one is set. ### Troubleshooting - **`Access Denied` on query start** — the credentials need `athena:StartQueryExecution` plus write access to the `stagingDir` bucket. - **`Table not found`** — `schema` in the `servers` block must be the Athena *database* name (as shown in the Glue Data Catalog), and `regionName` must match where the catalog lives. - **`Your session has expired`** — the AWS session has lapsed; run `aws sso login` again. No `DATACONTRACT_S3_*` variable is needed when a session is present. --- ## Azure Blob / ADLS ## ![](/img/icons/azure.svg) Azure Blob / ADLS Test data stored in Azure Blob storage or Azure Data Lake Storage Gen2 (ADLS) in various formats. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[azure]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate Authentication uses an Azure Service Principal (App Registration) with a secret. Create a `.env` file in your working directory (or export the variables): ```bash # .env DATACONTRACT_AZURE_TENANT_ID=79f5b80f-10ff-40b9-9d1f-774b42d605fc DATACONTRACT_AZURE_CLIENT_ID=3cf7ce49-e2e9-4cbc-a922-4328d4a58622 DATACONTRACT_AZURE_CLIENT_SECRET=yZK8Q~GWO1MMXXXXXXXXXXXXX ``` ### 3. Create a contract from your files Import the schema straight from the container. This also generates a ready-to-test `servers` block: ```bash datacontract import adls \ --source 'abfss://my-container/orders/*.json' \ --output datacontract.yaml ``` The format is taken from the file suffix; pass `--format` for Delta tables, which have none. The import reads only the field names and types. Properties like `required` cannot be inferred from the files and need to be adjusted manually. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: production (type=azure, format=json, location=abfss://my-container/orders/*.json) ╭────────┬─────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼─────────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has no missing values │ order_id │ │ │ ... │ │ │ │ ╰────────┴─────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 11 checks. Took 4.5 seconds. ``` ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule: ```yaml schema: - name: inventory_events # ... quality: - type: sql description: No event has a negative quantity query: SELECT COUNT(*) FROM inventory_events WHERE quantity < 0 mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options (service principal, connection string, account key), supported location URL formats, and the data type handling per file format: **[Azure Reference](../reference/azure.md)**. ### Troubleshooting - **`AuthorizationPermissionMismatch` / `403`** — the service principal needs the **Storage Blob Data Reader** role on the container or storage account (IAM role assignment, not just API permissions). - **`No files found that match the pattern`** — check the `location` URL format (see below) and the glob; it matches blob names under the prefix. ### Metadata checks Instead of reading the file contents, you can validate the metadata of the blobs themselves (size, content type, last modified, file count, …). Each ODCS schema object with `physicalType: file` represents a **unique folder/prefix** in Azure Blob storage or ADLS Gen2. Its `properties` map directly to `BlobProperties` attributes from the Azure SDK: for every declared property the engine extracts the corresponding attribute from each blob and validates it against the quality constraints declared on that property. | Property name | `BlobProperties` attribute | |---|---| | `name` | `blob.name` | | `size` | `blob.size` | | `lastModified` | `blob.last_modified` (UTC datetime) | | `creationTime` | `blob.creation_time` (UTC datetime) | | `lastAccessedOn` | `blob.last_accessed_on` (UTC datetime) | | `contentType` | `blob.content_settings.content_type` | | `contentEncoding` | `blob.content_settings.content_encoding` | | `contentLanguage` | `blob.content_settings.content_language` | | `contentDisposition` | `blob.content_settings.content_disposition` | | `cacheControl` | `blob.content_settings.cache_control` | | `contentMd5` | `blob.content_settings.content_md5` | | `etag` | `blob.etag` | | `blobType` | `blob.blob_type.value` (e.g. `BlockBlob`) | | `blobTier` | `blob.blob_tier.value` (e.g. `Hot`) | | `archiveStatus` | `blob.archive_status` | | `serverEncrypted` | `blob.server_encrypted` | | `deleted` | `blob.deleted` | | `snapshotId` | `blob.snapshot` | | `versionId` | `blob.version_id` | | `tagCount` | `blob.tag_count` | Schema-level quality checks (`schema.quality`) on the `rowCount` metric are evaluated as **file-count** thresholds against the total number of blobs found under the prefix. `contentType` is normalised before comparison: MIME parameters are stripped, so `application/json; charset=utf-8` matches `application/json`. Supported `location` URL formats (on the server block): - `https://.blob.core.windows.net//` - `abfss://@.dfs.core.windows.net/` - `azure://@.blob.core.windows.net/` - `wasbs://@.blob.core.windows.net/` The metadata-check path also accepts `DATACONTRACT_AZURE_CONNECTION_STRING` or `DATACONTRACT_AZURE_STORAGE_ACCOUNT_KEY` instead of the service principal variables. --- ## Google BigQuery ## ![](/img/icons/bigquery.svg) Google BigQuery Go from an existing BigQuery table to a tested data contract in about five minutes: import the schema straight from BigQuery, then test the actual data against it. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[bigquery]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate The easiest way is Application Default Credentials (ADC) — both `import` and `test` pick them up automatically: ```bash gcloud auth application-default login ``` Your account (or service account) needs the **BigQuery Job User**, **BigQuery Data Viewer** and **BigQuery Read Session User** roles. For service-account key files and CI/CD, see the [BigQuery Reference](../reference/bigquery.md). ### 3. Create a contract from your tables Import the table metadata directly from the BigQuery API. This also generates a ready-to-test `servers` block: ```bash datacontract import bigquery \ --project my-project \ --dataset my_dataset \ --table orders \ --output datacontract.yaml ``` Repeat `--table` for multiple tables, or omit it to import every table in the dataset. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: bigquery (type=bigquery, dataset=my_dataset, project=my-project) ╭────────┬─────────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼─────────────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has physical type INTEGER │ order_id │ │ │ ... │ │ │ │ ╰────────┴─────────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 24 checks. Took 6.1 seconds. ``` ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule to a schema in `datacontract.yaml`: ```yaml schema: - name: orders # ... quality: - type: sql description: No order has a negative total query: SELECT COUNT(*) FROM orders WHERE order_total < 0 mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options (service-account keys, WIF, billing project) and the data type mappings: **[BigQuery Reference](../reference/bigquery.md)**. ### Troubleshooting - **`Your default credentials were not found`** — run `gcloud auth application-default login`, or set `GOOGLE_APPLICATION_CREDENTIALS` / `DATACONTRACT_BIGQUERY_ACCOUNT_INFO_JSON_PATH` to a service-account key file. - **`403 Access Denied`** — the account is missing **BigQuery Job User** (to run query jobs), **BigQuery Data Viewer** (to read the tables) or **BigQuery Read Session User** (to fetch query results through the Storage Read API). - **Queries billed to the wrong project** — set `DATACONTRACT_BIGQUERY_BILLING_PROJECT`. --- ## ClickHouse ## ![](/img/icons/clickhouse.svg) ClickHouse Test data in ClickHouse. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[clickhouse]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate Create a `.env` file in your working directory (or export the variables): ```bash # .env DATACONTRACT_CLICKHOUSE_USERNAME=analyst DATACONTRACT_CLICKHOUSE_PASSWORD=mysecretpassword ``` Without them, the CLI connects as ClickHouse's `default` user with an empty password. For ClickHouse Cloud, set `DATACONTRACT_CLICKHOUSE_SECURE=true` — see the [ClickHouse Reference](../reference/clickhouse.md). ### 3. Create a contract from your tables Import the table metadata directly from `system.columns`. This also generates a ready-to-test `servers` block: ```bash datacontract import clickhouse \ --source localhost \ --database sales \ --table orders \ --output datacontract.yaml ``` Repeat `--table` for multiple tables, or omit it to import every table and view in the database. Add `--port` if the HTTP interface doesn't listen on the default `8123`. Only have a DDL script? `datacontract import sql --source orders.sql --dialect clickhouse` works too, but writes a `servers` block with placeholder values that you have to fill in by hand. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: clickhouse (type=clickhouse, host=localhost, port=8123, database=sales) ╭────────┬────────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼────────────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has physical type String │ order_id │ │ │ passed │ Check that field order_id has no missing values │ order_id │ │ │ ... │ │ │ │ ╰────────┴────────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 33 checks. Took 0.5 seconds. ``` ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule to a schema in `datacontract.yaml`: ```yaml schema: - name: orders # ... quality: - type: sql description: No order has a negative total query: SELECT countIf(order_total < 0) FROM orders mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options and the data type handling: **[ClickHouse Reference](../reference/clickhouse.md)**. ### Troubleshooting - **`Port 9000 is for clickhouse-client program`** — the CLI talks to ClickHouse over HTTP. A contract that names the native port `9000` (or `9440`) is connected to `8123` (or `8443`) automatically; any other native port has to be replaced by the HTTP port in the `servers` block or with `DATACONTRACT_CLICKHOUSE_PORT`. - **`Authentication failed`** — set `DATACONTRACT_CLICKHOUSE_USERNAME` and `DATACONTRACT_CLICKHOUSE_PASSWORD`; without them the CLI logs in as `default`. --- ## Databricks ## ![](/img/icons/databricks.svg) Databricks Go from an existing Unity Catalog table to a tested data contract in about five minutes: import the schema from Unity Catalog, then test the actual data on a SQL warehouse. Works with Unity Catalog and the Hive metastore; testing needs a running SQL warehouse or compute cluster. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[databricks]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate Create a `.env` file in your working directory (or export the variables): ```bash # .env DATACONTRACT_DATABRICKS_SERVER_HOSTNAME=dbc-abcdefgh-1234.cloud.databricks.com DATACONTRACT_DATABRICKS_HTTP_PATH=/sql/1.0/warehouses/b053a3ff69ee87a8 DATACONTRACT_DATABRICKS_TOKEN= ``` Find the hostname and HTTP path under your SQL warehouse's **Connection details** tab. For OAuth service principals and profile-based auth, see the [Databricks Reference](../reference/databricks.md). ### 3. Create a contract from your tables Import the table metadata directly from Unity Catalog. This also generates a ready-to-test `servers` block: ```bash datacontract import databricks \ --table my_catalog.my_schema.orders \ --output datacontract.yaml ``` Repeat `--table` for multiple tables. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: databricks (type=databricks, schema=my_schema, catalog=my_catalog) ╭────────┬────────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼────────────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has physical type string │ order_id │ │ │ ... │ │ │ │ ╰────────┴────────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 24 checks. Took 8.4 seconds. ``` ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule to a schema in `datacontract.yaml`: ```yaml schema: - name: orders # ... quality: - type: sql description: No order has a negative total query: SELECT COUNT(*) FROM orders WHERE order_total < 0 mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options (PAT, OAuth M2M, profiles, precedence order) and the data type mappings: **[Databricks Reference](../reference/databricks.md)**. ### Programmatic (in a notebook or pipeline) When running in a notebook or pipeline, pass the existing `spark` session — no extra authentication is required (requires Databricks Runtime with Python ≥ 3.10). ```python from datacontract.data_contract import DataContract data_contract = DataContract( data_contract_file="/Volumes/acme_catalog_prod/orders_latest/datacontract/datacontract.yaml", spark=spark) run = data_contract.test() run.result ``` :::tip[Installing on Databricks compute] The same `databricks` extra works there — it installs no PySpark of its own, so it cannot shadow the build the cluster ships. On the LTS ML runtimes (15.4, 16.4), installing via `%pip install` in notebooks can cause issues — add `datacontract-cli[databricks]` as a **PyPI library** on the cluster instead (Compute → your cluster → Libraries → Install new → PyPI), then restart the cluster. See **[Databricks Notebooks and Jobs](../databricks.md)** for the full walkthrough. ::: ### Troubleshooting - **`Invalid HTTP path`, or the test hangs** — `DATACONTRACT_DATABRICKS_HTTP_PATH` must point at a running SQL warehouse or cluster (check the **Connection details** tab). `import databricks` works without it, but `test` requires it. - **`403 Invalid access token`** — the PAT is expired or belongs to a different workspace than `DATACONTRACT_DATABRICKS_SERVER_HOSTNAME`. - **`TABLE_OR_VIEW_NOT_FOUND`** — the token's principal lacks `USE CATALOG`/`USE SCHEMA`/`SELECT` privileges on the table. --- ## Spark DataFrame ## ![](/img/icons/spark.svg) Spark DataFrame Test Spark DataFrames in a pipeline before writing them to a data source. DataFrames are registered as named temporary views; multiple views are supported if the contract has multiple schemas. This connection is used programmatically from Python — no credentials are needed, the existing Spark session is reused. ### 1. Install Install the `dataframe` extra into the environment your pipeline runs in — this connection is used from Python, so it is a library install rather than a CLI tool: ```bash pip install 'datacontract-cli[dataframe]' ``` The extra adds the Spark backend of the query engine, not PySpark itself. You already have PySpark wherever you can build the session this connection needs, and on a Spark runtime such as Databricks or EMR a second copy would shadow the one the cluster ships. Outside such a runtime, install PySpark yourself. See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Create a contract from your DataFrames Inside an active Spark session, import the schema of registered tables or views: ```bash datacontract import spark --tables my_table --output datacontract.yaml ``` The generated contract includes a `servers` entry for a DataFrame. `dataframe` is not an ODCS server type, so it is written as `type: custom` with a `customType` property: ```yaml servers: - server: local type: custom customProperties: - property: customType value: dataframe ``` ### 3. Test the DataFrame Register the DataFrame as a temporary view named like the schema, then run the test with the Spark session: ```python from datacontract.data_contract import DataContract df.createOrReplaceTempView("my_table") data_contract = DataContract( data_contract_file="datacontract.yaml", spark=spark, ) run = data_contract.test() assert run.result == "passed" ``` ### 4. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, mark a field as `required: true` or add a quality rule — and run the test again: `run.result` becomes `"failed"` and `run.checks` lists each violation, so the assert stops your pipeline before bad data is written. ### Reference The Spark data type mappings: **[Spark DataFrame Reference](../reference/dataframe.md)**. --- ## DuckDB ## ![](/img/icons/duckdb.svg) DuckDB Test the tables inside a DuckDB database file. This is different from [Local files](./local.md): there the CLI reads data *files* (CSV, JSON, XML, Parquet, Delta) through DuckDB, and the contract's `path` points at those files. Here the DuckDB database itself is the data source, and the contract's schema objects are the tables inside it. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[duckdb]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Describe the server ODCS carries the path to the database file in the server's `database` field. `schema` is optional and defaults to DuckDB's `main`: ```yaml servers: - server: production type: duckdb database: ./warehouse.duckdb schema: main schema: - name: orders properties: - name: order_id logicalType: string physicalType: VARCHAR required: true unique: true - name: order_total logicalType: integer physicalType: INTEGER ``` Each schema object is looked up as a table of that name in the database. The database file is opened **read-only**, so a test never modifies the data it is testing, and it can run while another process holds the same database open. ### 3. Test the data against the contract ```bash datacontract test datacontract.yaml ``` ### Quality rules Custom SQL rules are written in DuckDB SQL, and must be [read-only queries](../quality-rules/sql.md): ```yaml quality: - type: sql description: Every order total is one of the allowed amounts. query: SELECT count(*) FROM orders WHERE NOT list_contains([100, 200], order_total) mustBe: 0 ``` ### Environment variables | Environment Variable | Example | Description | |---|---|---| | `DATACONTRACT_DUCKDB_DATABASE` | `./warehouse.duckdb` | Path to the DuckDB database file. Overrides the server's `database`. | | `DATACONTRACT_DUCKDB_SCHEMA` | `sales` | The schema to resolve tables in. Overrides the server's `schema`. | ### Troubleshooting - **`Table with name ... does not exist`** — the table lives in a schema other than `main`. Set the server's `schema`, so that both the table lookup and the SQL of the quality rules resolve against it. - **`Could not open the duckdb database`** — the path is resolved relative to the working directory, not to the contract. A database written by a newer DuckDB than the CLI's also fails here. - **A write in a quality rule fails** — the database is opened read-only, and a quality rule must be a read-only query in any case. --- ## Exasol ## ![](/img/icons/exasol.svg) Exasol Test data in Exasol. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[exasol]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate Create a `.env` file in your working directory (or export the variables): ```bash # .env DATACONTRACT_EXASOL_USERNAME=sys DATACONTRACT_EXASOL_PASSWORD=mysecretpassword ``` The server certificate is verified against the system CA store; for a self-signed certificate, pin its fingerprint — see the [Exasol Reference](../reference/exasol.md#tls). ### 3. Describe the server in the contract The `exasol` server type was added in ODCS v3.2.0, so the contract has to declare `apiVersion: v3.2.0`. There is no `datacontract import exasol` yet: write the schema by hand, or start from a DDL script with `datacontract import sql --source orders.sql --dialect postgres` and replace the generated `servers` block. ```yaml apiVersion: v3.2.0 kind: DataContract id: orders version: 1.0.0 status: active servers: - server: production type: exasol host: exasol.acme.com port: 8563 schema: sales schema: - name: orders properties: - name: order_id logicalType: integer physicalType: DECIMAL(18,0) primaryKey: true - name: order_total logicalType: number physicalType: DECIMAL(10,2) ``` `host` may be a cluster range (`n11..14.acme.com`). ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: production (type=exasol, host=exasol.acme.com, port=8563, schema=sales) ╭────────┬───────────────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼───────────────────────────────────────────────────────────┼──────────┼─────────┤ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has physical type DECIMAL(18,0) │ order_id │ │ │ ... │ │ │ │ ╰────────┴───────────────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 6 checks. Took 1.4 seconds. ``` ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule to a schema in `datacontract.yaml`: ```yaml schema: - name: orders # ... quality: - type: sql description: No order has a negative total query: SELECT COUNT(*) FROM orders WHERE order_total < 0 mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options (including TLS) and the data type handling: **[Exasol Reference](../reference/exasol.md)**. ### Troubleshooting - **`Could not connect to Exasol: [SSL: CERTIFICATE_VERIFY_FAILED]`** — the cluster uses a certificate the system CA store does not know; pin it with `DATACONTRACT_EXASOL_FINGERPRINT`, see [TLS](../reference/exasol.md#tls). - **`Connection exception - schema ... not found`** — the `schema` in the `servers` block must exist, and a mixed-case schema created with quotes must be spelled exactly (`"Sales"` is not found by `sales`). --- ## Google Cloud Storage ## ![](/img/icons/gcs.svg) Google Cloud Storage (GCS) The [Amazon S3](./s3.md) integration also works with files on Google Cloud Storage through its [interoperability](https://cloud.google.com/storage/docs/interoperability). ODCS defines no `gcs` server type, so a GCS contract uses `type: s3` with `https://storage.googleapis.com` as the endpoint URL and the `s3://` scheme for the location — `datacontract import gcs` writes exactly that. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[gcs]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate Create an [HMAC key](https://cloud.google.com/storage/docs/authentication/hmackeys) for your user or service account, then create a `.env` file in your working directory (or export the variables): ```bash # .env DATACONTRACT_S3_ACCESS_KEY_ID=GOOG1EZZZXXXXXXXXXXXXX DATACONTRACT_S3_SECRET_ACCESS_KEY=PDWWpbXXXXXXXXXXXXX ``` ### 3. Create a contract from your files Import the schema straight from the bucket. This also generates a ready-to-test `servers` block: ```bash datacontract import gcs \ --source 's3://my-bucket/orders/*.json' \ --output datacontract.yaml ``` duckdb reads Google Cloud Storage through its S3-compatible endpoint, so the location uses the `s3://` scheme rather than `gs://`; a `gs://` source is rewritten for you. The format is taken from the file suffix; pass `--format` for Delta tables, which have none. The import reads only the field names and types. Properties like `required` cannot be inferred from the files and need to be adjusted manually. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: production (type=s3, format=json, location=s3://my-bucket/orders/*.json) ╭────────┬─────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼─────────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has no missing values │ order_id │ │ │ ... │ │ │ │ ╰────────┴─────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 11 checks. Took 3.1 seconds. ``` ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule: ```yaml schema: - name: orders # ... quality: - type: sql description: No order has a negative total query: SELECT COUNT(*) FROM orders WHERE order_total < 0 mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options and the data type handling per file format: **[GCS Reference](../reference/gcs.md)**. ### Troubleshooting - **`403 Forbidden`** — the HMAC key's principal lacks `storage.objects.get`/`storage.objects.list` on the bucket, or the key was deactivated. - **`No files found that match the pattern`** — the `location` must use the `s3://` scheme (not `gs://`), and `endpointUrl` must be `https://storage.googleapis.com`. --- ## SAP HANA ## ![](/img/icons/sap.svg) SAP HANA Test data in SAP HANA Cloud and in the Open SQL schemas of SAP Datasphere. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[hana]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. :::note `hana` is not part of `datacontract-cli[all]`. It needs `hdbcli`, the SAP HANA Client, which SAP distributes under its own license (the SAP Developer License), so you have to opt into it explicitly and accept that license. ::: ### 2. Authenticate Create a `.env` file in your working directory (or export the variables): ```bash # .env DATACONTRACT_HANA_USERNAME=MY_USER DATACONTRACT_HANA_PASSWORD=mysecretpassword ``` | Environment variable | Default | Description | |-------------------------------------------------|---------|-----------------------------------------------------------| | `DATACONTRACT_HANA_USERNAME` | | Database user (required) | | `DATACONTRACT_HANA_PASSWORD` | | Password (required) | | `DATACONTRACT_HANA_ENCRYPT` | `true` | Use TLS. Keep it on for HANA Cloud and Datasphere. | | `DATACONTRACT_HANA_SSL_VALIDATE_CERTIFICATE` | `true` | Validate the server certificate | | `DATACONTRACT_HANA_SSL_HOSTNAME_IN_CERTIFICATE` | `*` | Expected host name in the certificate | ### 3. Describe the server in the contract The `hana` server type was added in ODCS v3.2.0, so the contract has to declare `apiVersion: v3.2.0`. There is no `datacontract import hana` yet: write the schema by hand, or start from a DDL script with `datacontract import sql --source orders.sql --dialect postgres` and replace the generated `servers` block. ```yaml apiVersion: v3.2.0 kind: DataContract id: orders version: 1.0.0 status: active servers: - server: production type: hana host: abcd1234-1234-5678-90ab-cdef12345678.hana.prod-eu10.hanacloud.ondemand.com port: 443 schema: SALES schema: - name: orders physicalName: ORDERS properties: - name: order_id physicalName: ORDER_ID logicalType: string physicalType: NVARCHAR primaryKey: true - name: order_total physicalName: ORDER_TOTAL logicalType: number physicalType: DECIMAL ``` `schema` is the HANA schema the objects live in, and is required. For SAP Datasphere, use the Open SQL schema you exposed the views in, and its host and port from the database user's connection details. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: production (type=hana, host=..., port=443, schema=SALES) ╭────────┬─────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼─────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field ORDER_ID is present │ ORDER_ID │ │ │ passed │ Check that field ORDER_ID has type NVARCHAR │ ORDER_ID │ │ │ ... │ │ │ │ ╰────────┴─────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 7 checks. Took 1.4 seconds. ``` The engine reads the declared types from `SYS.TABLE_COLUMNS` and `SYS.VIEW_COLUMNS`, and runs every other check as SQL against the objects themselves. ### 5. Let it catch a violation Add a quality rule to a schema in `datacontract.yaml`: ```yaml schema: - name: orders # ... quality: - type: sql description: No order has a negative total query: SELECT COUNT(*) FROM {model} WHERE ORDER_TOTAL < 0 mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1`, ready for [CI/CD and scheduled runs](../scheduling/index.md). ### SAP Datasphere Objects in an Open SQL schema are mostly views, and a view carries no primary key or unique constraint in the catalog. `primaryKey` and `unique` are therefore validated against the data (`COUNT(*)` over the duplicated keys) rather than against constraint metadata, so they report actual duplicates even where nothing declares the key. ### Notes and limitations - `datacontract test --filter` / `--filters` restrict the rows the checks read, and `--dimension`, `--quality-id`, `--tag`, `--checks`, `--dry-run` and `--metadata-only` work as they do for the other engines. - Quality rules of `type: custom` with `engine: soda` are not executed and report a warning. Write them as `type: sql` instead. - `--include-failed-samples` collects no rows for HANA. ### Troubleshooting - **`Install the extra datacontract-cli[hana]`** — `hdbcli` is missing, see step 1. - **`Required environment variable DATACONTRACT_HANA_USERNAME is not set`** — the credentials come from the environment, never from the contract. - **`Model SALES.ORDERS does not exist`** — the object names are case-sensitive as stored in the catalog, which is upper case unless it was created quoted. Use `physicalName` for the name in the database and keep `name` as the business name. --- ## Apache Hive ## ![](/img/icons/database.svg) Apache Hive Test data in Apache Hive, through HiveServer2. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[hive]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate A HiveServer2 without authentication (`hive.server2.authentication=NONE`) needs no configuration. For LDAP, create a `.env` file in your working directory (or export the variables): ```bash # .env DATACONTRACT_HIVE_USERNAME=analyst DATACONTRACT_HIVE_PASSWORD=mysecretpassword DATACONTRACT_HIVE_AUTH_MECHANISM=LDAP ``` Kerberos, TLS, and HTTP transport are configured the same way — see the [Hive Reference](../reference/hive.md). ### 3. Create a contract from your tables Import the table metadata directly from Hive. This also generates a ready-to-test `servers` block: ```bash datacontract import hive \ --source localhost \ --database sales \ --table orders \ --output datacontract.yaml ``` Repeat `--table` for multiple tables, or omit it to import every table and view in the database. Add `--port` if HiveServer2 doesn't listen on the default `10000`. Only have a DDL script? `datacontract import sql --source orders.sql --dialect hive` works too, but writes a `servers` block with placeholder values that you have to fill in by hand. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: hive (type=hive, host=localhost, port=10000, database=sales) ╭────────┬────────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼────────────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has physical type string │ order_id │ │ │ ... │ │ │ │ ╰────────┴────────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 37 checks. Took 4.4 seconds. ``` Each check is a Hive query, so a test run takes as long as your cluster needs to answer a few dozen aggregations. ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule to a schema in `datacontract.yaml`: ```yaml schema: - name: orders # ... quality: - type: sql description: No order has a negative total query: SELECT COUNT(*) FROM orders WHERE order_total < 0 mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options and the data type handling: **[Hive Reference](../reference/hive.md)**. ### Troubleshooting - **`TSocket read 0 bytes`** — the client and HiveServer2 disagree on the transport: check `DATACONTRACT_HIVE_AUTH_MECHANISM` against `hive.server2.authentication`, and set `DATACONTRACT_HIVE_USE_HTTP_TRANSPORT=true` when `hive.server2.transport.mode=http`. - **`Table not found`** — the `database` in the `servers` block must contain the table; Hive table names are lower case. --- ## Apache Iceberg ## ![](/img/icons/iceberg.svg) Apache Iceberg Test Apache Iceberg tables through a REST catalog such as Polaris, Nessie, Unity Catalog, AWS Glue, or Amazon S3 Tables. The CLI loads each table with [pyiceberg](https://py.iceberg.apache.org/), scans it to Arrow, and runs the checks in DuckDB. For S3 Tables and Glue the requests are signed with your AWS credentials; see the [reference](../reference/iceberg.md#amazon-s3-tables). ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[iceberg]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate For **Amazon S3 Tables or AWS Glue**, use AWS credentials, not OAuth. The dedicated [Amazon S3 Tables guide](./s3-tables.md) covers `AWS_PROFILE`, SSO login, the table bucket ARN, import/test commands, and a deliberate quality failure. For **OAuth-based REST catalogs**, the catalog and data-file authentication are separate. Supply a client credential or bearer token as required by your catalog; data files on S3 use the same `DATACONTRACT_S3_*` options as the [S3](./s3.md) source, unless the catalog vends credentials. ```bash # catalog: one of export DATACONTRACT_ICEBERG_CREDENTIAL=client_id:client_secret export DATACONTRACT_ICEBERG_TOKEN=eyJ... # data files (skip when the catalog vends credentials) export DATACONTRACT_S3_ACCESS_KEY_ID=... export DATACONTRACT_S3_SECRET_ACCESS_KEY=... export DATACONTRACT_S3_REGION=eu-central-1 ``` ### 3. Create a contract from a table ```bash datacontract import iceberg --catalog-url https://polaris.example.com/api/catalog \ --catalog main --namespace sales --table orders --output datacontract.yaml ``` The contract gets the table's schema, with `map`, `list`, and `struct` columns expanded, and a ready-to-test `servers` block: ```yaml servers: - server: production type: iceberg catalog: main catalogUrl: https://polaris.example.com/api/catalog namespace: sales ``` `datacontract import iceberg --source schema.json` still imports from a schema file, without a server. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` Each schema object is one table in the namespace. A `physicalName` on the schema object names the table when it differs from the schema name; a name with a dot is used as the full identifier. ### 5. Let it catch a violation Add a `quality` rule to the contract and run the test again: ```yaml quality: - type: sql description: No order without lines query: SELECT count(*) FROM orders WHERE cardinality(lines) = 0 mustBe: 0 ``` The query runs in DuckDB against the scanned table, so DuckDB's SQL dialect applies. Use the schema object's logical `name` in SQL, not its `physicalName`. The full selected tables are read into memory before the checks run. ### Reference All options and the data type handling: **[Apache Iceberg Reference](../reference/iceberg.md)**. ### Troubleshooting - **`catalogUrl is required`** — the server needs the REST endpoint in `catalogUrl`, or `DATACONTRACT_ICEBERG_CATALOG_URL`. - **`Table 'sales.orders' was not found`** — check `namespace`; the identifier the CLI asked for is in the message. - **Expired AWS SSO session** — run `aws sso login --profile "$AWS_PROFILE"` again and retry. Check the selected account with `aws sts get-caller-identity`. - **`403` while reading S3 Tables data files** — verify `s3tables:GetTableData` on the table and that temporary credentials include their session token. A successful catalog connection does not prove data-file access. - **`403` with other catalogs** — check the data-file credentials (`DATACONTRACT_S3_*`) or the catalog's credential-vending configuration. --- ## Apache Impala ## ![](/img/icons/impala.svg) Apache Impala Run checks against an Apache Impala cluster. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[impala]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate Create a `.env` file in your working directory (or export the variables): ```bash # .env DATACONTRACT_IMPALA_USERNAME=analytics_user DATACONTRACT_IMPALA_PASSWORD=mysecretpassword ``` On a Cloudera Virtual Warehouse, or any cluster reached over HTTPS, also set the transport options listed in the [reference](../reference/impala.md#cloudera-virtual-warehouse). ### 3. Create a contract from your tables Get the DDL of a table (`SHOW CREATE TABLE orders;` in impala-shell or Hue), save it to a file, and import it. Impala DDL is Hive-compatible, so use the `spark` dialect: ```bash datacontract import sql --source orders.sql --dialect spark --output datacontract.yaml ``` The SQL import can't know your connection details, so it writes a `servers` block with placeholder values. Open `datacontract.yaml` and fill in your cluster: ```yaml servers: - server: impala type: impala host: my-impala-host port: 21050 # 443 for a Cloudera Virtual Warehouse database: my_database ``` ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: impala (type=impala, host=my-impala-host, port=21050, database=my_database) ╭────────┬───────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼───────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has type string │ order_id │ │ │ ... │ │ │ │ ╰────────┴───────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 24 checks. Took 2.8 seconds. ``` ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule to a schema in `datacontract.yaml`: ```yaml schema: - name: orders # ... quality: - type: sql description: No order has a negative total query: SELECT COUNT(*) FROM orders WHERE order_total < 0 mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options (SSL, transport, auth mechanism) and the data type mappings: **[Impala Reference](../reference/impala.md)**. ### Troubleshooting - **`TSocket read 0 bytes`** — the client is speaking binary thrift to an HTTPS endpoint. On a Cloudera Virtual Warehouse (or anything else behind an HTTPS load balancer) set `DATACONTRACT_IMPALA_AUTH_MECHANISM=LDAP`, `DATACONTRACT_IMPALA_USE_HTTP_TRANSPORT=true`, and `DATACONTRACT_IMPALA_HTTP_PATH=cliservice`, with `port: 443` in the `servers` block. See the [reference](../reference/impala.md#cloudera-virtual-warehouse). - **`AuthorizationException`** — the user lacks `SELECT` on the table or the Ranger/Sentry policy doesn't cover the database in the `servers` block. --- ## Kafka ## ![](/img/icons/kafka.svg) Kafka Test data in Kafka topics. Kafka support is currently considered **experimental**. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[kafka]' ``` No Java runtime is needed: the topic is consumed and decoded in Python, and the checks run in DuckDB. See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate Create a `.env` file in your working directory (or export the variables): ```bash # .env DATACONTRACT_KAFKA_SASL_USERNAME=mykey DATACONTRACT_KAFKA_SASL_PASSWORD=mysecret ``` If no username/password is set, the CLI connects without authentication (e.g. a local broker). If the topic is Avro-encoded through the Confluent Schema Registry, add the registry as well, so the messages are decoded with the schema they were actually written with: ```bash DATACONTRACT_KAFKA_SCHEMA_REGISTRY_URL=https://psrc-12345.eu-central-1.aws.confluent.cloud DATACONTRACT_KAFKA_SCHEMA_REGISTRY_USERNAME=myregistrykey DATACONTRACT_KAFKA_SCHEMA_REGISTRY_PASSWORD=myregistrysecret ``` ### 3. Create a contract for your topic If you have an Avro schema for the topic (e.g. from a schema registry), import it: ```bash datacontract import avro --source orders.avsc --output datacontract.yaml ``` The topic read is the schema's `physicalName` (or its `name`, if unset); set it to your topic name. Then add a `servers` entry pointing at your broker: ```yaml servers: - server: production type: kafka host: abc-12345.eu-central-1.aws.confluent.cloud:9092 format: json # or avro ``` ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: production (type=kafka, format=json, host=abc-12345.eu-central-1.aws.confluent.cloud:9092) ╭────────┬─────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼─────────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has no missing values │ order_id │ │ │ ... │ │ │ │ ╰────────┴─────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 24 checks. Took 8.4 seconds. ``` ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, mark a field as `required: true` or restrict a field to its allowed values. Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options (SASL mechanisms) and the Avro data type mappings: **[Kafka Reference](../reference/kafka.md)**. ### Troubleshooting - **Authentication failures against Confluent Cloud** — use an API key/secret as `SASL_USERNAME`/`SASL_PASSWORD` with the default `PLAIN` mechanism. - **The test reads no messages** — the check consumes the topic from the beginning; verify the topic name in the schema's `physicalName` and that the topic contains messages in the declared `format`. - **The test runs out of memory on a large topic** — every message is held in memory. Set `DATACONTRACT_KAFKA_MAX_MESSAGES` to check a sample of the topic instead; the run then reports that it read only part of it. - **`Cannot decode the Avro messages of the topic`** — the schema used for decoding is not the one the messages were written with. For a topic produced through the Confluent Schema Registry, set `DATACONTRACT_KAFKA_SCHEMA_REGISTRY_URL`; otherwise re-import the contract from the topic's Avro schema with `datacontract import avro`. --- ## Local files ## ![](/img/icons/local.svg) Local files Test local files in Parquet, JSON, CSV, XML, Excel, or Delta format. This is the fastest way to see the CLI in action — no warehouse, no credentials. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[duckdb]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Create a contract from a file Take any CSV file — or use this one, saved as `orders.csv`: ```text order_id,order_timestamp,customer_id,order_total,status ORD-1001,2024-01-01T10:00:00Z,CUST-1,4999,delivered ORD-1002,2024-01-02T11:30:00Z,CUST-2,2500,shipped ORD-1003,2024-01-03T09:15:00Z,CUST-3,1299,pending ``` Import it. The CLI profiles the data and generates a contract with a schema, value ranges, and a ready-to-test `servers` block: ```bash datacontract import csv --source orders.csv --output datacontract.yaml ``` ### 3. Test the data against the contract ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: production (type=local, format=csv, path=orders.csv) ╭────────┬──────────────────────────────────────────────────────┬─────────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼──────────────────────────────────────────────────────┼─────────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field order_total has a minimum of 1299.0 │ order_total │ │ │ passed │ Check that field order_total has a maximum of 4999.0 │ order_total │ │ │ ... │ │ │ │ ╰────────┴──────────────────────────────────────────────────────┴─────────────┴─────────╯ 🟢 Data contract is valid. Ran 17 checks. Took 1.2 seconds. ``` ### 4. Let it catch a violation Now break the data — append a row with a negative total, a duplicate customer and a repeated status: ```bash echo 'ORD-1004,2024-01-04T12:00:00Z,CUST-1,-100,delivered' >> orders.csv datacontract test datacontract.yaml ``` ``` 🔴 Data contract is invalid, found the following errors: 1) customer_id Check that unique field customer_id has no duplicate values: Actual duplicate_count(customer_id) was 1, expected = 0 2) order_total Check that field order_total has a minimum of 1299.0: Actual invalid_count(order_total) was 1, expected = 0 3) status Check that unique field status has no duplicate values: Actual duplicate_count(status) was 1, expected = 0 ``` The command exits with code `1`, so the same call works as a gate in [CI/CD pipelines](../scheduling/index.md). ### Excel workbooks Excel workbooks (`format: xlsx`) need `pip install 'datacontract-cli[xlsx]'`. Each schema is one sheet: its `physicalName` is the sheet's name, matched case-sensitively, and a contract with a single schema may leave it out to read the first sheet. The first row of a sheet holds the column names. Columns before the first and after the last header cell are not read, and a column with an empty header cell between them gets a generated name. Rows that are empty in every column are skipped. ```yaml servers: - server: local type: local path: ./delivery.xlsx format: xlsx schema: - name: orders physicalName: Orders - name: country_codes physicalName: Country Codes ``` The `path` names a single workbook, without a glob. When several schemas share it, each needs its `physicalName`. Formulas are read as the values Excel last stored for them, and the older `.xls` format is not supported. ### XML files XML documents (`format: xml`) have a guide of their own, from importing their XML Schema to testing nested elements and attributes: **[XML files](./xml.md)**. ### Reference No environment variables are needed for local files. Data type inference and the per-format type handling: **[Local Files Reference](../reference/local.md)**. CSV files are read as UTF-8 unless the server declares an `encoding` (ODCS v3.2.0), such as `ISO-8859-1` or `UTF-16`. JSON files are always read as UTF-8. JSON files (`.json`, `.jsonl`, `.ndjson`) are also validated against a JSON Schema derived from the contract, and every record that breaks it is reported, without stopping the other checks. Without a `delimiter`, a file holds one record, an array of records, or one record per line. ### Troubleshooting - **`No files found that match the pattern`** — the `path` is a glob over file paths, not a directory, and it is resolved relative to the working directory rather than to the contract. - **No checks run at all** — the `format` in the `servers` block must be one of `csv`, `json`, `parquet`, `xml`, `xlsx`, or `delta`. It is never guessed at test time (only `datacontract import` infers it from the file suffix), so a missing or misspelled `format` leaves the table unreadable. - **Every schema reads the same files** — with more than one schema in the contract, put the `{model}` placeholder in the `path` (e.g. `./data/{model}/*.parquet`); it is substituted with each schema's name. - **A CSV or XML value fails as a read error instead of a type check** — CSV and XML files are read *as* the contract's types, so a value that cannot be coerced surfaces while reading. See [Data types](../reference/local.md#data-types). Ready for your real data? Do the same against [Snowflake](./snowflake.md), [BigQuery](./bigquery.md), or [Databricks](./databricks.md). --- ## MySQL ## ![](/img/icons/mysql.svg) MySQL Test data in MySQL or MySQL-compatible databases (e.g. MariaDB). ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[mysql]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate Create a `.env` file in your working directory (or export the variables): ```bash # .env DATACONTRACT_MYSQL_USERNAME=root DATACONTRACT_MYSQL_PASSWORD=mysecretpassword ``` ### 3. Create a contract from your tables Import the table metadata directly from the database. This also generates a ready-to-test `servers` block: ```bash datacontract import mysql \ --source localhost \ --database mydb \ --table orders \ --output datacontract.yaml ``` `--source` is the host of your MySQL server. Add `--port` if it doesn't listen on the default `3306`, repeat `--table` for multiple tables, or omit it to import every table in the database. Only have a DDL file? `datacontract import sql --source orders.sql --dialect mysql` works too, but writes a `servers` block with placeholder values that you have to fill in by hand. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: mysql (type=mysql, host=localhost, port=3306, database=mydb) ╭────────┬───────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼───────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has type string │ order_id │ │ │ ... │ │ │ │ ╰────────┴───────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 24 checks. Took 2.1 seconds. ``` ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule to a schema in `datacontract.yaml`: ```yaml schema: - name: orders # ... quality: - type: sql description: No order has a negative total query: SELECT COUNT(*) FROM orders WHERE order_total < 0 mustBe: 0 ``` The tables are copied into DuckDB before the checks run, so write the query in [DuckDB SQL](../quality-rules/sql.md#sql-dialect), not MySQL. Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options and the data type mappings: **[MySQL Reference](../reference/mysql.md)**. ### Troubleshooting - **`Access denied for user`** — check the two environment variables above and the user's host restrictions (`'user'@'%'` vs `'user'@'localhost'`). - **`Can't connect to MySQL server`** — host/port in the `servers` block are wrong, or the server doesn't accept remote connections (`bind-address`). --- ## Oracle ## ![](/img/icons/oracle.svg) Oracle Test data in Oracle Database. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[oracle]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate Create a `.env` file in your working directory (or export the variables): ```bash # .env DATACONTRACT_ORACLE_USERNAME=system DATACONTRACT_ORACLE_PASSWORD=mysecretpassword ``` ### 3. Create a contract from your tables Import the table metadata directly from the database. This also generates a ready-to-test `servers` block: ```bash datacontract import oracle \ --source localhost \ --service-name ORCL \ --schema ADMIN \ --table ORDERS \ --output datacontract.yaml ``` `--source` is the host and `--service-name` the service. Repeat `--table` for multiple tables, or omit it to import every table in the schema; Oracle upper-cases identifiers, so `--schema` is upper-cased for you. Only have a DDL script? `datacontract import sql --source orders.sql --dialect oracle` works too, but writes a `servers` block with placeholder values that you have to fill in by hand. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: oracle (type=oracle, host=localhost, port=1521, schema=ADMIN) ╭────────┬──────────────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼──────────────────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'ORDER_ID' is present │ ORDER_ID │ │ │ passed │ Check that field ORDER_ID has physical type VARCHAR2(36) │ ORDER_ID │ │ │ ... │ │ │ │ ╰────────┴──────────────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 24 checks. Took 3.4 seconds. ``` ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule to a schema in `datacontract.yaml`: ```yaml schema: - name: ORDERS # ... quality: - type: sql description: No order has a negative total query: SELECT COUNT(*) FROM ORDERS WHERE ORDER_TOTAL < 0 mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options and the data type mappings: **[Oracle Reference](../reference/oracle.md)**. ### Troubleshooting - **`ORA-12514: TNS:listener does not currently know of service`** — `service_name` in the `servers` block doesn't match the database service; check with `lsnrctl status` or your DBA. - **`DPY-3010: connections to this database server version are not supported`** — older Oracle versions need thick mode: install the Oracle Instant Client and set `DATACONTRACT_ORACLE_CLIENT_DIR`. - **`ORA-00942: table or view does not exist`** — the `schema` in the `servers` block must be the owning schema (in uppercase), and the user needs `SELECT` on the table. --- ## Postgres ## ![](/img/icons/postgres.svg) Postgres Go from an existing Postgres table to a tested data contract in about five minutes: import the schema straight from Postgres, then test the actual data against it. Works with Postgres and Postgres-compatible databases (e.g. RisingWave). ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[postgres]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate Create a `.env` file in your working directory (or export the variables): ```bash # .env DATACONTRACT_POSTGRES_USERNAME=postgres DATACONTRACT_POSTGRES_PASSWORD=mysecretpassword ``` The database user needs `USAGE` on the schema and `SELECT` on the tables. `import` and `test` use the same two variables. ### 3. Create a contract from your tables Import the table metadata directly from the Postgres catalog. This also generates a ready-to-test `servers` block: ```bash datacontract import postgres \ --source localhost \ --database postgres \ --schema public \ --table orders \ --output datacontract.yaml ``` `--source` is the host of your Postgres server. Add `--port` if it doesn't listen on the default `5432`, repeat `--table` for multiple tables, or omit it to import every table in the schema. `--schema` defaults to `public`. Only have a DDL file? `datacontract import sql --source orders.sql --dialect postgres` works too, but writes a `servers` block with placeholder values that you have to fill in by hand. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: postgres (type=postgres, host=localhost, port=5432, database=postgres, schema=public) ╭────────┬───────────────────────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼───────────────────────────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has physical type character varying(20) │ order_id │ │ │ ... │ │ │ │ ╰────────┴───────────────────────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 24 checks. Took 2.3 seconds. ``` :::tip[No database at hand?] The [Quickstart](../quickstart.md) tests a public demo contract against a hosted Postgres database — no setup required. ::: ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule to a schema in `datacontract.yaml`: ```yaml schema: - name: orders # ... quality: - type: sql description: No order has a negative total query: SELECT COUNT(*) FROM orders WHERE order_total < 0 mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options and the data type mappings: **[Postgres Reference](../reference/postgres.md)**. ### Troubleshooting - **`password authentication failed`** — check the two environment variables above; note that values from an already-set shell variable take precedence over `.env`. - **`relation does not exist`** — the user lacks `USAGE` on the schema or `SELECT` on the table; a missing grant reads as a missing relation. --- ## Amazon Redshift ## ![](/img/icons/redshift.svg) Amazon Redshift Go from an existing Redshift table to a tested data contract in about five minutes: import the schema straight from Redshift, then test the actual data against it. Works with provisioned clusters and Redshift Serverless — both are reached over the PostgreSQL wire protocol. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[redshift]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate The easiest way is IAM — sign in to AWS once and the CLI requests temporary database credentials itself, so no database password is stored anywhere and nothing else has to be configured: ```bash aws sso login # or any other way of getting an AWS session ``` Your AWS identity needs `redshift-serverless:GetCredentials` (Serverless) or `redshift:GetClusterCredentialsWithIAM` (provisioned cluster), and the resulting database user needs `USAGE` on the schema plus `SELECT` on the tables. Prefer a database user? Set the credentials directly and they are used instead: ```bash # .env DATACONTRACT_REDSHIFT_USERNAME=awsuser DATACONTRACT_REDSHIFT_PASSWORD=mysecretpassword ``` The CLI picks the method from what you configured: a password means a database login, otherwise it uses your AWS session. Either way, `import` and `test` use the same setup. For custom domains, VPC endpoints, and the full list of options, see the [Redshift Reference](../reference/redshift.md). ### 3. Create a contract from your tables Import the table metadata directly from the Redshift catalog. This also generates a ready-to-test `servers` block: ```bash datacontract import redshift \ --source my-workgroup.123456789012.us-east-1.redshift-serverless.amazonaws.com \ --database dev \ --schema analytics \ --table orders \ --output datacontract.yaml ``` `--source` is the endpoint host of your cluster or serverless workgroup (without the trailing `:5439/dev` that the console shows). Repeat `--table` for multiple tables, or omit it to import every table in the schema. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: redshift (type=redshift, host=my-workgroup..., database=dev, schema=analytics) ╭────────┬───────────────────────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼───────────────────────────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has physical type character varying(36) │ order_id │ │ │ ... │ │ │ │ ╰────────┴───────────────────────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 24 checks. Took 4.9 seconds. ``` ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule to a schema in `datacontract.yaml`: ```yaml schema: - name: orders # ... quality: - type: sql description: No order has a negative total query: SELECT COUNT(*) FROM orders WHERE order_total < 0 mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options and the data type mappings: **[Redshift Reference](../reference/redshift.md)**. ### Troubleshooting - **Connection timeout** — the cluster/workgroup must be reachable from your machine: check **Publicly accessible**, the VPC security group's inbound rule for port `5439`, and VPN routing. - **`password authentication failed`** — Redshift Serverless expects the namespace's admin credentials or a database user; an IAM user name is not a database user. Unset `DATACONTRACT_REDSHIFT_PASSWORD` to sign in with your AWS identity instead. - **`Could not obtain temporary Redshift credentials`** — the AWS identity may call the wrong region (set `DATACONTRACT_REDSHIFT_REGION`) or lack `GetCredentials` / `GetClusterCredentialsWithIAM` permission. - **`Using the login credential provider requires an additional dependency`** — your AWS profile uses the `aws login` flow, which needs `pip install "botocore[crt]"` in the same environment as the CLI. - **`relation does not exist`** — check the `schema` in the `servers` block and the user's `USAGE` grant on it. - **The import finds no tables** — `--schema` is case-sensitive and must match the schema as stored in the catalog; external schemas need the `SVV_EXTERNAL_*` permissions granted to your user. --- ## Amazon S3 Tables ## ![](/img/icons/s3.svg) Amazon S3 Tables Test an Amazon S3 Table directly through its Iceberg REST catalog. The CLI imports the table's schema, reads its data with PyIceberg, and runs schema and quality checks in DuckDB. :::note S3 Tables uses **`type: iceberg`**, not `type: s3`, and requires the **`iceberg` extra**. For CSV, JSON, Delta, or Parquet files in a regular S3 bucket, use the [Amazon S3 guide](./s3.md). ::: ### Before you start Have an existing table bucket, namespace, and populated Iceberg table. The examples use `sales.orders`; replace the region, account ID, bucket, namespace, and table with your values. This guide uses the [direct S3 Tables REST endpoint](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-integrating-open-source.html), which needs no Glue integration or Athena workgroup. Your AWS identity needs `s3tables:GetTableBucket` on the bucket and `s3tables:GetTableMetadataLocation` and `s3tables:GetTableData` on the table. Listing or creating resources requires additional permissions. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[iceberg]' ``` See [Installation](../installation.md) for other installation methods. ### 2. Sign in to AWS For an IAM Identity Center (SSO) profile configured in the AWS CLI: ```bash export AWS_PROFILE=my-playground-profile aws sso login --profile "$AWS_PROFILE" aws sts get-caller-identity ``` Check that the last command reports the intended account. The CLI uses the AWS credential chain for both the catalog and its data files. It detects the SigV4 signing service and region from the endpoint; no OAuth token or Iceberg client secret is needed. Explicit `DATACONTRACT_S3_*` credentials override the default chain. Temporary credentials also need `DATACONTRACT_S3_SESSION_TOKEN`; see the [authentication reference](../reference/iceberg.md#amazon-s3-tables). Credentials are not stored in the imported contract. ### 3. Import the table's contract ```bash datacontract import iceberg \ --catalog-url https://s3tables.eu-central-1.amazonaws.com/iceberg \ --warehouse arn:aws:s3tables:eu-central-1:123456789012:bucket/my-table-bucket \ --catalog s3tables --namespace sales --table orders \ --output datacontract.yaml ``` `--warehouse` takes the **table bucket ARN**, not a table ARN or an `s3://` path. The generated ODCS v3.2.0 contract contains the imported properties and this server: ```yaml servers: - server: production type: iceberg catalog: s3tables catalogUrl: https://s3tables.eu-central-1.amazonaws.com/iceberg warehouse: arn:aws:s3tables:eu-central-1:123456789012:bucket/my-table-bucket namespace: sales ``` `--catalog` is a local catalog label, not an AWS resource name; it defaults to `default`. Alternatively, pass `--table sales.orders` without `--namespace`. The importer preserves the qualified name in `physicalName`. All import options: [`datacontract import iceberg`](../commands/import/iceberg.md). ### 4. Test the live data ```bash datacontract lint datacontract.yaml datacontract test datacontract.yaml --server production ``` Lint validates the contract document; test connects to S3 Tables and checks the actual columns, logical types, required values, and other declared constraints. Imported lists, maps, and structs include nested type definitions. A passing test exits with code `0`; failed checks exit with code `1`. For a machine-readable report: ```bash datacontract test datacontract.yaml --server production \ --output test-results.json --output-format json ``` ### 5. Add a quality rule and prove it catches a violation Add this `quality` block to the `orders` schema object, alongside its existing `name` and `properties`: ```yaml quality: - type: sql description: Orders must not be empty query: SELECT count(*) FROM orders mustBeGreaterThan: 0 ``` Run `datacontract test datacontract.yaml` again. For a populated table, the rule passes. To verify the failure path without changing any data, temporarily replace `mustBeGreaterThan: 0` with `mustBe: 0`. The rule now fails and the command exits with code `1`. Restore the original rule afterward. SQL uses DuckDB's dialect and the schema object's logical `name`. If `name` is `purchases` and `physicalName` is `sales.orders`, write `FROM purchases` in the query. ### Limits and troubleshooting - **Full-table scan:** selected tables are read into memory before checks run. Use tables that fit in available memory. SQL quality rules and row filters do not reduce the Iceberg scan. - **Expired session:** sign in again and rerun. Data-file credentials are captured when the catalog opens and must stay valid for the scan. - **Access denied:** confirm the account, bucket ARN, and table permissions. Catalog access alone does not prove permission to read data files. - **Table not found:** check `namespace` and the table's `physicalName`. S3 Tables supports a single namespace level. - **Binary columns:** ODCS has no binary logical type. Imports retain `physicalType`; presence and applicable quality checks still run, but there is no logical type check for these columns. This is the direct S3 Tables path. The [Glue Iceberg REST endpoint](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-integrating-glue-endpoint.html) has a separate setup and authorization model. ### Run the CLI's AWS integration suite For contributors, a development checkout with the `dev` and `iceberg` extras includes an opt-in live test: ```bash AWS_PROFILE=my-playground-profile \ DATACONTRACT_TEST_S3_TABLES_WAREHOUSE=arn:aws:s3tables:eu-central-1:123456789012:bucket/my-table-bucket \ pytest -q tests/test_test_iceberg_s3tables.py ``` This **creates and writes** a three-row table in a unique namespace, exercises import/lint/test with passing and deliberately failing contracts, then deletes its table and namespace. Use a playground bucket in the signed-in account with create, read, write, and delete permissions. AWS request and storage charges apply. Interrupted runs can leave `datacontract_e2e_*` namespaces; inspect them before cleaning them up. The normal test suite skips these AWS tests. ### Reference - [Iceberg configuration and type handling](../reference/iceberg.md) - [`datacontract test` command](../commands/test.md) - [Run tests in CI/CD](../scheduling/github-actions.md) --- ## Amazon S3 ## ![](/img/icons/s3.svg) Amazon S3 Go from files in a bucket to a tested data contract in about five minutes. Works with S3 and any S3-compatible endpoint (e.g. MinIO), for data in CSV, JSON, Delta, or Parquet format. For tables in an Amazon S3 table bucket, use the dedicated [Amazon S3 Tables guide](./s3-tables.md) with `type: iceberg` instead. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[s3]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate The easiest way is to sign in to AWS once — the CLI picks the session up, so no key is stored anywhere: ```bash aws sso login # or any other way of getting an AWS session ``` Prefer static keys? Set them directly and they are used instead: ```bash # .env DATACONTRACT_S3_REGION=eu-central-1 DATACONTRACT_S3_ACCESS_KEY_ID=AKIAXV5Q5QABCDEFGH DATACONTRACT_S3_SECRET_ACCESS_KEY=93S7LRrJcqLaaaa/XXXXXXXXXXXXX ``` Public buckets need neither: with nothing configured the objects are read unsigned. ### 3. Create a contract from your files Import the schema straight from the bucket. This also generates a ready-to-test `servers` block: ```bash datacontract import s3 \ --source 's3://my-bucket/orders/*.json' \ --output datacontract.yaml ``` The format is taken from the file suffix; pass `--format` for Delta tables, which have none. Add `--endpoint-url` for an S3-compatible store such as MinIO. The import reads only the field names and types. Properties like `required` cannot be inferred from the files and need to be adjusted manually. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: production (type=s3, format=json, location=s3://my-bucket/orders/*.json) ╭────────┬─────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼─────────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has no missing values │ order_id │ │ │ ... │ │ │ │ ╰────────┴─────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 11 checks. Took 3.6 seconds. ``` ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule: ```yaml schema: - name: orders # ... quality: - type: sql description: No order has a negative total query: SELECT COUNT(*) FROM orders WHERE order_total < 0 mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options and the data type handling per file format: **[S3 Reference](../reference/s3.md)**. CSV files are read as UTF-8 unless the server declares an `encoding` (ODCS v3.2.0), such as `ISO-8859-1` or `UTF-16`. JSON files are always read as UTF-8. ### Troubleshooting - **`403 Forbidden` / `Access Denied`** — the key pair lacks `s3:GetObject`/`s3:ListBucket` on the location, or `DATACONTRACT_S3_REGION` doesn't match the bucket's region. - **`No files found that match the pattern`** — check the `location` glob; it matches object keys, not directories. - **MinIO / S3-compatible storage fails to connect** — set `endpointUrl` in the `servers` block; the CLI then switches to path-style addressing. --- ## Snowflake ## ![](/img/icons/snowflake.svg) Snowflake Go from an existing Snowflake table to a tested data contract in about five minutes: import the schema straight from Snowflake, then test the actual data against it. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[snowflake]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate Create a `.env` file in your working directory (or export the variables): ```bash # .env DATACONTRACT_SNOWFLAKE_USERNAME= DATACONTRACT_SNOWFLAKE_PASSWORD= DATACONTRACT_SNOWFLAKE_WAREHOUSE=COMPUTE_WH DATACONTRACT_SNOWFLAKE_ROLE= ``` If `DATACONTRACT_SNOWFLAKE_PASSWORD` is not set, the import falls back to browser-based SSO. For key-pair authentication and other modes, see the [Snowflake Reference](../reference/snowflake.md). ### 3. Create a contract from your tables Import the table metadata directly from Snowflake. This also generates a ready-to-test `servers` block: ```bash datacontract import snowflake \ --source - \ --database ORDER_DB \ --schema PUBLIC \ --output datacontract.yaml ``` `--source` is your [account identifier](https://docs.snowflake.com/en/user-guide/admin-account-identifier) in the `-` format. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: workspace (type=snowflake, host=..., port=443, database=ORDER_DB, schema=PUBLIC, account=...) ╭────────┬─────────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼─────────────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'ORDER_ID' is present │ ORDER_ID │ │ │ passed │ Check that field ORDER_ID has physical type VARCHAR │ ORDER_ID │ │ │ ... │ │ │ │ ╰────────┴─────────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 24 checks. Took 5.2 seconds. ``` ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule to a schema in `datacontract.yaml`: ```yaml schema: - name: orders # ... quality: - type: sql description: No order has a negative total query: SELECT COUNT(*) FROM orders WHERE order_total < 0 mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options (key-pair, SSO, connections.toml) and the data type mappings: **[Snowflake Reference](../reference/snowflake.md)**. ### Troubleshooting - **`250001: Could not connect to Snowflake backend`** — the account identifier is wrong. Use the `-` format, without `.snowflakecomputing.com`. - **`Object does not exist or not authorized`** — the role in `DATACONTRACT_SNOWFLAKE_ROLE` lacks `USAGE` on the database/schema or `SELECT` on the table. - **Test hangs waiting for MFA** — interactive MFA pushes don't work well for repeated runs; use key-pair auth or an appropriate `DATACONTRACT_SNOWFLAKE_AUTHENTICATOR`. --- ## Microsoft SQL Server ## ![](/img/icons/sqlserver.svg) Microsoft SQL Server Test data in MS SQL Server, including Azure SQL, Synapse Analytics SQL Pool, and Microsoft Fabric. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[sqlserver]' ``` The connection also requires the [Microsoft ODBC Driver 18 for SQL Server](https://learn.microsoft.com/en-us/sql/connect/odbc/download-odbc-driver-for-sql-server) on your machine. See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate Create a `.env` file in your working directory (or export the variables): ```bash # .env DATACONTRACT_SQLSERVER_USERNAME=sa DATACONTRACT_SQLSERVER_PASSWORD=mysecretpassword DATACONTRACT_SQLSERVER_TRUST_SERVER_CERTIFICATE=True # for local/dev servers ``` For Entra ID (Active Directory) authentication modes, see the [SQL Server Reference](../reference/sqlserver.md). ### 3. Create a contract from your tables Import the table metadata directly from the database. This also generates a ready-to-test `servers` block: ```bash datacontract import sqlserver \ --source localhost \ --database mydb \ --schema dbo \ --table orders \ --output datacontract.yaml ``` `--source` is the host of your SQL Server instance. Add `--port` if it doesn't listen on the default `1433`, repeat `--table` for multiple tables, or omit it to import every table. `--schema` defaults to `dbo`. Only have a DDL script? `datacontract import sql --source orders.sql --dialect sqlserver` works too, but writes a `servers` block with placeholder values that you have to fill in by hand. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: production (type=sqlserver, host=localhost, port=1433, database=mydb, schema=dbo) ╭────────┬─────────────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼─────────────────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has physical type varchar(36) │ order_id │ │ │ ... │ │ │ │ ╰────────┴─────────────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 24 checks. Took 3.7 seconds. ``` ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule to a schema in `datacontract.yaml`: ```yaml schema: - name: orders # ... quality: - type: sql description: No order has a negative total query: SELECT COUNT(*) FROM orders WHERE order_total < 0 mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options (SQL logins, Entra ID modes, `az login`) and the data type mappings: **[SQL Server Reference](../reference/sqlserver.md)**. ### Troubleshooting - **`Can't open lib 'ODBC Driver 18 for SQL Server'`** — the ODBC driver isn't installed, or its name doesn't match `driver` in the `servers` block / `DATACONTRACT_SQLSERVER_DRIVER`. - **`SSL Provider: certificate verify failed`** — for servers with self-signed certificates (local Docker, dev), set `DATACONTRACT_SQLSERVER_TRUST_SERVER_CERTIFICATE=True`. - **`Login failed for user`** — check the authentication mode: SQL logins need `DATACONTRACT_SQLSERVER_AUTHENTICATION=sql` (the default); Entra ID users need one of the `ActiveDirectory*` modes or `cli`. - **`Could not read model ''`** — the tables must be in the `schema` named in the `servers` block. Without it, the lookup falls back to the login's default schema, usually `dbo`. --- ## Trino ## ![](/img/icons/trino.svg) Trino Test data in Trino. ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[trino]' ``` See [Installation](../installation.md) for pip, pipx, and Docker. ### 2. Authenticate Create a `.env` file in your working directory (or export the variables): ```bash # .env DATACONTRACT_TRINO_USERNAME=trino DATACONTRACT_TRINO_PASSWORD=mysecretpassword ``` The default is `basic` auth, which connects over HTTPS whenever a password is set, so the server must serve TLS. JWT and OAuth2 are also supported — see the [Trino Reference](../reference/trino.md). ### 3. Create a contract from your tables Import the table metadata directly from the catalog. This also generates a ready-to-test `servers` block: ```bash datacontract import trino \ --source localhost \ --port 8443 \ --catalog my_catalog \ --schema my_schema \ --table orders \ --output datacontract.yaml ``` Repeat `--table` for multiple tables, or omit it to import every table in the schema. Only have a DDL script? `datacontract import sql --source orders.sql --dialect postgres` works too — Trino's ANSI-style DDL generally parses with that dialect — but writes a `servers` block with placeholder values that you have to fill in by hand. ### 4. Test the actual data ```bash datacontract test datacontract.yaml ``` ``` Testing datacontract.yaml Server: trino (type=trino, host=localhost, port=8443, schema=my_schema, catalog=my_catalog) ╭────────┬─────────────────────────────────────────────────────┬──────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼─────────────────────────────────────────────────────┼──────────┼─────────┤ │ ... │ │ │ │ │ passed │ Check that field 'order_id' is present │ order_id │ │ │ passed │ Check that field order_id has physical type varchar │ order_id │ │ │ ... │ │ │ │ ╰────────┴─────────────────────────────────────────────────────┴──────────┴─────────╯ 🟢 Data contract is valid. Ran 24 checks. Took 1.9 seconds. ``` ### 5. Let it catch a violation The contract becomes valuable when it detects drift. Tighten an expectation — for example, add a quality rule to a schema in `datacontract.yaml`: ```yaml schema: - name: orders # ... quality: - type: sql description: No order has a negative total query: SELECT COUNT(*) FROM orders WHERE order_total < 0 mustBe: 0 ``` Run `datacontract test datacontract.yaml` again: every violation is listed as an error, and the command exits with code `1` — ready for [CI/CD and scheduled runs](../scheduling/index.md) so you catch drift before your consumers do. ### Reference All authentication options (basic, JWT, OAuth2) and the data type handling: **[Trino Reference](../reference/trino.md)**. ### Troubleshooting - **`401 Unauthorized`** — check the auth mode: password-protected clusters need `basic` credentials over HTTPS; token-based setups need `DATACONTRACT_TRINO_AUTHENTICATION=jwt` and `DATACONTRACT_TRINO_JWT_TOKEN`. - **`Catalog ... does not exist` / `Schema ... does not exist`** — `catalog` and `schema` in the `servers` block must match `SHOW CATALOGS` / `SHOW SCHEMAS FROM `. --- ## XML files ## ![](/img/icons/custom.svg) XML files Test XML documents against a data contract: every record is checked for required values, types, enumerations, patterns, ranges, and your quality SQL, the same as rows of a table. The quickest contract comes from the XML Schema (XSD) the documents follow. The files of this walkthrough are in [`examples/testing/xml`](https://github.com/datacontract/datacontract-cli/tree/main/examples/testing/xml). ### 1. Install ```bash uv tool install --python python3.11 --upgrade 'datacontract-cli[duckdb,xml]' ``` `duckdb` reads the documents, `xml` imports the XML Schema. The CLI reads XML through the [webbed](https://duckdb.org/community_extensions/extensions/webbed) DuckDB community extension, which it downloads on the first test run, so that run needs network access. ### 2. Create a contract from the XML Schema Given this `orders.xsd`, with one `order` per document: ```xml ``` Then import it: ```bash datacontract import xsd --source orders.xsd --output orders.odcs.yaml ``` and point a server at the documents, here `orders/order-1001.xml`, `orders/order-1002.xml`, and so on: ```yaml servers: - server: local type: local path: orders/*.xml format: xml ``` ### 3. Test the documents ```bash datacontract test orders.odcs.yaml ``` ``` Testing orders.odcs.yaml Server: local (type=local, format=xml, path=orders/*.xml) ╭────────┬──────────────────────────────────────────────────┬───────────┬─────────╮ │ Result │ Check │ Field │ Details │ ├────────┼──────────────────────────────────────────────────┼───────────┼─────────┤ │ passed │ Check that the documents have order elements │ │ │ │ passed │ Check that field 'line_item' is present │ line_item │ │ │ passed │ Check that field line_item has no missing values │ line_item │ │ │ ... │ │ │ │ │ passed │ Check that field version has no missing values │ version │ │ │ passed │ Check that field version has a minimum of 1 │ version │ │ ╰────────┴──────────────────────────────────────────────────┴───────────┴─────────╯ 🟢 Data contract is valid. Ran 16 checks. Took 0.35 seconds. ``` ### 4. Let it catch a violation Add a document with an unknown status, no `version`, and a price without its currency: ```xml ORD-1003 2026-09-03T12:00:00 cancelled ABC-1 49.95 ``` ``` 🔴 Data contract is invalid, found the following errors: 1) status Check that field status only contains enum values ['pending', 'shipped']: Actual invalid_count(status) was 1, expected = 0 2) line_item[].price.currency Check that field line_item[].price.currency has no missing values: Actual missing_count(line_item[].price.currency) was 1, expected = 0 3) version Check that field version has no missing values: Actual missing_count(version) was 1, expected = 0 ``` The command exits with code `1`, so the same call works as a gate in [CI/CD pipelines](../scheduling/index.md). ### How documents become records - **Records.** Every element named like the schema's `physicalName` (or its `name`, when there is none) is one record, wherever it is in the document: the root of a file with one record, or repeated inside a wrapper element such as `……`. All files the `path` matches are read together. - **Properties.** Child elements and attributes are both properties of the record. A child element with children of its own is an `object`, and an element that repeats is an `array`; checks on their fields read `line_item[].sku`. - **Text with attributes.** The text of an element that also has attributes, such as `49.95`, is read into the property with the custom property `xmlNode: text`. `datacontract import xsd` adds it as `value`; in a contract written by hand, add it yourself: ```yaml - name: price logicalType: object properties: - name: value logicalType: number customProperties: - property: xmlNode value: text - name: currency logicalType: string ``` - **Namespaces.** Elements are matched by their local name, so `` is an `order` record. - **Records.** A check fails when the documents contain no element named like the record element at all, so a misspelled `physicalName` never passes. - **Presence.** An element or attribute that is not required may be absent from every document, so only the required properties of the record element are checked for presence. Required values further down are checked for missing values. - **Attributes named like an element.** The import names such an attribute with an `@` prefix (`@id`), and the test reads it under that name. When an element and an attribute of the same element share a name, DuckDB's XML reader reads only the attribute, so the element's checks cannot run, with a warning. - **Large files.** Files of any size are read, each one whole, so a file needs about its size in memory. - **Types.** The documents are read as the contract's `logicalType`s, like [CSV files](../reference/local.md#data-types); `physicalType` is not checked. ### Reference No environment variables are needed. The `path` supports glob patterns and a `{model}` placeholder, the same as for [local files](./local.md). The XSD import and its mapping: **[Import: XML Schema](../imports/xsd.md)**. To validate documents against the contract with XML tools instead, export it with **[`datacontract export xsd`](../exports/xsd.md)**. ### Troubleshooting - **`Failed to install the 'webbed' DuckDB community extension`** — the first run downloads the extension. Run it once with network access; later runs use the installed copy. - **`Check that the documents have … elements` fails** — no element in the documents is named like the schema's `physicalName`. Set `physicalName` to the element of one record, for example `order` rather than the wrapper `orders`. - **`value '…' does not match column type …`** — a value cannot be read as its property's `logicalType`, such as `many` for an `integer`. The checks that need the value report the read error. - **The text of an element is always missing** — the property for it needs the custom property `xmlNode: text` (see above). --- ## Define your Schema The `schema` block describes the structure of the data: the schemas (tables, views, topics, files) and their properties (columns, fields). Many of its attributes are **automatically verified** by [`datacontract test`](./testing/index.md) — you declare them once and they become executable checks on every run, on every backend. This page covers only those attributes. Everything else in the `schema` block is documentation, carried into exports but never asserted against the data. ```bash datacontract test --checks properties datacontract.yaml ``` ### What generates a check | Attribute | Level | Check | |---|---|---| | *(any property)* | property | The column exists in the data source | | `physicalType` | property | The column's native type matches (catalog backends only) | | `logicalType` | property | The column's type matches, normalized to an ODCS category | | `required` | property | No missing values | | `unique` | property | No duplicate values | | `primaryKey` | property | No missing values **and** no duplicates | | `relationships` (`type: foreignKey`) | property, schema | Every non-null key exists in the referenced schema | | `logicalTypeOptions.minLength` / `maxLength` | property | Value length within bounds | | `logicalTypeOptions.minimum` / `maximum` | property | Value within bounds (inclusive) | | `logicalTypeOptions.exclusiveMinimum` / `exclusiveMaximum` | property | Value within bounds (exclusive) | | `logicalTypeOptions.pattern` | property | Value matches the regular expression | | `enum` | property | Value is one of the listed values (ODCS v3.2.0; `logicalTypeOptions.enum` is still accepted) | | `logicalTypeOptions.minItems` / `maxItems` | array property | Number of elements within bounds | | `logicalTypeOptions.uniqueItems` | array property | Elements of the array are distinct | | `quality` | schema, property | See [Define your Quality Rules](./quality-rules/index.md) | A contract that uses all of them: ```yaml schema: - name: orders physicalName: orders_v2 properties: - name: order_id logicalType: string physicalType: varchar(36) primaryKey: true - name: order_status logicalType: string required: true enum: - value: pending - value: shipped label: Shipped - value: delivered label: Delivered description: Handed over to the customer - name: order_total logicalType: integer physicalType: integer logicalTypeOptions: minimum: 0 maximum: 1000000 - name: customer_email logicalType: string logicalTypeOptions: maxLength: 255 pattern: '^[^@]+@[^@]+$' - name: tags logicalType: array items: logicalType: string logicalTypeOptions: minItems: 1 maxItems: 10 uniqueItems: true ``` ### Presence and naming Every property produces a **presence check** — the column must exist in the data source. This is the one check you always get, even for a property that declares nothing but a name. The exception are [XML files](./testing/xml.md#how-documents-become-records), where an element or attribute that is not required may be absent from every document: there only the required properties of the record element are checked for presence. `physicalName` selects the real object in the data source; `name` is the logical name used in the contract. When `physicalName` is set, the checks run against it: ```yaml schema: - name: orders # logical name physicalName: orders_v2 # the real table properties: - name: total physicalName: order_total # the real column ``` ### Types A property can declare a portable `logicalType`, a native `physicalType`, or both. Which one is checked depends on the backend: - **`physicalType`** is compared against the column's real declared type read from the platform catalog. This applies on the backends with catalog introspection: Snowflake, BigQuery, Databricks, Postgres, Redshift, SQL Server, Oracle, Trino, Athena, Exasol, ClickHouse, and Hive. It takes precedence over `logicalType`. - **`logicalType`** is used everywhere else, and as the fallback when the native type cannot be read. Both the declared and the actual type are normalized to an ODCS category before comparison, so `integer` and `number` are mutually compatible. Nested properties are checked like top-level ones: each property under `properties`, the `items` of an array, and the `key` and `value` of a map get their own type check at their path, such as `customer.email`, `items[].sku`, `matrix[][]` or `attributes[value]`. A nested `physicalType` is compared against the native type the catalog reports for it on Athena, Trino, Databricks, BigQuery, ClickHouse (except inside a `Tuple`), Hive and Snowflake, and reported as a warning on the other backends with catalog introspection. ODCS v3.2.0 `logicalType: vector` requires `logicalTypeOptions.dimensions` to be a positive integer. Lint rejects a missing options block as well as missing or invalid dimensions, including in nested definitions. Property-level `enum` entries must have distinct values, even if their labels, IDs, or descriptions differ. Lint checks this throughout properties, array items, and map keys and values. JSON numbers compare by value (`1` equals `1.0`); strings and booleans remain distinct from numbers. An `enum` constrains only values that are present, whether a value may be missing is up to `required`. Therefore, `null` should not be part of an `enum`. Vector type checks compare dimensions and element types when the data source reports them. `logicalTypeOptions.elementType` defaults to `float32`; a column reported as `float64` or `int8` does not satisfy that declaration. Catalogs that expose only a numeric array without its element width cannot confirm an element-type mismatch. :::note For file servers with `format: csv`, `json`, `xml`, `avro` or `parquet` **no type check is generated at all** — the file is read *as* the contract's types, so a mismatch surfaces as a read error instead. `format: delta` is read with the table's own types, so its types, nested ones included, are checked. `format: json` is additionally validated against a JSON Schema derived from the contract. See [Data Source Reference](./reference/index.md#how-data-types-work) for the full type-mapping rules. ::: ### Required, unique, and primary keys ```yaml properties: - name: order_id primaryKey: true - name: external_ref unique: true - name: order_status required: true ``` - **`required: true`** → no missing values. - **`unique: true`** → no duplicate values (`NULL` values are not duplicates of each other). - **`primaryKey: true`** → both of the above. Declaring `required` or `unique` alongside it does not duplicate the check. A **composite primary key** — several properties with `primaryKey: true` — is treated as a key over the tuple, not column by column. Each member gets its own not-null check, and the combination gets a single uniqueness check. Use `primaryKeyPosition` to order the members: ```yaml properties: - name: order_id primaryKey: true primaryKeyPosition: 1 - name: line_number primaryKey: true primaryKeyPosition: 2 ``` > Produces: `order_id` not null, `line_number` not null, and `(order_id, line_number)` unique. ### Foreign keys A `relationships` entry of `type: foreignKey` checks that every non-null value of the referencing column exists in the referenced one. Declare it on the property, with `to`, or on the schema, with `from` and `to` lists for a composite key: ```yaml schema: - name: line_items properties: - name: order_id relationships: - type: foreignKey to: orders.order_id - name: shipments relationships: - type: foreignKey from: [shipments.order_id, shipments.line_no] to: [order_lines.order_id, order_lines.line_no] ``` The referenced schema must be read in the same run. When `--schema-name` selects only the referencing schema of a file server, the check is reported as a warning. ### Value constraints `logicalTypeOptions` turns into value checks that behave identically on every backend — the CLI compiles each into dialect-specific SQL. ```yaml properties: - name: order_total logicalType: integer logicalTypeOptions: minimum: 0 maximum: 1000000 - name: country_code logicalType: string logicalTypeOptions: minLength: 2 maxLength: 2 pattern: '^[A-Z]{2}$' - name: order_status logicalType: string enum: - value: pending - value: shipped - value: delivered - name: tags logicalType: array items: logicalType: string logicalTypeOptions: minItems: 1 maxItems: 10 uniqueItems: true ``` | Option | Fails when a value is… | |---|---| | `minLength` / `maxLength` | shorter / longer than the bound | | `minimum` / `maximum` | below / above the bound (the bound itself passes) | | `exclusiveMinimum` / `exclusiveMaximum` | below / above **or equal to** the bound | | `pattern` | not matching the regular expression | | `enum` (property level, or `logicalTypeOptions.enum`) | not one of the listed values | | `minItems` / `maxItems` | an array with fewer / more elements than the bound | | `uniqueItems` | an array that repeats an element | `exclusiveMinimum` and `exclusiveMaximum` each produce two checks — a bound check and an inequality check — so a violation of either is reported separately. The value options also apply to the `items` of an array of plain values, such as a `pattern` on every tag or a `minimum` on every score. The check is named after the items, `tags[]`, and counts the rows with at least one item that breaks it; `unique` on the items fails a row that repeats an item. Items inside an array of objects are reached the same way, as in `orders[].codes[]`. ### Descriptive metadata ODCS v3.2.0 adds fields that describe a schema object or property for people and tools without generating a check: `semanticType` (`column`, `measure`, `dimension`), `synonyms`, `deprecated`, and a `context` block with `instructions`, `verifiedStatements` and `constraints` for AI agents and semantic layers. The [HTML](./exports/html.md) and [Markdown](./exports/markdown.md) exports render them, [`changelog`](./commands/changelog.md) reports changes to them by their natural key (a synonym's `synonym` or `id`, an enum entry's `value` or `id`), and `export odcs` keeps them. The DCS export drops them, as the Data Contract Specification has no equivalent. ### What is not checked These are common sources of confusion. They are valid ODCS and appear in exports, but they generate no check: - **`isNullable`** — the CLI reads `required`, not `isNullable`. Write `required: true` to assert that a column has no nulls. - **`logicalTypeOptions.format`** — never enforced, on any property. On a `string` property (`email`, `uuid`, `uri`, …) use `pattern` for an enforceable equivalent. On a `date`, `timestamp` or `time` property `format` holds a date pattern such as `yyyy-MM-dd`, and `pattern` is *not* an equivalent there: by the time a check runs, the column has already been parsed as a date, so there is no string left to match. Such a column is validated as a date, in whatever format the server stores it. - **Descriptive attributes** — `description`, `businessName`, `examples`, `tags`, `classification`, `criticalDataElement`, `transformSourceObjects`, and `customProperties`. `authoritativeDefinitions` generates no check either, but it *is* resolved and inlined before the checks are built — see [Link your Semantics](./semantics.md). - **Schema-level attributes** other than `name`, `physicalName`, `properties`, and `quality`. - **Constraints and quality rules on nested properties, on some servers** — `required`, `unique`, `pattern`, `enum`, the other `logicalTypeOptions` and `quality` rules below the top level are checked on the `dataframe`, `databricks`, `trino` and `athena` servers and on every server read through DuckDB (`local`, `s3`, `gcs`, `azure`, `duckdb`, `iceberg`, `kafka`). On every other server, nested properties are only type-checked, and each of their constraints and quality rules is reported as a warning. On SAP HANA, only their quality rules are reported as warnings; their constraints are not checked. Anything beyond this list that you want verified belongs in a [quality rule](./quality-rules/index.md) — a `type: library` metric for the common cases, or `type: sql` for arbitrary expressions. ### Next steps - Add rules that go beyond structure: **[Define your Quality Rules](./quality-rules/index.md)**. - Declare freshness and retention promises: **[Define your Service Levels](./service-levels.md)**. - Link a property to a shared business term instead of repeating it: **[Link your Semantics](./semantics.md)**. - Generate a schema block from an existing table instead of writing it by hand: **[Imports](./imports/index.md)**. --- ## Compare contract versions Use `datacontract changelog` to describe the differences between two versions of a contract, or use `datacontract breaking` to classify their compatibility impact. ### Changelog Use `datacontract changelog` to compare the source contract (`v1`) with the target contract (`v2`) and report the changes between them: ```bash datacontract changelog v1.odcs.yaml v2.odcs.yaml ``` See the generated [changelog command reference](./commands/changelog.md) for all options. ### Breaking changes Use `datacontract breaking` when a contract change must be checked for compatibility. The command compares the source contract (`v1`) with the target contract (`v2`) and preserves the detailed changelog while adding a severity classification. ```bash datacontract breaking v1.odcs.yaml v2.odcs.yaml ``` See the generated [breaking command reference](./commands/breaking.md) for all options. #### Perspectives Whether a change breaks depends on whose expectations it must keep. `--perspective` selects one of two: - **`consumer`** (default) - "If we change the data in this way, will the expectations of data users break?" - **`producer`** - "If we change the contract in this way, will the next contract test fail?" ```bash datacontract breaking --perspective producer v1.odcs.yaml v2.odcs.yaml ``` #### Severity levels - **ERROR** - a breaking change from the selected perspective. The command exits with status `1`. - **WARNING** - a change that may break and requires review. The command exits with status `0`. - **INFO** - a change that does not break from the selected perspective. The command exits with status `0`. The result is breaking only when at least one detailed entry has severity `ERROR`. #### API The same result is available from `POST /breaking`, using the same JSON request shape as `POST /changelog` and an optional `perspective` query parameter: ```json { "v1": "", "v2": "" } ``` The response includes `summary`, `entries`, the `perspective` it was graded from, and an `is_breaking` boolean. The endpoint returns HTTP `200` for a valid comparison even when `is_breaking` is `true`; invalid YAML or contracts return HTTP `422`. --- ## Define your Quality Rules Beyond the [schema checks](../schema.md) (presence, type, `required`, `unique`, primary keys, value ranges), a data contract can declare **quality rules**. When you run [`datacontract test`](../testing/index.md), executable rules run against the data source and are reported alongside the schema checks. Quality rules use the ODCS `quality` attribute, which can be attached either to a **schema** (table/object level) or to an individual **property** (column/field level): ```yaml schema: - name: orders quality: # schema-level rule - type: library metric: rowCount mustBeGreaterThan: 0 properties: - name: order_total quality: # property-level rule - type: library metric: nullValues mustBe: 0 ``` ### Quality rule types ODCS defines four `type`s of quality rule. Each has its own page: ![](/img/icons/database.svg) SQLRun a custom SQL query and compare the result to a threshold. ![](/img/icons/custom.svg) LibraryPredefined, portable checks such as rowCount, nullValues, and duplicateValues. ![](/img/icons/custom.svg) TextA human-readable expectation, for documentation only. ![](/img/icons/database.svg) CustomEngine-specific checks (e.g. DQX, SodaCL) and reusable custom quality checks. ### Quality dimensions Any rule can be classified with a `dimension` — the aspect of data quality it protects. It is optional and works with every rule type. It documents what the contract's quality coverage adds up to, and `datacontract test --dimension` runs one aspect at a time. ```yaml schema: - name: orders quality: - type: library metric: rowCount mustBeGreaterThan: 0 dimension: completeness properties: - name: order_id quality: - type: library metric: duplicateValues mustBe: 0 dimension: uniqueness ``` ODCS defines seven dimensions. `datacontract lint` rejects any other value: | Dimension | The data… | |---|---| | `accuracy` | correctly describes the real-world entity or event it represents | | `completeness` | contains all the records and values that are expected | | `conformity` | follows the agreed format, type, and set of allowed values | | `consistency` | agrees with itself and with related data elsewhere | | `coverage` | includes the full population it claims to describe | | `timeliness` | is available and current when it is needed | | `uniqueness` | contains no unintended duplicates | The [HTML export](../exports/html.md) renders the dimension as a badge next to schema-level rules. ### Identifying rules Any rule can carry an `id` and a list of `tags`. Both are optional and work with every rule type. They name a rule so that a pipeline can run it on its own: an `id` addresses exactly one rule, `tags` group rules that belong together — by cost, criticality, or the pipeline step they guard. ```yaml schema: - name: orders quality: - id: orders_not_empty type: library metric: rowCount mustBeGreaterThan: 0 tags: ["critical", "cheap"] properties: - name: order_id quality: - id: order_id_is_unique type: library metric: duplicateValues mustBe: 0 tags: ["critical", "expensive"] ``` An `id` must be unique within the contract, so that `--quality-id` selects a single rule. Test results report the `id` and `tags` of the rule each check comes from. ### Running only quality checks Use `--checks` to restrict a run to quality rules: ```bash datacontract test --checks quality datacontract.yaml ``` Use `--dimension` to run one aspect of quality across the whole contract: ```bash datacontract test --dimension uniqueness datacontract.yaml datacontract test --dimension completeness,accuracy datacontract.yaml ``` This selects quality rules by their declared `dimension` **and** the built-in checks that measure the same thing — `--dimension uniqueness` runs your `duplicateValues` rules alongside the `unique` and primary-key checks derived from the schema. Quality rules that declare no `dimension` are never selected: only the author can say what a custom rule measures. If nothing matches, the run executes nothing and reports no checks. Use `--quality-id` to run a single rule, and `--tag` to run a group of them: ```bash datacontract test --quality-id order_id_is_unique datacontract.yaml datacontract test --tag critical datacontract.yaml datacontract test --tag critical,cheap datacontract.yaml ``` Both select quality rules only — no schema or service level checks run alongside them. This makes it possible to spread the rules of one contract over a data pipeline: run the cheap rules on ingest, the expensive ones after the nightly load, without splitting the contract into several files. `--quality-id` matches the `id` of the rule, and fails the run if no rule in the contract declares it — a mistyped id must not pass by testing nothing. `--tag` matches the `tags` of the rule (not the `tags` of a schema or property), and reports no checks when nothing matches. Options combine: `--tag critical --checks quality --server production` runs the critical rules on production. #### Dimensions of the built-in checks [Schema checks](../schema.md) and [service levels](../service-levels.md) have no `dimension` field to set, so the CLI assigns each the dimension it measures: | Dimension | Built-in checks | |---|---| | `completeness` | `required` fields, primary key not-null | | `uniqueness` | `unique` fields, primary key uniqueness (including composite keys) | | `conformity` | field presence, logical and physical types, nested types, `pattern`, `enum`, length and value bounds, JSON Schema validation, `slaProperties` retention | | `consistency` | `relationships` foreign keys | | `timeliness` | `slaProperties` freshness | ### Where quality rules are used Executable rules (`sql`, `library`) run during `test`. All rule types are also translated when you export to a quality engine: - **[dbt](../dbt.md)** — rules become native dbt tests, with `dbt_utils` tests and singular SQL tests for rules that cannot be expressed natively. - **[SodaCL](../exports/sodacl.md)** — rules become Soda checks. - **[Great Expectations](../exports/great-expectations.md)** — rules become expectations in a suite. - **[DQX](../exports/dqx.md)** — rules become Databricks DQX checks. ### Inspecting failing rows When a quality (or schema) check fails, add `--include-failed-samples` to `test` to collect a small sample of the offending rows (identifier + offending columns; sensitive columns are omitted). This is off by default. ```bash datacontract test --include-failed-samples datacontract.yaml ``` ### Learn more - The full quality syntax is part of the [Open Data Contract Standard](https://bitol-io.github.io/open-data-contract-standard/latest/). - Promises about *when* the data arrives and how long it is kept are not quality rules — see [Define your Service Levels](../service-levels.md). --- ## Custom Quality Checks A custom quality check is a SQL check defined **once**, outside any contract, and used **by its ID** from as many contracts as need it. The contract names the check and supplies its arguments; the check holds the query. ```yaml schema: - name: orders properties: - name: amount logicalType: number quality: - type: custom engine: datacontract-cli implementation: check: between arguments: min: 0 max: 10000 ``` Point `datacontract test` at the folder the checks live in: ```bash datacontract test datacontract.yaml --custom-quality-checks ./custom-quality-checks ``` or set `DATACONTRACT_CUSTOM_QUALITY_CHECKS`. See the [`test` command reference](../commands/test.md). ### Defining a check The folder holds one YAML file per check. The file name is the check's ID (`between.yaml` defines `between`). IDs are unique and must not contain whitespace or any of `/ \ % ; # ?`. ```yaml # custom-quality-checks/between.yaml name: Value range description: All ${column} values are between ${arguments.min} and ${arguments.max}. owner: data-platform dimension: conformity arguments: min: max: queries: default: | SELECT COUNT(*) FROM ${table} WHERE ${column} < ${arguments.min} OR ${column} > ${arguments.max} mustBe: 0 ``` | Key | Meaning | |---|---| | `name` | A short display name (optional, only used when exporting to Entropy Data). | | `description` | What the check verifies. Becomes the check's name in the results, with the placeholders and arguments filled in, unless the rule has a `description` of its own. | | `owner` | Who maintains the check. Documentation only. | | `dimension` | The [quality dimension](./index.md#quality-dimensions) the check measures. A `dimension` on the rule takes precedence. | | `arguments` | The arguments the check takes, each with an optional `type` and `default`. See [Arguments](#arguments). | | `queries` | At least one query, keyed by SQL dialect or `default`. | | `mustBe`, `mustBeGreaterThan`, … | The default expected result, one of the [SQL rule comparators](./sql.md#comparators). | ### Arguments A check declares its arguments under `arguments`, and a rule passes their values under `implementation.arguments`. `${arguments.}` in the check definition gets replaced by the respective value: ```yaml # custom-quality-checks/recent_rows.yaml arguments: timestamp_column: {type: identifier} days: {type: number, default: 1} queries: default: | SELECT COUNT(*) FROM ${table} WHERE ${arguments.timestamp_column} >= CURRENT_DATE - ${arguments.days} * INTERVAL '1' DAY mustBeGreaterThan: 0 ``` ```yaml # datacontract.yaml implementation: check: recent_rows arguments: timestamp_column: created_at days: 7 ``` The `type` in the check definition specifies how arguments are parsed: | Type | Becomes | Example | |---|---|---| | `any` (default) | An SQL literal; a list becomes comma-separated literals, as in `IN (...)` | `EUR` → `'EUR'`, `[A, B]` → `'A', 'B'` | | `number` | A number literal | `"7"` → `7` | | `identifier` | A column or table name, quoted where needed like the [placeholders](./sql.md#placeholders) | `order date` → `"order date"` | Values are never inserted as raw SQL. Inside a single-quoted string, an `any` or `number` argument is inserted as escaped text, as in `LIKE '%${arguments.text}%'`. ### Placeholders The queries use the same placeholders as [SQL rules](./sql.md#placeholders) — `${table}`, `${column}`, `${schema}`, and so on. A check whose queries use `${column}` (or `${field}`, `${property}`) can only be declared on a property. As in placeholders, the `$` in an argument reference is optional: `{arguments.min}` works the same as `${arguments.min}`. ### Dialects A run uses the query for the [SQL dialect of the server](./sql.md#sql-dialect), and otherwise the `default` query (if it exists): ```yaml queries: default: | SELECT COUNT(*) FROM ${table} WHERE ${arguments.timestamp_column} >= CURRENT_DATE - ${arguments.days} * INTERVAL '1' DAY tsql: | SELECT COUNT(*) FROM ${table} WHERE ${arguments.timestamp_column} >= DATEADD(day, -${arguments.days}, CAST(GETDATE() AS date)) ``` The keys are the dialect names in the [SQL dialect table](./sql.md#sql-dialect), so a check for a `mysql` server needs a `duckdb` query. Write the `default` query in ANSI SQL. Note that on SAP HANA, only the `default` query runs. ### Overriding the expected result The check's expected result is a default. A comparator on the rule replaces it: ```yaml quality: - type: custom engine: datacontract-cli implementation: check: recent_rows arguments: timestamp_column: created_at mustBeGreaterThan: 10000 ``` --- ## Custom Quality Rules A `type: custom` rule carries a check written in the **native syntax of a specific quality engine**. You name the engine with `engine` and provide the engine-specific definition under `implementation`. Custom rules let you use capabilities of an engine that the portable [library](./library.md) and [SQL](./sql.md) rules don't cover. Custom rules are consumed by the matching engine — typically via the corresponding [exporter](../exports/index.md) — rather than being compiled to portable SQL. :::note `datacontract test` does not run custom quality rules except for those with `engine: datacontract-cli`. Consider `type: sql` as an executed alternative. ::: ### Custom quality checks With `engine: datacontract-cli`, a custom rule uses a [custom quality check](./custom-quality-checks.md): a parameterised SQL check defined once in a folder and run by `datacontract test`. ```yaml quality: - type: custom engine: datacontract-cli implementation: check: between arguments: min: 0 max: 10000 ``` ### Databricks DQX ```yaml schema: - name: events properties: - name: event_type logicalType: string quality: - type: custom engine: dqx implementation: criticality: error check: function: is_in_list arguments: allowed: - click - view - purchase ``` Export these to a DQX check file with [`datacontract export dqx`](../exports/dqx.md). ### SodaCL ```yaml schema: - name: orders quality: - type: custom engine: soda implementation: | checks for orders: - row_count > 10 ``` Export to a SodaCL file with [`datacontract export sodacl`](../exports/sodacl.md). :::warning Raw SodaCL custom checks (`type: custom`, `engine: soda`) are **no longer executed** by `datacontract test` since `soda-core` was removed; such a rule is reported as a warning. Migrate it to a [SQL rule](./sql.md) (`type: sql`) to keep it executable, or keep it as a custom rule purely for the SodaCL export. ::: ### Other engines The same pattern applies to other engines (for example Great Expectations). Provide `engine` and an `implementation` understood by that engine, and use the matching [exporter](../exports/index.md) to generate its native artifact. --- ## Library Quality Rules A `type: library` rule is a **predefined, portable check** selected with a `metric` name. Unlike [SQL rules](./sql.md), you don't write a query — the CLI compiles the metric into dialect-specific SQL for the selected server, so the same rule works across all backends. ```yaml quality: - type: library metric: nullValues mustBe: 0 ``` Each rule combines a **metric**, optional **arguments**, and exactly one **comparator** (`mustBe`, `mustBeBetween`, …). ### Supported metrics | Metric | Level | Measures | Arguments | |---|---|---|---| | `rowCount` | schema | Number of rows in the table | — | | `duplicateValues` | schema or property | Number of duplicate values | schema-level: `properties` (list of columns to check together) | | `nullValues` | property | Number of `NULL` values | — | | `missingValues` | property | Number of missing values (`NULL` plus the values you treat as missing) | `missingValues` (list of values counted as missing, e.g. `['', 'N/A']`) | | `invalidValues` | property | Number of values **not** in an allow-list | `validValues` (list of permitted values) | :::note `nullValues`, `missingValues`, and `invalidValues` are only supported at the **property** level. `rowCount` is **schema** level. `duplicateValues` works at either level. ::: ### Examples **Row count** (schema level): ```yaml schema: - name: orders quality: - type: library metric: rowCount mustBeGreaterThan: 0 ``` **No nulls** in a property: ```yaml quality: - type: library metric: nullValues mustBe: 0 ``` **Missing values** — treat empty string and `N/A` as missing, in addition to `NULL`: ```yaml quality: - type: library metric: missingValues arguments: missingValues: [null, '', 'N/A'] mustBe: 0 ``` **Invalid values** — the value must be one of an allow-list: ```yaml quality: - type: library metric: invalidValues arguments: validValues: ['CX-263-DU', 'IK-894-MN', 'ER-399-JY'] mustBe: 0 ``` **Duplicate values** across a set of columns (schema level): ```yaml schema: - name: orders quality: - type: library metric: duplicateValues arguments: properties: [order_id, line_number] mustBe: 0 ``` ### Comparators Use exactly one comparator to define the threshold: | Comparator | Passes when the metric… | |---|---| | `mustBe` | equals the value | | `mustNotBe` | does not equal the value | | `mustBeGreaterThan` / `mustBeGreaterOrEqualTo` | is above a lower bound | | `mustBeLessThan` / `mustBeLessOrEqualTo` | is below an upper bound | | `mustBeBetween` / `mustNotBeBetween` | is inside / outside a `[min, max]` range | ### Percentage thresholds For the count-of-bad-rows metrics (`nullValues`, `missingValues`, `invalidValues`), set `unit: percent` to compare against a percentage of rows instead of an absolute count: ```yaml quality: - type: library metric: nullValues unit: percent mustBeLessThan: 1 # fewer than 1% of rows are null ``` `unit: percent` is ignored (with a warning) for metrics that are not count-of-bad-rows. --- ## SQL Quality Rules A `type: sql` rule runs a custom SQL query against the server and compares the single returned value to a threshold. It is the most flexible rule type — use it whenever a check can be expressed as a query. The query must be read-only, and is written in the [dialect of the selected server](#sql-dialect). ### Property-level example ```yaml schema: - name: orders properties: - name: order_total logicalType: integer physicalType: bigint required: true quality: - type: sql description: 95% of all order total values are expected to be between 10 and 499 EUR. query: | SELECT quantile_cont(order_total, 0.95) AS percentile_95 FROM orders mustBeBetween: - 1000 - 99900 ``` ### Schema-level example ```yaml schema: - name: orders quality: - type: sql description: The maximum duration between two orders should be less than 3600 seconds. query: | SELECT MAX(duration) AS max_duration FROM ( SELECT EXTRACT(EPOCH FROM (order_timestamp - LAG(order_timestamp) OVER (ORDER BY order_timestamp))) AS duration FROM orders ) subquery mustBeLessThan: 3600 ``` ### Placeholders Instead of hard-coding names, the query can reference the schema, the property, and the location the server names: | Placeholder | Replaced with | |---|---| | `${model}` / `${table}` / `${object}` | the name of the schema object the rule belongs to | | `${field}` / `${column}` / `${property}` | the name of the property the rule belongs to (property-level rules only) | | `${schema}` | the server's `schema` | | `${dataset}` | the server's `dataset` (BigQuery) | | `${project}` | the server's `project` (BigQuery) | | `${catalog}` | the server's `catalog` (Databricks, Trino) | | `${database}` | the server's `database` (Postgres, SQL Server, Snowflake) | The `$` is optional: `{schema}` works the same as `${schema}`. A placeholder the server has no value for falls back to the name of the schema object. Any other `${NAME}` or `${NAME:-default}` in the query is an ODCS v3.2.0 [variable reference](../configuration.md#variables-in-the-data-contract), resolved from the environment after the placeholders above. A reference to an unset variable without a default fails the check with the variable's name. ```yaml quality: - type: sql description: The orders table is not empty. query: | SELECT COUNT(*) FROM ${project}.${dataset}.${table} mustBeGreaterThan: 0 ``` ### Comparators The query must return a single value, which is compared using exactly one of: | Comparator | Passes when the result… | |---|---| | `mustBe` | equals the value | | `mustNotBe` | does not equal the value | | `mustBeGreaterThan` / `mustBeGreaterOrEqualTo` | is above a lower bound | | `mustBeLessThan` / `mustBeLessOrEqualTo` | is below an upper bound | | `mustBeBetween` / `mustNotBeBetween` | is inside / outside a `[min, max]` range | ### The query must be a query A rule computes a single value from the data, so the query must be a **single read-only statement** — a `SELECT`, a `WITH … SELECT`, a `UNION`, or a parenthesized query. Anything else is reported as a failed check and never sent to the data source: DDL (`CREATE`, `DROP`, `ALTER`), DML (`INSERT`, `UPDATE`, `DELETE`), `COPY`, `ATTACH`, `INSTALL`/`LOAD`, `SET`, `PRAGMA`, `CALL`, and a second statement appended behind a legitimate one. This applies to every data source, and to every way the CLI is run. A data contract is not always written by the person whose credentials execute it. ### SQL dialect There is no `dialect` field on a quality rule. The dialect is **derived from the type of the server the rule runs against**, so a query is read the same way the engine that runs it will read it — BigQuery's backtick-quoted table names, Snowflake's `SAMPLE`, SQL Server's `TOP` and Postgres' `->>` are all understood without any declaration: | Server type | SQL dialect | |---|---| | `local`, `s3`, `gcs`, `azure`, `kafka`, `api`, `iceberg`, `duckdb`, `mysql` | `duckdb` | | `postgres` | `postgres` | | `redshift` | `redshift` | | `oracle` | `oracle` | | `sqlserver` | `tsql` | | `snowflake` | `snowflake` | | `bigquery` | `bigquery` | | `databricks` | `databricks` | | `athena` | `athena` | | `trino` | `trino` | | `exasol` | `exasol` | | `impala`, `hive` | `hive` | | `clickhouse` | `clickhouse` | | `dataframe` | `spark` | The ODCS synonyms resolve to the spelling above before the dialect is looked up, so `postgresql` is read as `postgres`. A server declared as `type: custom` with `customType: mssql` is read as `tsql`, like `sqlserver`. Files, Kafka topics, API responses and Iceberg tables are read through DuckDB, and MySQL tables are copied into DuckDB before the checks run, so a rule on those server types is written in DuckDB SQL — including its functions, such as `read_parquet` or `list_contains`. MySQL-only syntax, such as backtick-quoted names or `DATE_SUB`, does not work there. ### Notes - **Referencing the data** — reference the schema/table by its name in the `FROM` clause (e.g. `FROM orders`). - **`severity`** — set `severity: warning` to report a failing rule without failing the run (see [`--fail-on`](../commands/ci.md)). --- ## Text Quality Rules A `type: text` rule is a **human-readable expectation** written in plain language. It is **not executed** by `datacontract test` — it documents an agreement or an expectation that cannot (yet) be automated, so it stays visible in the contract and in generated documentation. ```yaml schema: - name: orders properties: - name: order_status logicalType: string quality: - type: text description: > order_status transitions follow the lifecycle placed → shipped → delivered, and never moves backwards. ``` ### When to use it - The expectation is understood and agreed, but you don't (yet) have a query or metric for it. - The rule is enforced by an external process and you want to record it in the contract. - You want to communicate intent to consumers alongside the executable rules. When you are ready to enforce it automatically, promote it to a [SQL](./sql.md) or [library](./library.md) rule. --- ## Define your Service Levels Beyond structure and quality, a data contract can declare **service levels** — the promises a provider makes about *when* the data arrives and *how long* it is kept. In ODCS these live in the top-level `slaProperties` block, and the CLI turns two of them into executable checks: ```yaml slaProperties: - property: freshness value: 24 unit: h element: orders.order_timestamp - property: retention value: P1Y element: orders.order_timestamp ``` Run them with [`datacontract test`](./testing/index.md) like any other check: ```bash datacontract test --checks slaProperties datacontract.yaml ``` Omit `--checks` to run service levels alongside the schema and quality checks. ### Freshness **`property: freshness`** asserts that the data is recent. The CLI reads the **newest** value of the referenced timestamp column (`MAX`) and compares its age against the threshold: ```yaml slaProperties: - property: freshness value: 24 unit: h element: orders.order_timestamp ``` > Passes when `now − MAX(orders.order_timestamp) < 24 hours`. | Freshness unit | Meaning | |---|---| | `d`, `day`, `days` | days (the default when `unit` is omitted) | | `h`, `hr`, `hour`, `hours` | hours | | `m`, `min`, `minute`, `minutes` | minutes | ### Retention **`property: retention`** asserts the opposite end: that data older than the retention period has been deleted. The CLI reads the **oldest** value of the column (`MIN`) and compares its age against the threshold: ```yaml slaProperties: - property: retention value: 1 unit: y element: orders.order_timestamp ``` > Passes when `now − MIN(orders.order_timestamp) < 1 year`. | Retention unit | Meaning | |---|---| | `y`, `yr`, `year`, `years` | years (365 days) | | `m`, `mo`, `month`, `months` | months (30 days) | | `d`, `day`, `days` | days (the default when `unit` is omitted) | | `h`, `hr`, `hour`, `hours` | hours | | `min`, `minute`, `minutes` | minutes | | `s`, `sec`, `second`, `seconds` | seconds | :::caution `unit: m` means **minutes** for `freshness` but **months** for `retention`. Spell the unit out (`minutes` / `months`) if you want to be unambiguous. ::: Retention also accepts an **ISO 8601 duration** as the `value`, in which case `unit` is ignored: ```yaml slaProperties: - property: retention value: P1Y6M # 1 year and 6 months; also P7Y, P90D, P12W, PT48H element: orders.order_timestamp ``` ### The `element` reference `element` must be a **fully qualified `schema.property`** reference — exactly one dot — pointing at a date or timestamp column: ```yaml element: orders.order_timestamp # schema "orders", property "order_timestamp" ``` Timestamps without a timezone are interpreted as UTC, and the age is measured against the current UTC time. :::note ODCS also defines a contract-level `slaDefaultElement`. The CLI does not use it — spell out `element` on every SLA property that should be checked. ::: ### When no check is generated A service level is skipped — without failing the run — when: - the `property` is anything other than `freshness` or `retention` (for example `latency`, `frequency`, or `timeOfAvailability`, which are documentation only), - `element` or `value` is missing, - `element` is not exactly one `schema.property` reference, - the referenced schema is not in the contract, - the `unit` is not one of the values listed above, or - the `value` is a string that is not a supported ISO 8601 duration. Run with `--debug` to see the reasons. If the check *does* run but the column is empty or entirely `NULL`, the check **fails** with `No timestamp value found in ''`. ### Where service levels are used - **[`test`](./commands/test.md)** — `freshness` and `retention` run as `slaProperties` checks. - **[SodaCL export](./exports/sodacl.md)** — both become Soda freshness and retention checks. - **[Markdown](./exports/markdown.md)**, **[HTML](./exports/html.md)**, and **[Excel](./exports/excel.md)** exports render the full `slaProperties` block, including the properties that are documentation only, and the `customProperties` and `authoritativeDefinitions` an entry may carry since ODCS v3.2.0. - **[`changelog`](./commands/changelog.md)** reports added, removed, and changed service levels between two versions. ### Learn more - The full `slaProperties` syntax is part of the [Open Data Contract Standard](https://bitol-io.github.io/open-data-contract-standard/latest/). - The [`orders` example contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) declares both a freshness and a retention SLA. --- ## Link your Semantics A property does not have to carry its full meaning inline. With the ODCS `authoritativeDefinitions` attribute, a property can **link to a shared semantic concept** — a business term defined once, centrally — and the CLI resolves that link and inlines the definition before linting, testing, or exporting the contract. ```yaml schema: - name: orders properties: - name: article authoritativeDefinitions: - type: semantics url: http://www.entropy-data.com/ns/main/Article ``` The referenced concept supplies the description, business name, logical type, and anything else it defines, so every contract that links to it stays consistent by construction. ### URL or IRI The `url` of a `type: semantics` link may be either of the two shapes ODCS allows, and the CLI resolves each differently: | `url` | Resolved as | |---|---| | A path or an absolute URL on the configured host — `/demo/semantics/main/product.brand` | **REST URL** — fetched directly | | An absolute URL on any other host — `http://www.entropy-data.com/ns/main/Article` | **IRI** — an identifier, not an address; looked up through `/api/semantics?iri=…` on the configured host | An IRI names a concept but is usually not fetchable at its own address, so the CLI never dereferences it directly. It URL-encodes the IRI and asks the configured host to resolve it. Only `type: semantics` (and the older `semantic` and `definition`) links are resolved. The `glossary`, `ontology` and `taxonomy` types that ODCS v3.2.0 recommends are kept as links: the HTML export shows them, and nothing is merged from them. ### Reusable definitions `type: definition` links to a reusable ODCS property definition instead of a semantic concept: ```yaml properties: - name: customer_email authoritativeDefinitions: - type: definition url: https://example.com/definitions/email.json ``` A `definition` URL on a different host is fetched **directly and anonymously** — a contract may legitimately point at a third-party URL, and the API key is never sent across hosts. When a property carries several links, the highest-precedence one wins and is the only one fetched: **`semantics`** → `semantic` (the legacy singular spelling, still accepted) → `definition` → `businessDefinition`. ### Reference a file A link may also point at a file next to the contract instead of a server. This is the shape to reach for when the business meaning of a field is described in the same repository, and no server is involved. There are two ways to write it, depending on where the definition lives. #### A property in another contract Use a `#` reference when the meaning belongs to a property of another contract — typically a business-level contract that a technical contract materializes: ```yaml # top-artists-by-year-view.odcs.yaml properties: - name: artist_name logicalType: string physicalType: character authoritativeDefinitions: - type: businessDefinition url: top-artists-by-year.odcs.yaml#schema/top_artists_by_year_ba/properties/artist_name ``` The technical contract keeps `logicalType`, `physicalType`, and `primaryKey`, and inherits `businessName`, `description`, `examples`, and everything else the business attribute defines. See [`examples/business-definitions`](https://github.com/datacontract/datacontract-cli/tree/main/examples/business-definitions) for the full pair. **The fragment** walks the referenced contract: ``` #schema//properties/[/properties/]…[/items] ``` Each step matches on `id` first and falls back to `name`. Contracts that carry stable ids should reference by `id` — that keeps the link intact when a human-readable `name` changes. The fragment has to end at a property; it cannot point at a schema object. #### A file that is the definition Leave the fragment off, and the file *is* the definition: it holds the elements of the property directly, with no contract around them. This is the shape for a glossary kept as one file per term: ```yaml # definitions/shipment_id.odcs.yaml businessName: Shipment ID description: Unique identifier for each shipment. logicalType: string examples: - 123e4567-e89b-12d3-a456-426614174000 ``` ```yaml # shipments.odcs.yaml properties: - name: sid physicalType: uuid authoritativeDefinitions: - type: businessDefinition url: definitions/shipment_id.odcs.yaml ``` A fragment-less url is read from disk when it names a `.yaml`, `.yml`, or `.json` file. Everything else without a fragment stays what it has always been — a path on the configured host, e.g. `url: /definitions/shipment_id`. #### Both shapes **The file path** is relative to the contract that holds the reference, so a checked-out directory resolves from any working directory. Absolute paths work too. A contract read from an HTTP URL cannot resolve a file reference. The type of the link says what the reference *means*; the shape of the `url` says where it *lives*. Every resolvable type — `semantics`, `semantic`, `definition`, `businessDefinition` — accepts a file reference and a URL alike, so the two are never tied together. Chains resolve: if the referenced business attribute itself links to a glossary file or a semantic concept, that link is resolved before the value is inlined. A cycle between files is reported as an error rather than followed. ### How the definition is merged The resolved definition fills in only what the property leaves unset — **inline values always win**: ```yaml properties: - name: article description: The article as sold in the German shop # kept authoritativeDefinitions: # supplies logicalType, - type: semantics # businessName, tags, … url: http://www.entropy-data.com/ns/main/Article ``` These are never merged, because they belong to the contract author: `id`, `name`, `authoritativeDefinitions` itself, and the `properties` / `items` that make up the structure. This is the same for a link to a semantic concept, a reusable definition, and a business attribute in another file. Resolution recurses into nested `properties` and array `items`, so a link on a deeply nested field resolves too. Successful lookups are cached per run; failures are not, so a transient error retries on the next run. ### Configuration | Environment variable | Purpose | |---|---| | `ENTROPY_DATA_HOST` | The host that resolves references. Defaults to `https://api.entropy-data.com`. Falls back to the deprecated `DATAMESH_MANAGER_HOST`, then `DATACONTRACT_MANAGER_HOST`. | | `ENTROPY_DATA_API_KEY` | Sent as `x-api-key`, and **only** to the configured host. Falls back to the deprecated `DATAMESH_MANAGER_API_KEY`, then `DATACONTRACT_MANAGER_API_KEY`. | An IRI lookup always requires an API key — `/api/semantics` is API-key only. A REST URL on the configured host uses the key when one is set. ### Turning resolution off Resolution runs by default on [`lint`](./commands/lint.md), [`test`](./commands/test.md), [`ci`](./commands/ci.md), [`changelog`](./commands/changelog.md), [`breaking`](./commands/breaking.md), and every [`export`](./commands/export/index.md) format. Pass `--no-inline-references` to skip it and work with the contract exactly as written: ```bash datacontract lint --no-inline-references datacontract.yaml ``` :::caution A reference that cannot be resolved **rejects the contract** — the command fails rather than silently continuing with an incomplete property. If the error is a 401 or 403, the configured host is usually the wrong deployment for that IRI; the message includes the `ENTROPY_DATA_HOST` to set. ::: ### Learn more - The `authoritativeDefinitions` syntax is part of the [Open Data Contract Standard](https://bitol-io.github.io/open-data-contract-standard/latest/). - Semantic concepts are managed in [Entropy Data](./entropy-data.md). --- ## Scheduling Data contracts deliver the most value when they are checked **continuously**, not just once. The recommended practice is to test contracts in CI/CD on every change and, in addition, to run them on a recurring schedule (for example daily) so you detect data drift and quality regressions in production data over time. The [`ci`](../commands/ci.md) command is purpose-built for automated runs: like [`test`](../commands/test.md), it runs a contract's checks, and adds CI-friendly annotations, a markdown summary, machine-readable output, and exit-code control via `--fail-on`. It has no options to narrow a run down (`--schema-name`, `--checks`, `--filter`, `--dry-run` and the like); use `test` for those. Two setups are covered in detail: - **[GitHub Actions](./github-actions.md)**: test contracts in pull requests and on a cron schedule with the same workflow. - **[Apache Airflow](./airflow.md)**: run tests as DAG tasks with the Data Contract provider, including credentials via connections, XCom results, and a results view in the Airflow UI. ### Azure DevOps ```yaml # azure-pipelines.yml trigger: branches: include: - main schedules: - cron: "0 6 * * *" displayName: Daily data contract tests branches: include: [main] always: true pool: vmImage: "ubuntu-latest" steps: - task: UsePythonVersion@0 inputs: versionSpec: "3.11" - script: pip install datacontract-cli displayName: "Install datacontract-cli" - script: datacontract ci datacontract.yaml displayName: "Run data contract tests" ``` ### Plain cron with Docker Without a CI system or orchestrator, schedule the Docker image with cron: ```cron # Run every day at 06:00 — /etc/crontab or `crontab -e` 0 6 * * * docker run --rm -v "/path/to/contracts:/home/datacontract" \ -e DATACONTRACT_POSTGRES_USERNAME -e DATACONTRACT_POSTGRES_PASSWORD \ datacontract/cli:latest ci datacontract.yaml ``` ### Other orchestrators (Databricks, Dagster, Prefect, …) Because the CLI is also a [Python library](../python-library.md), any orchestrator task can call it directly: ```python from datacontract.data_contract import DataContract def test_orders_contract(): run = DataContract(data_contract_file="orders.odcs.yaml").test() if not run.has_passed(): raise RuntimeError("Data contract tests failed") ``` Wrap this in a Databricks job, a Dagster op, or a Prefect task and schedule it with the orchestrator's native scheduler. For Airflow, prefer the [provider](./airflow.md). ### Controlling failure behavior Use `--fail-on` to decide when a run should be marked as failed: ```bash # Fail the job on errors only (default) datacontract ci --fail-on error datacontract.yaml # Also fail on warnings datacontract ci --fail-on warning datacontract.yaml # Never fail (e.g. report-only schedules) datacontract ci --fail-on never datacontract.yaml ``` ### Publishing results A run tells you whether the data is compliant *today*. Once several contracts run on a schedule, the next question is usually how they behave *over time*, and across teams. Publish each run to track results centrally: ```bash datacontract ci datacontract.yaml --publish https://api.entropy-data.com/api/test-results ``` The [Airflow provider](./airflow.md) publishes automatically when an Entropy Data connection is configured. See [Integrate with Entropy Data](../entropy-data.md). ### Next steps - Roll this out beyond a single contract: **[Adopting Data Contracts](../best-practices.md)**. - Share the current state with your team: `datacontract export html` and the [`catalog`](../commands/catalog.md) command. --- ## Apache Airflow The **[Data Contract Provider for Apache Airflow](https://github.com/datacontract/airflow-provider-datacontract)** runs `datacontract test` as a task in your DAGs: - `DataContractTestOperator` tests a contract against real data and fails the task when the contract is violated, so bad data stops before it propagates downstream. - Per-check results are rendered in the task log (pass/fail, reason, model/field). - The full run report is pushed to XCom in the test-results API model, so downstream tasks can branch on the outcome. - On Airflow 3, a "Data Contract Results" view in the UI shows the state of all tested contracts, with run history, per-contract stats, and per-check details. - A "Test Results" button on the task instance links to the published results, e.g. in Entropy Data. ### Installation Install the provider on your Airflow workers. Extras are passed through to the Data Contract CLI and select the data source support: ```bash pip install "airflow-provider-datacontract[snowflake]" ``` Available extras: `duckdb` (local files, csv/parquet), `snowflake`, `databricks`, `bigquery`, `postgres`, `s3`, `azure`, `kafka`, `trino`. In a containerized deployment, add it to your Airflow image: ```dockerfile FROM apache/airflow:3.1.6 RUN pip install --no-cache-dir "airflow-provider-datacontract[databricks]" ``` The provider requires Apache Airflow 2.10+ and Python 3.10+. The results view in the UI requires Airflow 3.1+. ### Set up connections Credentials are resolved through Airflow's connection machinery, including any configured secrets backend (Vault, AWS Secrets Manager, Azure Key Vault, …). The connection is mapped to [Data Contract CLI configuration](../configuration.md) based on its type and passed programmatically; credentials never touch the process environment. Create a connection for the server under test, for example Databricks with a service principal: ```bash airflow connections add databricks_prod \ --conn-type databricks \ --conn-host adb-1234567890123456.7.azuredatabricks.net \ --conn-login "" \ --conn-password "" \ --conn-extra '{"http_path": "/sql/1.0/warehouses/abc123"}' ``` Supported connection types: `databricks` (host, extra `http_path`; token auth: password = token, login empty; service principal OAuth: login = client id, password = client secret), `snowflake` (login/password, extra `account`, `warehouse`, `role`), `postgres`, `mysql`, `oracle`, `impala`, `trino`, `mssql`, `redshift` (login/password/host/port/schema), `aws` (login/password = key pair, extra `region_name`), `google_cloud_platform` (extra `key_path`, `project`), `wasb`/`azure`, and `kafka`. For anything else, add `datacontract_`-prefixed keys to the connection extra (e.g. `datacontract_trino_jwt_token`); those pass through to any [configuration](../configuration.md) field and also override the mapped values. To publish test results to [Entropy Data](../entropy-data.md), create a connection with the `entropydata` type. The API key goes in the password field; the host is optional (default `https://api.entropy-data.com`): ```bash airflow connections add entropy_data \ --conn-type entropydata \ --conn-password "" ``` ### Test a data contract in a DAG ```python from datetime import datetime from airflow.sdk import dag from datacontract_provider.operators.datacontract import DataContractTestOperator @dag(schedule="0 2 * * *", start_date=datetime(2026, 1, 1), catchup=False) def nightly_datacontract_test(): DataContractTestOperator( task_id="test_orders_contract", data_contract_file="https://demo.datacontract.com/orders-latest/datacontract.yaml", server="production", server_conn_id="databricks_prod", # credentials for the server under test entropy_data_conn_id="entropy_data", # optional: publish results to Entropy Data ) nightly_datacontract_test() ``` `data_contract_file` accepts a local path or a URL, so the contract can live next to the DAG, in a Git repository, or on a data contract platform. When `entropy_data_conn_id` is set, test results are published to `/api/test-results` automatically. #### Operator parameters | Parameter | Description | |---|---| | `data_contract_file` | Path or URL of the data contract YAML (templated) | | `data_contract_str` | Contract as a YAML string, alternative to `data_contract_file` | | `server` | Server key from the contract's `servers` section to test against | | `schema_name` | Schema/model to test, defaults to all | | `check_categories` | Subset of `schema`, `quality`, `servicelevel`, `custom` | | `server_conn_id` | Airflow connection with credentials for the server under test | | `entropy_data_conn_id` | Airflow connection for Entropy Data (type `entropydata`) | | `config` | Dict of additional [configuration](../configuration.md) fields, merged over the connection-derived values | | `publish_url` | URL to publish test results to (optional) | | `results_web_url` | Web page of the published results, shown as a "Test Results" button (optional) | | `include_failed_samples` | Collect samples of failing rows | | `fail_on_warning` | Also fail the task on result `warning` | | `datacontract_kwargs` | Extra kwargs for the `DataContract` constructor, e.g. `spark` | ### Branch on the result The operator pushes the full run report to XCom under the key `datacontract_result`, in the shape of the test-results API model (the Data Contract CLI `Run`): ```json { "runId": "da03bc96-...", "dataContractId": "orders", "dataContractVersion": "1.0.0", "server": "production", "timestampStart": "2026-08-02T02:00:04Z", "timestampEnd": "2026-08-02T02:03:05Z", "result": "passed", "checks": [ { "category": "schema", "type": "field_type", "name": "Check that field order_id has type string", "model": "orders", "field": "order_id", "result": "passed", "engine": "datacontract-cli" } ], "logs": [{"level": "INFO", "message": "Running checks with ibis", "timestamp": "..."}] } ``` `None` fields are omitted. This is the same JSON the CLI publishes to `/api/test-results`, so anything built against that API works against the XCom payload too. Downstream tasks can read it: ```python from airflow.sdk import dag, task @task(trigger_rule="all_done") def notify_on_failures(**context): report = context["ti"].xcom_pull( task_ids="test_orders_contract", key="datacontract_result" ) failed = [c for c in report.get("checks", []) if c.get("result") in ("failed", "error")] if failed: send_slack_message(f"{len(failed)} contract checks failed for {report['dataContractId']}") ``` The operator itself fails the task when the run result is `failed` or `error` (and on `warning` with `fail_on_warning=True`), so a plain `>>` dependency is already a quality gate; use `trigger_rule="all_done"` for tasks that must run on failure, like notifications. ### Results in the Airflow UI On Airflow 3.1+, the provider adds a **Data Contract Results** entry to the navigation: a status board with one row per data contract, showing the latest result, a clickable run-history strip, and the check summary (a Runs tab keeps the raw run log): ![Data Contract Results view in the Airflow UI, one row per data contract with result badge, run history, and check counts](/img/airflow-results.png) Each contract expands into stats (pass rate over the tracked runs, failing checks, last passed, average duration) and the run detail: result counts as clickable filters, a text filter, checks grouped by model, per-check diagnostics and failed samples, and the run logs. Runs with problems open filtered to the not-passed checks, and each failure is annotated as new in this run or failing for several runs: ![Expanded data contract in the Data Contract Results view, showing per-contract stats, run metadata, and the per-check results grouped by model](/img/airflow-results-details.png) With `results_web_url` set, the task instance additionally shows a "Test Results" button that deep-links to the published results, e.g. in Entropy Data. ### Further reading - [`datacontract test`](../commands/test.md) reference and [Test your Data](../testing/index.md) for the supported data sources - [Configuration](../configuration.md) for all credential and connection options - [Integrate with Entropy Data](../entropy-data.md) for tracking results over time - [Provider repository](https://github.com/datacontract/airflow-provider-datacontract) on GitHub --- ## GitHub Actions Run data contract tests in the same workflow twice over: on every change (push and pull request), so a breaking change is caught in the PR that introduces it, and on a cron schedule, so data drift in production is caught between changes. The quickest route is the ready-made **[datacontract/datacontract-action](https://github.com/datacontract/datacontract-action/)**. To run the CLI directly: ```yaml # .github/workflows/datacontract.yml name: Data Contract CI on: push: branches: [main] pull_request: schedule: # Run every day at 06:00 UTC to catch data drift in production - cron: "0 6 * * *" jobs: datacontract-ci: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.11" - run: pip install datacontract-cli # Test one or more data contracts (supports globs, e.g. contracts/*.yaml) - run: datacontract ci datacontract.yaml env: DATACONTRACT_POSTGRES_USERNAME: ${{ secrets.DB_USERNAME }} DATACONTRACT_POSTGRES_PASSWORD: ${{ secrets.DB_PASSWORD }} ``` ### What `ci` does in Actions The [`ci`](../commands/ci.md) command detects GitHub Actions and emits: - **Annotations** on failed checks, so violations show up inline in the PR. - A **job summary** with the check results as markdown. - A nonzero **exit code** on violations, failing the workflow; tune with `--fail-on error|warning|never` (see [controlling failure behavior](./index.md#controlling-failure-behavior)). ### Credentials Pass credentials for the server under test as [configuration environment variables](../configuration.md) from GitHub secrets, as in the example above (`DATACONTRACT_POSTGRES_USERNAME`, `DATACONTRACT_SNOWFLAKE_PASSWORD`, …). Each data source's variables are listed in [Test your Data](../testing/index.md). ### Publishing results Add `--publish` to track results centrally: ```yaml - run: datacontract ci datacontract.yaml --publish https://api.entropy-data.com/api/test-results env: ENTROPY_DATA_API_KEY: ${{ secrets.ENTROPY_DATA_API_KEY }} ``` See [Integrate with Entropy Data](../entropy-data.md). --- ## export dbt ## `datacontract export dbt` Removed: use `datacontract export dbt-models` instead. ```bash datacontract export dbt [OPTIONS] ``` --- ## import dbt ## `datacontract import dbt` Import a data contract from a dbt manifest file. ```bash datacontract import dbt [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to the dbt manifest.json file. | | `--model` | — | List of models names to import from the dbt manifest file (repeat for multiple models names, leave empty for all models in the dataset). | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import dbt --source manifest.json --output datacontract.yaml ``` Guide: **[Import: dbt](../../imports/dbt.md)**. --- ## Sync with dbt The Data Contract CLI integrates with [dbt](https://www.getdbt.com/) in multiple ways: - **Sync**: Merge the schema and tests of one or multiple data contracts into an existing dbt project (`datacontract dbt sync`) - **Test**: Run the CLI-generated dbt tests, optionally scoped to a contract ( `datacontract dbt test`) - **Export**: Do a one-time export from a data contract into a dbt model schema, a sources YAML, or a staging SQL file (`datacontract export`) - **Import**: Create a data contract from a dbt manifest file (`datacontract import dbt`) The tests that `dbt sync`, `dbt-models` and `dbt-sources` generate need dbt 1.10 or later. `datacontract dbt test` and `dbt sync --run-tests` stop with an error on an older dbt. ### `datacontract dbt sync` `dbt sync` reads one or more ODCS data contracts and applies the specified schema and tests to an existing dbt project. It updates the model properties files (YAML) in-place and adds singular SQL tests if needed. ```bash # Auto-discover every *.odcs.yaml in a dbt project, update models and test datacontract dbt sync # Delete columns, tests etc. that are not specified in the contract datacontract dbt sync --prune # Specify contract and dbt project directory datacontract dbt sync orders.odcs.yaml --project-dir ./warehouse # Several contracts, or a glob datacontract dbt sync orders.odcs.yaml customers.odcs.yaml datacontract dbt sync "contracts/*.odcs.yaml" # Run the generated tests (or as separate command: datacontract dbt test) datacontract dbt sync --run-tests # Generate, run, and publish results to an Entropy Data instance datacontract dbt sync orders.odcs.yaml --run-tests --publish https://api.entropy-data.com/api/test-results # Preview changes without applying them datacontract dbt sync orders.odcs.yaml --dry-run ``` A list of all options can be found at the [`dbt` command reference](./commands/dbt/sync.md). #### What is synced? All CLI-managed content in the dbt project gets marked with custom meta tags. By default, schemas or tests that are neither specified in the contract nor generated by the CLI are kept, unless `--prune` is specified. | ODCS source | dbt target | |---|------------------------------------------------------------| | `schema` / `property` description | model / column `description` | | `property.logicalType` / `physicalType` | column `data_type`, mapped to the server dialect | | `property` | column entry | | `schema.tags` / `property.tags` | `config.tags` | | `property.required`, single `primaryKey`, library metrics `nullValues`, `duplicateValues` and `invalidValues` (with `validValues`) at `mustBe: 0` | native dbt tests (`not_null`, `unique`, `accepted_values`) | | composite `primaryKey`, `logicalTypeOptions` bounds (`minLength`, `maxLength`, `pattern`, `minimum`, …), `quality` with a `query` + bound, other library metrics, `schema.quality` `rowCount` and `duplicateValues` | singular SQL tests at `/datacontract_cli/` | #### Example Given this contract for IDs and email addresses in the `orders` schema: ```yaml title="orders-v1.odcs.yaml" apiVersion: v3.0.0 kind: DataContract id: orders version: 1.0.0 status: active schema: - name: orders description: Customer orders properties: - name: order_id physicalType: integer primaryKey: true - name: customer_email description: Billing email address logicalType: string physicalType: text required: true logicalTypeOptions: maxLength: 320 ``` Let's assume that a minimal model properties file already exists. This is what it looks like after running `datacontract dbt sync orders-v1.odcs.yaml`: ```yaml title="models/orders.yml" version: 2 models: - name: orders description: Customer orders config: meta: datacontract_cli: contract_id: orders columns: - name: order_id data_type: integer data_tests: - not_null: config: meta: datacontract_cli: check: orders__order_id__field_required include_in_tests: true contract_versions: - 1.0.0 generated: true description: Check that field order_id has no missing values - unique: config: meta: datacontract_cli: check: orders__order_id__field_unique include_in_tests: true contract_versions: - 1.0.0 generated: true description: Check that field order_id has no duplicate values config: meta: datacontract_cli: generated: true - name: customer_email data_type: text description: Billing email address data_tests: - not_null: config: meta: datacontract_cli: check: orders__customer_email__field_required include_in_tests: true contract_versions: - 1.0.0 generated: true description: Check that field customer_email has no missing values config: meta: datacontract_cli: generated: true ``` The generated `config.meta.datacontract_cli` block is how `dbt sync`/`dbt test` recognize and scope managed tests; the per-column `config.meta.datacontract_cli.generated` marks a column the CLI added (and is allowed to remove). The `maxLength` bound becomes a self-contained singular SQL test (no `dbt_utils` needed). Its `config()` header carries the same `datacontract_cli` metadata: ```sql title="tests/datacontract_cli/orders/orders__1_0_0__orders__customer_email__length.sql" -- AUTO-GENERATED by `datacontract dbt sync`. Do not edit. -- Source contract: orders@1.0.0 (model: orders, check: customer_email__length) {{ config(meta={"datacontract_cli": {"check": "orders__customer_email__field_length", "contract_versions": ["1.0.0"], "generated": true, "include_in_tests": true, "model": "orders", "field": "customer_email", "description": "Check that field customer_email has a length of at most 320"}}) }} SELECT * FROM {{ ref('orders') }} WHERE customer_email IS NOT NULL AND (LENGTH(customer_email) > 320) ``` #### Versioned models `dbt sync` works natively with [dbt model versions](https://docs.getdbt.com/docs/mesh/govern/model-versions). To specify multiple versions using data contracts, create one contract file per version. Their filename must include an integer version number with a preceding `v`, e.g. `orders-v01.odcs.yaml`. If `dbt sync` is fed with a subset of the versioned contracts, the other versions remain unchanged. ### `datacontract dbt test` Runs the contract-managed tests that `dbt sync` generated, reports the results, and optionally publishes them. Like `dbt sync`, the command accepts multiple contracts. ```bash # Every contract in the current project datacontract dbt test # A single contract and dbt target in a specified project datacontract dbt test orders.odcs.yaml --project-dir ./warehouse --target dev ``` The command shells out to `dbt`, so a dbt adapter for your warehouse has to be installed alongside the CLI, e.g. `pip install dbt-snowflake`. `dbt sync --run-tests` needs one for the same reason. :::warning[Not available in the Docker image] `datacontract dbt test` does not work in the `datacontract/cli` container. The image ships no dbt adapter, and it is built on a minimal base with no shell or package manager, so one cannot be added to it — a derived image with `RUN pip install dbt-` will not build. Run the command from a Python install (`pip install datacontract-cli`, plus your adapter) instead. `datacontract dbt sync` is unaffected and works in the container: it only reads and writes files in the dbt project. ::: See the [`dbt` command reference](./commands/dbt/index.md). ### dbt exporters Convert a contract into dbt artifacts: - **[`dbt-models`](./exports/dbt-models.md)** — dbt model schema YAML. If a server is selected via `--server`, the dbt `data_types` match the expected data types of that server; otherwise it defaults to `snowflake`. - **[`dbt-sources`](./exports/dbt-sources.md)** — dbt sources YAML. - **[`dbt-staging-sql`](./exports/dbt-staging-sql.md)** — a dbt staging SQL file. ```bash datacontract export dbt-models datacontract.yaml --server snowflake ``` ### dbt importer Generate a contract from a dbt project's `manifest.json`: ```bash # Import specific tables from a dbt manifest datacontract import dbt --source manifest.json --model orders --model line_items # Import all tables datacontract import dbt --source manifest.json ``` --- ## Import: dbt ## ![](/img/icons/dbt.svg) Import: dbt Creates a data contract from a dbt `manifest.json`. ```bash # Import specific tables datacontract import dbt --source manifest.json --model orders --model line_items # Import all tables in the database datacontract import dbt --source manifest.json ``` A column's `meta` is kept: `classification` becomes the property's `classification`, and the other entries become a custom property named `meta`. See the [dbt Integration](../dbt.md) guide for the full dbt workflow. All options: **[`datacontract import dbt`](../commands/import/dbt.md)**. --- ## Edit your Contract The [Data Contract Editor](https://github.com/datacontract/datacontract-editor) is a web-based visual editor for ODCS data contracts. It is hosted at [editor.datacontract.com](https://editor.datacontract.com), and the CLI can launch it locally against a file on your machine. ### Edit a local file ```bash datacontract edit odcs.yaml ``` This starts a local web server and opens the Data Contract Editor for the given file in your browser. The editor is bundled with the CLI, so **no internet access is required**. Saving in the editor writes directly back to the local file. Key behaviors: - If the file does not exist, you are asked whether to initialize a new data contract. - If a **URL** is given, you are asked whether to download a local copy, which is then edited. - The server also acts as the editor's **test runner**: clicking "Run test" in the editor executes the data contract tests locally against the servers defined in the contract. Credentials for the data sources must be provided as environment variables — see [Test your Data](./testing/index.md#configuring-the-connection). ### Requirements The `edit` command requires the `api` extra: ```bash pip install 'datacontract-cli[api]' ``` The editor assets (JS/CSS) are bundled with the CLI and work offline by default. You can change where they are loaded from: - `--editor-version` — load a specific version of the `datacontract-editor` npm package from the CDN, e.g. `0.1.9` or `latest`. - `--editor-assets-url` — load assets from a self-hosted editor build. Takes precedence over `--editor-version`. ### AI Assistant The editor's AI assistant is enabled when `DATACONTRACT_EDITOR_AI_ENDPOINT` is set. Like data source credentials, these variables can be set in a `.env` file. | Environment variable | Description | |---|---| | `DATACONTRACT_EDITOR_AI_ENDPOINT` | Chat completions URL (OpenAI-compatible) or Messages URL (Anthropic). | | `DATACONTRACT_EDITOR_AI_API_KEY` | API key. The key is passed to the browser, which calls the endpoint directly. | | `DATACONTRACT_EDITOR_AI_MODEL` | Model name, e.g. `gpt-4o` or `claude-sonnet-4-5`. | | `DATACONTRACT_EDITOR_AI_PROVIDER` | `openai` (default) or `anthropic`. | | `DATACONTRACT_EDITOR_AI_AUTH_HEADER` | `bearer` (default), `api-key` (Azure OpenAI), or `x-api-key` (Anthropic). | ```bash export DATACONTRACT_EDITOR_AI_PROVIDER=anthropic export DATACONTRACT_EDITOR_AI_ENDPOINT=https://api.anthropic.com/v1/messages export DATACONTRACT_EDITOR_AI_API_KEY=sk-ant-... export DATACONTRACT_EDITOR_AI_MODEL=claude-sonnet-4-5 export DATACONTRACT_EDITOR_AI_AUTH_HEADER=x-api-key datacontract edit datacontract.yaml ``` ### Options | Option | Default | Description | |---|---|---| | `--port` | `4243` | Bind the local server to this port. | | `--host` | `127.0.0.1` | Bind to this host. For Docker, set it to `0.0.0.0`. | | `--editor-version` | bundled | Version of the `datacontract-editor` npm package to load from the CDN. | | `--editor-assets-url` | — | Base URL to load editor assets from (e.g. a self-hosted build). | | `--language` | — | UI language of the editor: `en`, `de`, `sv` or `fr`. Without it, the editor starts in English and the language can be switched in the editor. | | `--open` / `--no-open` | `--open` | Open the editor in the default browser. | | `--debug` / `--no-debug` | `--no-debug` | Enable debug logging. | ### Example ```bash datacontract edit datacontract.yaml ``` See the [`edit` command reference](./commands/edit.md) for the full signature. To let a coding agent edit the YAML while you review it in the editor, see [Use with AI Coding Agents](./ai-agents.md). --- ## Use with AI Coding Agents The [Data Contract Skills](https://github.com/datacontract/datacontract-skills) teach coding agents such as Claude Code, OpenAI Codex, and GitHub Copilot CLI to work with data contracts. The agent uses the Data Contract CLI and the [Data Contract Editor](./editor.md) under the hood: it imports schemas, writes descriptions and quality rules, lints until the contract is valid, runs the tests, and explains failing checks. | Skill | What the agent does | |---|---| | Create a Data Contract | Imports a contract from a table, DDL, dbt, Avro, JSON Schema, or Excel, or writes it from requirements, then enriches and lints it. | | Test a Data Contract | Sets up the connection, runs `datacontract test`, and finds the cause of every failing check. | | Quality Rules | Adds quality rules and service levels per quality dimension, with thresholds grounded in the data. | | Breaking Change Check | Runs `changelog` and `breaking` on a changed contract, adds semantic changes, and proposes the version bump. | | Export a Data Contract | Generates SQL DDL, dbt models and tests, schemas, and model classes, and integrates them into the project. | | Data Contracts in CI/CD | Sets up lint and breaking-change checks on pull requests and scheduled data tests. | | Data Contract Editor | Opens the contract in the visual editor while the agent edits the YAML. | ### Install Claude Code: ```bash claude plugin marketplace add https://github.com/datacontract/datacontract-skills claude plugin install datacontract-skills@datacontract-skills ``` OpenAI Codex: ```bash codex plugin marketplace add https://github.com/datacontract/datacontract-skills codex plugin add datacontract-skills@datacontract-skills ``` GitHub Copilot CLI: ```bash copilot plugin marketplace add https://github.com/datacontract/datacontract-skills copilot plugin install datacontract-skills@datacontract-skills ``` Other agents that support [Agent Skills](https://agentskills.io) can use the `skills/` folder of the repository directly, or reference its `AGENTS.md`. ### Usage Ask in plain language: - "Create a data contract for the `orders` table in Snowflake." - "Test the orders contract against production and tell me why it fails." - "Is my change to the orders contract breaking?" - "Generate the dbt models and tests from the contract." The skills ask before connecting to a data source and never ask for credentials in the chat: set them as [environment variables](./configuration.md) or in a `.env` file. ### Documentation for LLMs Agents without the skills can still read this documentation in a compact form: [llms.txt](https://cli.datacontract.com/llms.txt) (a link index) and [llms-full.txt](https://cli.datacontract.com/llms-full.txt) (every page in one file). --- ## Imports `datacontract import` creates a data contract from an existing source format. This is the fastest way to bootstrap a contract from a system you already have. ```bash # Import from a SQL DDL file datacontract import sql --source my_ddl.sql --dialect postgres # Save the result to a file datacontract import sql --source my_ddl.sql --dialect postgres --output datacontract.yaml ``` The [Snowflake](./snowflake.md), [BigQuery](./bigquery.md), [Amazon Redshift](./redshift.md), [Postgres](./postgres.md), [MySQL](./mysql.md), [SQL Server](./sqlserver.md), [Oracle](./oracle.md), [Trino](./trino.md), [ClickHouse](./clickhouse.md), [Hive](./hive.md), [Amazon Athena](./athena.md), [Amazon S3](./s3.md), [Google Cloud Storage](./gcs.md), [Azure Blob / ADLS](./adls.md), [Databricks](./databricks.md), and [AWS Glue](./glue.md) importers can connect directly to the live system and introspect your tables — no export files needed. Snowflake, BigQuery, Redshift, Postgres, MySQL, SQL Server, Oracle, Trino, ClickHouse, Hive, Athena, S3, GCS, ADLS, and Databricks also generate a ready-to-test `servers` block, so `datacontract test` works right after the import. Run `datacontract import --help` to see the format-specific options (e.g. `datacontract import sql --help`). If a format you need is missing, [open an issue on GitHub](https://github.com/datacontract/datacontract-cli/issues). ### Example sources Each import page shows a runnable example: a small source file under [`examples/imports/`](https://github.com/datacontract/datacontract-cli/tree/main/examples/imports) and the data contract the CLI generates from it. Download a source and run the command on the page to reproduce the output. ### Available importers ![](/img/icons/azure.svg) adlsFiles in Azure Blob Storage. ![](/img/icons/athena.svg) athenaAn Amazon Athena database. ![](/img/icons/avro.svg) avroAn Avro schema file. ![](/img/icons/bigquery.svg) bigqueryGoogle BigQuery (file or API). ![](/img/icons/clickhouse.svg) clickhouseA ClickHouse database. ![](/img/icons/custom.svg) csvA CSV file. ![](/img/icons/databricks.svg) databricksDatabricks Unity Catalog. ![](/img/icons/dbml.svg) dbmlA DBML file. ![](/img/icons/dbt.svg) dbtA dbt manifest file. ![](/img/icons/excel.svg) excelAn ODCS Excel template. ![](/img/icons/gcs.svg) gcsFiles in Google Cloud Storage. ![](/img/icons/glue.svg) glueAWS Glue Data Catalog. ![](/img/icons/database.svg) hiveA Hive database. ![](/img/icons/iceberg.svg) icebergAn Iceberg schema. ![](/img/icons/json.svg) jsonA JSON data file. ![](/img/icons/jsonschema.svg) jsonschemaA JSON Schema file. ![](/img/icons/mysql.svg) mysqlA MySQL database. ![](/img/icons/odata.svg) odataAn OData 4.x service. ![](/img/icons/odcs.svg) odcsAn ODCS data contract file. ![](/img/icons/api.svg) openapiA GET or POST operation of an OpenAPI document. ![](/img/icons/oracle.svg) oracleAn Oracle database. ![](/img/icons/parquet.svg) parquetA Parquet file. ![](/img/icons/postgres.svg) postgresA Postgres schema. ![](/img/icons/powerbi.svg) powerbiA Power BI semantic model (.pbit, .bim, or .json). ![](/img/icons/custom.svg) protobufA Protobuf schema file. ![](/img/icons/pydantic.svg) pydantic-modelPydantic model classes. ![](/img/icons/redshift.svg) redshiftAn Amazon Redshift schema. ![](/img/icons/s3.svg) s3Files in an Amazon S3 bucket. ![](/img/icons/snowflake.svg) snowflakeA Snowflake workspace. ![](/img/icons/spark.svg) sparkA Spark schema / DataFrame. ![](/img/icons/database.svg) sqlA SQL DDL file. ![](/img/icons/sqlserver.svg) sqlserverA SQL Server database. ![](/img/icons/trino.svg) trinoA Trino catalog. ![](/img/icons/custom.svg) xsdAn XML Schema (XSD) file. See the [`import` command reference](../commands/import/index.md) for the common signature. --- ## Import: Azure Blob / ADLS ## ![](/img/icons/azure.svg) Import: Azure Blob / ADLS Creates a data contract from files in Azure Blob Storage, in CSV, JSON, Parquet, or Delta format. ```bash datacontract import adls \ --source abfss://my-container/orders/*.json \ --output datacontract.yaml ``` The format is taken from the file suffix; pass `--format` for Delta tables, which have none, or to override the guess. `--delimiter` pins how a JSON file is laid out (`new_line`, `array`, or `none`) instead of letting it be detected. The objects are read with duckdb through the same connection the test path uses, so the import authenticates identically and infers the column types from exactly the reader that later verifies them. The generated contract includes a ready-to-test `servers` block, so you can run `datacontract test datacontract.yaml` immediately afterwards — see the **[Azure Blob / ADLS connection guide](../testing/azure.md)** for the full 5-minute walkthrough and troubleshooting. Credentials are the same ones `datacontract test` uses — see the [Azure Blob Storage Reference](../reference/azure.md). All options: **[`datacontract import adls`](../commands/import/adls.md)**. --- ## Import: Amazon Athena ## ![](/img/icons/athena.svg) Import: Amazon Athena Creates a data contract from an Amazon Athena database, including column types, partition keys, and comments. ```bash datacontract import athena \ --schema my_database \ --staging-dir s3://my-bucket/athena-results/ \ --region eu-central-1 \ --output datacontract.yaml ``` `--schema` is the Athena database. Repeat `--table` to import only specific tables (by default every table in the database is imported), and set `--catalog` if you don't use `awsdatacatalog`. `--staging-dir` is the S3 location Athena writes query results to. `datacontract test` needs it, so the import writes it into the `servers` block. Athena keeps its table metadata in the AWS Glue Data Catalog, so the import reads the catalog rather than querying Athena — it needs only `glue:GetTables`, costs nothing to run, and requires no query results to be staged. Glue stores the Hive spelling of two types, which the import rewrites to what Athena reports back (`string` → `varchar`, `binary` → `varbinary`) so the physical type checks pass on the first test run. The generated contract includes a ready-to-test `servers` block, so you can run `datacontract test datacontract.yaml` immediately afterwards — see the **[Athena connection guide](../testing/athena.md)** for the full 5-minute walkthrough and troubleshooting. Credentials are provided as environment variables and are the same ones `datacontract test` uses: `DATACONTRACT_S3_REGION`, `DATACONTRACT_S3_ACCESS_KEY_ID`, and `DATACONTRACT_S3_SECRET_ACCESS_KEY` — see the [Athena Reference](../reference/athena.md). Want the Glue Data Catalog itself rather than Athena? Use [`datacontract import glue`](./glue.md). All options: **[`datacontract import athena`](../commands/import/athena.md)**. --- ## Import: Avro ## ![](/img/icons/avro.svg) Import: Avro Creates a data contract from an Avro schema (`.avsc`) file. Given this [`orders.avsc`](https://github.com/datacontract/datacontract-cli/blob/main/examples/imports/avro/orders.avsc): ```json { "type": "record", "name": "orders", "namespace": "com.example.checkout", "fields": [ { "name": "order_id", "type": "string", "doc": "Unique identifier of the order." }, { "name": "order_timestamp", "type": { "type": "long", "logicalType": "timestamp-millis" } }, { "name": "customer_id", "type": "string" }, { "name": "order_total", "type": "long" }, { "name": "status", "type": "string" } ] } ``` Run: ```bash datacontract import avro --source orders.avsc ``` to produce the data contract: ```yaml version: 1.0.0 kind: DataContract apiVersion: v3.2.0 id: my-data-contract name: My Data Contract status: draft schema: - name: orders physicalType: record customProperties: - property: namespace value: com.example.checkout logicalType: object physicalName: orders properties: - name: order_id physicalType: string description: Unique identifier of the order. logicalType: string required: true - name: order_timestamp physicalType: long logicalType: timestamp required: true - name: customer_id physicalType: string logicalType: string required: true - name: order_total physicalType: long logicalType: integer required: true - name: status physicalType: string # … ``` All options: **[`datacontract import avro`](../commands/import/avro.md)**. --- ## Import: BigQuery ## ![](/img/icons/bigquery.svg) Import: BigQuery Creates a data contract from BigQuery, either directly from the BigQuery API or from an exported table definition (JSON file). ### From the BigQuery API ```bash datacontract import bigquery \ --project my-project \ --dataset my_dataset \ --table orders \ --output datacontract.yaml ``` Repeat `--table` for multiple tables, or omit it to import every table in the dataset. Authentication uses Application Default Credentials (`gcloud auth application-default login`, or `GOOGLE_APPLICATION_CREDENTIALS`). The generated contract includes a ready-to-test `servers` block, so you can run `datacontract test datacontract.yaml` immediately afterwards — see the **[BigQuery connection guide](../testing/bigquery.md)** for the full 5-minute walkthrough and troubleshooting. ### From an exported JSON file Given this [`orders.json`](https://github.com/datacontract/datacontract-cli/blob/main/examples/imports/bigquery/orders.json): ```json { "kind": "bigquery#table", "tableReference": { "projectId": "my-gcp-project", "datasetId": "orders", "tableId": "orders" }, "description": "One row per customer order.", "schema": { "fields": [ { "name": "order_id", "type": "STRING", "mode": "REQUIRED", "description": "Unique identifier of the order." }, { "name": "order_timestamp", "type": "TIMESTAMP", "mode": "REQUIRED" }, { "name": "customer_id", "type": "STRING", "mode": "REQUIRED" }, { "name": "order_total", "type": "NUMERIC", "mode": "REQUIRED" }, { "name": "status", "type": "STRING", "mode": "REQUIRED" } ] } } ``` Run: ```bash datacontract import bigquery --source orders.json ``` to produce the data contract: ```yaml version: 1.0.0 kind: DataContract apiVersion: v3.2.0 id: my-data-contract name: My Data Contract status: draft servers: - server: bigquery type: bigquery dataset: orders project: my-gcp-project schema: - name: orders physicalType: table description: One row per customer order. logicalType: object physicalName: orders properties: - name: order_id physicalType: STRING description: Unique identifier of the order. logicalType: string required: true - name: order_timestamp physicalType: TIMESTAMP logicalType: timestamp required: true - name: customer_id physicalType: STRING logicalType: string required: true - name: order_total physicalType: NUMERIC logicalType: number # … ``` All options: **[`datacontract import bigquery`](../commands/import/bigquery.md)**. --- ## Import: ClickHouse ## ![](/img/icons/clickhouse.svg) Import: ClickHouse Creates a data contract from a ClickHouse database by reading `system.tables` and `system.columns` — including the full declared column types, the sorting key, and the comments on tables and columns. ```bash datacontract import clickhouse \ --source localhost \ --database sales \ --output datacontract.yaml ``` `--source` is the host of your ClickHouse server. Add `--port` if its HTTP interface doesn't listen on the default `8123`, and repeat `--table` to import only specific tables (by default every table and view in the database is imported). ClickHouse reports a complete type string, so `Nullable(String)`, `LowCardinality(String)` and `Decimal(10, 2)` are taken verbatim and match what `datacontract test` reads back. A column is `required` unless its type is `Nullable`, and the columns of the sorting key become the primary key. The generated contract includes a ready-to-test `servers` block, so you can run `datacontract test datacontract.yaml` immediately afterwards — see the **[ClickHouse connection guide](../testing/clickhouse.md)** for the full 5-minute walkthrough and troubleshooting. Credentials are the same ones `datacontract test` uses — see the [ClickHouse Reference](../reference/clickhouse.md). Only have a DDL file? Use [`datacontract import sql --dialect clickhouse`](./sql.md). All options: **[`datacontract import clickhouse`](../commands/import/clickhouse.md)**. --- ## Import: CSV ## ![](/img/icons/custom.svg) Import: CSV Creates a data contract by inferring the schema (column names and types) from a CSV file. Given this [`orders.csv`](https://github.com/datacontract/datacontract-cli/blob/main/examples/imports/csv/orders.csv): ```text order_id,order_timestamp,customer_id,order_total,status ORD-1001,2024-01-01T10:00:00Z,CUST-1,4999,delivered ORD-1002,2024-01-02T11:30:00Z,CUST-2,2500,shipped ORD-1003,2024-01-03T09:15:00Z,CUST-3,1299,pending ``` Run: ```bash datacontract import csv --source orders.csv ``` to produce the data contract: ```yaml version: 1.0.0 kind: DataContract apiVersion: v3.2.0 id: my-data-contract name: My Data Contract status: draft servers: - server: production type: local customProperties: - property: delimiter value: ',' format: csv path: examples/imports/csv/orders.csv schema: - name: orders physicalType: table description: Generated model of examples/imports/csv/orders.csv logicalType: object physicalName: orders properties: - name: order_id physicalType: VARCHAR logicalType: string required: true unique: true examples: - ORD-1003 - ORD-1001 - ORD-1002 - name: order_timestamp physicalType: VARCHAR logicalType: timestamp required: true # … ``` All options: **[`datacontract import csv`](../commands/import/csv.md)**. --- ## Import: Databricks ## ![](/img/icons/databricks.svg) Import: Databricks Creates a data contract from Databricks Unity Catalog, from an exported JSON file or via the HTTP endpoint. ```bash # From the HTTP endpoint (repeat --table for multiple tables) datacontract import databricks --table my_catalog.my_schema.orders # From an exported Unity Catalog TableInfo JSON file datacontract import databricks --source unity_table.json ``` For the HTTP endpoint, authenticate either with a personal access token (`DATACONTRACT_DATABRICKS_SERVER_HOSTNAME` + `DATACONTRACT_DATABRICKS_TOKEN`) or with a profile from `~/.databrickscfg` (`DATACONTRACT_DATABRICKS_PROFILE`). The generated contract includes a ready-to-test `servers` block (`type: databricks` with catalog and schema). To run `datacontract test` afterwards, additionally set `DATACONTRACT_DATABRICKS_HTTP_PATH` to a running SQL warehouse — see the **[Databricks connection guide](../testing/databricks.md)** for the full 5-minute walkthrough and troubleshooting. The importer was previously called `unity`. That name still works, so existing scripts keep running. All options: **[`datacontract import databricks`](../commands/import/databricks.md)**. --- ## Import: DBML ## ![](/img/icons/dbml.svg) Import: DBML Creates a data contract from a [DBML](https://dbml.dbdiagram.io/) file. You can filter by schema and table name. Given this [`orders.dbml`](https://github.com/datacontract/datacontract-cli/blob/main/examples/imports/dbml/orders.dbml): ```text Table orders { order_id varchar [pk, note: 'Unique identifier of the order.'] order_timestamp timestamp [not null] customer_id varchar [not null] order_total bigint [not null] status varchar [not null] } ``` Run: ```bash datacontract import dbml --source orders.dbml ``` to produce the data contract: ```yaml version: 1.0.0 kind: DataContract apiVersion: v3.2.0 id: my-data-contract name: My Data Contract status: draft schema: - name: orders physicalType: table customProperties: - property: namespace value: public logicalType: object physicalName: orders properties: - name: order_id physicalType: varchar description: Unique identifier of the order. primaryKey: true primaryKeyPosition: 1 logicalType: string - name: order_timestamp physicalType: timestamp logicalType: timestamp required: true - name: customer_id physicalType: varchar logicalType: string required: true - name: order_total physicalType: bigint logicalType: integer required: true - name: status # … ``` All options: **[`datacontract import dbml`](../commands/import/dbml.md)**. --- ## Import: Excel ## ![](/img/icons/excel.svg) Import: Excel Creates a data contract from an ODCS Excel template — the round-trip counterpart of [`export excel`](../exports/excel.md). ```bash datacontract import excel --source odcs.xlsx --output datacontract.yaml ``` You can customize the template as you like. Start from a copy of the official template and adapt it (branding, extra sheets, additional columns). Note that the import will not read sheets or columns that are named differently than in the template. All options: **[`datacontract import excel`](../commands/import/excel.md)**. --- ## Import: Google Cloud Storage ## ![](/img/icons/gcs.svg) Import: Google Cloud Storage Creates a data contract from files in Google Cloud Storage, in CSV, JSON, Parquet, or Delta format. ```bash datacontract import gcs \ --source s3://my-bucket/orders/*.json \ --output datacontract.yaml ``` The format is taken from the file suffix; pass `--format` for Delta tables, which have none, or to override the guess. `--delimiter` pins how a JSON file is laid out (`new_line`, `array`, or `none`) instead of letting it be detected. duckdb reads Google Cloud Storage through its S3-compatible endpoint, so the location uses the `s3://` scheme rather than `gs://`; a `gs://` source is rewritten for you. The objects are read with duckdb through the same connection the test path uses, so the import authenticates identically and infers the column types from exactly the reader that later verifies them. The generated contract includes a ready-to-test `servers` block, so you can run `datacontract test datacontract.yaml` immediately afterwards — see the **[Google Cloud Storage connection guide](../testing/gcs.md)** for the full 5-minute walkthrough and troubleshooting. Credentials are the same ones `datacontract test` uses — see the [Google Cloud Storage Reference](../reference/gcs.md). All options: **[`datacontract import gcs`](../commands/import/gcs.md)**. --- ## Import: AWS Glue ## ![](/img/icons/glue.svg) Import: AWS Glue Creates a data contract from the AWS Glue Data Catalog. ```bash # Import specific tables from a Glue database datacontract import glue --database my_database --table orders --table line_items # Import all tables in the database datacontract import glue --database my_database ``` AWS credentials are resolved from the standard AWS environment / configuration (e.g. `AWS_PROFILE`, `AWS_REGION`). :::note `datacontract test` cannot connect to Glue directly. To test the actual data, replace the generated `servers` entry with the underlying storage — typically [Amazon S3](../testing/s3.md) or [Amazon Athena](../testing/athena.md). ::: All options: **[`datacontract import glue`](../commands/import/glue.md)**. --- ## Import: Hive ## ![](/img/icons/database.svg) Import: Hive Creates a data contract from a Hive database by reading `DESCRIBE FORMATTED` for each table — including the full declared column types, the partition columns, and the comments on tables and columns. ```bash datacontract import hive \ --source localhost \ --database sales \ --output datacontract.yaml ``` `--source` is the host of your HiveServer2. Add `--port` if it doesn't listen on the default `10000`, and repeat `--table` to import only specific tables (by default every table and view in the database is imported). Hive reports a complete type string, so `varchar(10)`, `decimal(10,2)` and `struct` are taken verbatim and match what `datacontract test` reads back. Arrays, maps, and structs are expanded into `items`, `map` and nested `properties`. The generated contract includes a ready-to-test `servers` block, so you can run `datacontract test datacontract.yaml` immediately afterwards — see the **[Hive connection guide](../testing/hive.md)** for the full 5-minute walkthrough and troubleshooting. Credentials are the same ones `datacontract test` uses — see the [Hive Reference](../reference/hive.md). Only have a DDL file? Use [`datacontract import sql --dialect hive`](./sql.md). All options: **[`datacontract import hive`](../commands/import/hive.md)**. --- ## Import: Iceberg ## ![](/img/icons/iceberg.svg) Import: Iceberg Creates a data contract from an [Apache Iceberg](https://iceberg.apache.org/) table schema. ### Import from a REST catalog ```bash datacontract import iceberg \ --catalog-url https://polaris.example.com/api/catalog \ --catalog main --namespace sales --table orders \ --output datacontract.yaml ``` The result includes a server ready for `datacontract test`. `--catalog` defaults to `default`; a qualified `--table sales.orders` also works without `--namespace`. See [testing Iceberg](../testing/iceberg.md) for authentication and [Amazon S3 Tables](../testing/s3-tables.md) for the AWS endpoint, SSO, and required warehouse ARN. ### Import from a schema file A schema-file import does not add a server. Add one before testing live data. Given this [`orders.json`](https://github.com/datacontract/datacontract-cli/blob/main/examples/imports/iceberg/orders.json): ```json { "type": "struct", "fields": [ { "id": 1, "name": "order_id", "type": "string", "required": true }, { "id": 2, "name": "order_timestamp", "type": "timestamptz", "required": true }, { "id": 3, "name": "customer_id", "type": "string", "required": true }, { "id": 4, "name": "order_total", "type": "long", "required": true }, { "id": 5, "name": "status", "type": "string", "required": true } ], "schema-id": 0, "identifier-field-ids": [1] } ``` Run: ```bash datacontract import iceberg --source orders.json ``` to produce the data contract: ```yaml version: 1.0.0 kind: DataContract apiVersion: v3.2.0 id: my-data-contract name: My Data Contract status: draft schema: - name: iceberg_table physicalType: table logicalType: object physicalName: iceberg_table properties: - name: order_id physicalType: string customProperties: - property: icebergFieldId value: 1 primaryKey: true primaryKeyPosition: 1 logicalType: string required: true - name: order_timestamp physicalType: timestamptz customProperties: - property: icebergFieldId value: 2 logicalType: timestamp required: true - name: customer_id physicalType: string customProperties: - property: icebergFieldId value: 3 logicalType: string # … ``` All options: **[`datacontract import iceberg`](../commands/import/iceberg.md)**. --- ## Import: JSON ## ![](/img/icons/json.svg) Import: JSON Creates a data contract by inferring the schema from a JSON data file. Given this [`orders.json`](https://github.com/datacontract/datacontract-cli/blob/main/examples/imports/json/orders.json): ```json { "order_id": "ORD-1001", "order_timestamp": "2024-01-01T10:00:00Z", "customer_id": "CUST-1", "order_total": 4999, "status": "delivered" } ``` Run: ```bash datacontract import json --source orders.json ``` to produce the data contract: ```yaml version: 1.0.0 kind: DataContract apiVersion: v3.2.0 id: my-data-contract name: My Data Contract status: draft servers: - server: production type: local format: json path: examples/imports/json/orders.json schema: - name: orders physicalType: object description: Generated from JSON object in examples/imports/json/orders.json logicalType: object physicalName: orders properties: - name: order_id physicalType: string logicalType: string examples: - ORD-1001 - name: order_timestamp physicalType: string logicalType: string logicalTypeOptions: format: date-time examples: - '2024-01-01T10:00:00Z' - name: customer_id physicalType: string logicalType: string examples: # … ``` All options: **[`datacontract import json`](../commands/import/json.md)**. --- ## Import: JSON Schema ## ![](/img/icons/jsonschema.svg) Import: JSON Schema Creates a data contract from a [JSON Schema](https://json-schema.org/) file. Given this [`orders.schema.json`](https://github.com/datacontract/datacontract-cli/blob/main/examples/imports/jsonschema/orders.schema.json): ```json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "orders", "type": "object", "properties": { "order_id": { "type": "string", "description": "Unique identifier of the order." }, "order_timestamp": { "type": "string", "format": "date-time" }, "customer_id": { "type": "string" }, "order_total": { "type": "integer" }, "status": { "type": "string" } }, "required": ["order_id", "order_timestamp", "customer_id", "order_total", "status"] } ``` Run: ```bash datacontract import jsonschema --source orders.schema.json ``` to produce the data contract: ```yaml version: 1.0.0 kind: DataContract apiVersion: v3.2.0 id: my-data-contract name: orders status: draft schema: - name: orders physicalType: object businessName: orders logicalType: object physicalName: orders properties: - name: order_id physicalType: string description: Unique identifier of the order. logicalType: string required: true - name: order_timestamp physicalType: string logicalType: string logicalTypeOptions: format: date-time required: true - name: customer_id physicalType: string logicalType: string required: true - name: order_total physicalType: integer logicalType: integer required: true - name: status physicalType: string # … ``` ODCS has no union type. A property that allows more than one type (`"type": ["string", "integer"]`, or several `anyOf`/`oneOf` branches) is imported as `logicalType: string` with the union in `physicalType` (`string|integer`), and the import prints a warning naming it. A single type plus `null` is not a union and imports as that type. Local references (`$ref` to `#/$defs/…` or `#/definitions/…`, also at the root) are resolved, and the branches of an `allOf` are merged: their properties are combined, the schema's own first, and so are their `required` lists. A definition that contains itself stops at its first repetition, as an `object` without properties. References to other documents are not loaded. The import warns about both. `const` becomes an `enum` with that one value, an object with `additionalProperties` but no `properties` becomes a `map`, and `minItems`, `maxItems`, `uniqueItems`, `multipleOf`, and `examples` are kept. Keywords ODCS cannot express, such as `patternProperties`, `if`, `not`, or `dependentRequired`, are left out with a warning that names the properties. All options: **[`datacontract import jsonschema`](../commands/import/jsonschema.md)**. --- ## Import: MySQL ## ![](/img/icons/mysql.svg) Import: MySQL Creates a data contract from a MySQL database by reading `information_schema` — including the full declared column types, nullability, primary keys, and the comments on tables and columns. ```bash datacontract import mysql \ --source localhost \ --database mydb \ --output datacontract.yaml ``` `--source` is the host of your MySQL server. Add `--port` if it doesn't listen on the default `3306`, and repeat `--table` to import only specific tables (by default every table and view in the database is imported). The database is reached the way `datacontract test` reaches it: duckdb attaches it through its `mysql` extension, so no extra driver is needed and the import authenticates with the same variables. The generated contract includes a ready-to-test `servers` block, so you can run `datacontract test datacontract.yaml` immediately afterwards — see the **[MySQL connection guide](../testing/mysql.md)** for the full 5-minute walkthrough and troubleshooting. Credentials are provided as environment variables and are the same ones `datacontract test` uses: `DATACONTRACT_MYSQL_USERNAME` and `DATACONTRACT_MYSQL_PASSWORD` — see the [MySQL Reference](../reference/mysql.md). Only have a DDL file? Use [`datacontract import sql --dialect mysql`](./sql.md). All options: **[`datacontract import mysql`](../commands/import/mysql.md)**. --- ## Import: OData ## ![](/img/icons/odata.svg) Import: OData Creates a data contract from OData 4.x CSDL XML or JSON metadata. Select individual EntitySets, or automatically import all EntitySets advertised in the service document. Both public and authenticated services are supported. ```bash datacontract import odata \ --service-root-url 'https://xmart-api-public-uat.who.int/refmart/' \ --entity-set ref_country \ --output datacontract.yaml ``` This example imports the WHO [country endpoint](https://xmart-api-public-uat.who.int/refmart/ref_country). `--service-root-url` is always required and is stored with a trailing `/` as the server's `location`. Metadata is read from `$metadata` under that root unless you supply `--metadata-url` or `--metadata-file`. Repeat `--entity-set` to select several EntitySets. An explicit selection skips the service document. Without `--entity-set`, the importer reads the JSON service document from the root URL and imports every EntitySet listed there into the same contract. For offline import, supply a local CSDL XML or JSON file through `--metadata-file`. Also supply `--service-document-file` or `--entity-set`. For authentication, set `DATACONTRACT_API_HEADER_AUTHORIZATION` to the complete header value, such as `Bearer ` or a precomputed `Basic `. The importer reads metadata only; no data records are fetched. It preserves primitive field types, nullability, declared keys and constraints. Primitive types without a logical type mapping are imported with their original `physicalType` and produce a warning. Complex types, collection-valued fields, enums, type definitions and inheritance cause errors when used by a selected schema. Navigation properties are omitted, and external metadata references are not downloaded. OData support currently covers import only. Support for `datacontract test` is planned to compare the contract's schema with `$metadata`, without fetching data records. All options: **[`datacontract import odata`](../commands/import/odata.md)**. --- ## Import: ODCS ## ![](/img/icons/odcs.svg) Import: ODCS Imports an existing [ODCS](../open-data-contract-standard.md) data contract file (for normalization or conversion). ```bash datacontract import odcs --source other.odcs.yaml --output datacontract.yaml ``` All options: **[`datacontract import odcs`](../commands/import/odcs.md)**. --- ## Import: OpenAPI ## ![](/img/icons/api.svg) Import: OpenAPI Creates a data contract from the response of one GET or POST operation in an [OpenAPI](https://www.openapis.org/) 3.0 or 3.1 document, in YAML or JSON. You can then [test the endpoint](../testing/api.md) against the contract. ```bash datacontract import openapi --source openapi.yaml --operation listOrders ``` ### What is imported - **Operation**: select the operation with `--operation`, by its `operationId` (`listOrders`), its method and path (`GET /orders`, `POST /orders/search`), or the path of a GET operation (`/orders`). If the document has only one GET operation, `--operation` can be omitted. A POST operation is never selected by its path alone, because a POST may change data: import only POST operations that read, such as a search. - **Schema**: the contract gets one schema, named after the `operationId` (or the path), described by the operation's `summary` or `description`. Its properties come from the response schema of the first success response (`200` first, then other `2xx` codes) with a JSON (`application/json`, `application/*+json`) or YAML (`application/yaml`, `text/yaml`) body. - **Arrays**: if the response is an array, the schema describes its items, which are the records. - **Types**: the response schema is mapped like a [JSON Schema](jsonschema.md). Local `$ref`s (to `#/components/...`) are resolved and `allOf` is merged. OpenAPI 3.0's `example` is imported as `examples`. A property that admits `null` (`nullable: true`, or `type: [string, "null"]`) is not `required`, as ODCS `required` means not null. - **Servers**: every absolute `servers` URL becomes an `api` server whose `location` is the endpoint's URL. Server variables take their `default`. Relative server URLs (such as `/v1`) are skipped with a warning, because they cannot be tested. - **Parameters**: path parameters become [variables](../configuration.md#variables-in-the-data-contract) in the `location`, and required query parameters variables in the server's `params` custom property, defaulting to the parameter's `example` or `default`. For example, `/orders/{orderId}` with `example: ORD-1001` becomes `/orders/${orderId:-ORD-1001}`. Set `orderId` in the environment to test another order. - **Request body**: for a POST operation, the server gets `method: POST` and a `body` custom property with the request body's JSON example: the media type's `example`, else its first entry in `examples`, else the schema's `example`. Without one, the contract has no `body` and a warning asks you to add it. Swagger 2.0 documents are not supported, and `$ref`s to other files are not followed. ### Example Given this [`orders.yaml`](https://github.com/datacontract/datacontract-cli/blob/main/examples/imports/openapi/orders.yaml): ```yaml openapi: 3.1.0 info: title: Orders API version: 1.0.0 servers: - url: https://api.example.com/v1 description: Production - url: https://staging.api.example.com/v1 description: Staging paths: /orders: get: operationId: listOrders summary: All orders of the last 30 days. responses: "200": description: The orders. content: application/json: schema: type: array items: $ref: "#/components/schemas/Order" /orders/{orderId}: get: operationId: getOrder parameters: - name: orderId in: path required: true example: ORD-1001 schema: type: string responses: "200": description: The order. content: application/json: schema: $ref: "#/components/schemas/Order" components: schemas: Order: type: object required: [order_id, order_timestamp, status] properties: order_id: type: string description: Unique identifier of the order. pattern: "^ORD-[0-9]+$" order_timestamp: type: string format: date-time customer_id: type: string order_total: type: integer minimum: 0 status: type: string enum: [placed, shipped, delivered] ``` Run: ```bash datacontract import openapi --source orders.yaml --operation listOrders ``` to produce the data contract: ```yaml version: 1.0.0 kind: DataContract apiVersion: v3.2.0 id: my-data-contract name: Orders API status: draft servers: - server: production type: api description: Production location: https://api.example.com/v1/orders - server: staging type: api description: Staging location: https://staging.api.example.com/v1/orders schema: - name: listOrders physicalType: object description: All orders of the last 30 days. logicalType: object physicalName: listOrders properties: - name: order_id physicalType: string description: Unique identifier of the order. logicalType: string logicalTypeOptions: pattern: ^ORD-[0-9]+$ required: true - name: order_timestamp physicalType: string logicalType: string logicalTypeOptions: format: date-time # … ``` All options: **[`datacontract import openapi`](../commands/import/openapi.md)**. --- ## Import: Oracle ## ![](/img/icons/oracle.svg) Import: Oracle Creates a data contract from an Oracle database by reading `ALL_TAB_COLUMNS` — including column types with length and precision, nullability, and primary keys. ```bash datacontract import oracle \ --source localhost \ --service-name XEPDB1 \ --schema ADMIN \ --output datacontract.yaml ``` `--source` is the host of your Oracle database and `--service-name` its service. Add `--port` if it doesn't listen on the default `1521`, and repeat `--table` to import only specific tables. Oracle stores identifiers in upper case, so `--schema` is upper-cased for you. Types are reconstructed exactly as the test path reads them back. Oracle reports a `DATA_LENGTH` for *every* column, but it is only part of the declared type for character and raw types — so a `DATE` stays `DATE` rather than becoming `DATE(7)`. The generated contract includes a ready-to-test `servers` block, so you can run `datacontract test datacontract.yaml` immediately afterwards — see the **[Oracle connection guide](../testing/oracle.md)** for the full 5-minute walkthrough and troubleshooting. Credentials are the same ones `datacontract test` uses: `DATACONTRACT_ORACLE_USERNAME` and `DATACONTRACT_ORACLE_PASSWORD` — see the [Oracle Reference](../reference/oracle.md). Only have a DDL script? Use [`datacontract import sql --dialect oracle`](./sql.md). All options: **[`datacontract import oracle`](../commands/import/oracle.md)**. --- ## Import: Parquet ## ![](/img/icons/parquet.svg) Import: Parquet Creates a data contract from the schema of a Parquet file. ```bash datacontract import parquet --source data.parquet --output datacontract.yaml ``` All options: **[`datacontract import parquet`](../commands/import/parquet.md)**. --- ## Import: Postgres ## ![](/img/icons/postgres.svg) Import: Postgres Creates a data contract from a Postgres schema. Tables and columns are read from `information_schema`, comments from `pg_description`, and key metadata from `pg_catalog`. Works with Postgres and Postgres-compatible databases (e.g. RisingWave). ```bash datacontract import postgres \ --source localhost \ --database postgres \ --schema public \ --output datacontract.yaml ``` `--source` is the host of your Postgres server. Add `--port` if it doesn't listen on the default `5432`, and repeat `--table` to import only specific tables (by default every table and view in the schema is imported). `--schema` defaults to `public`. The generated contract includes a ready-to-test `servers` block, so you can run `datacontract test datacontract.yaml` immediately afterwards — see the **[Postgres connection guide](../testing/postgres.md)** for the full 5-minute walkthrough and troubleshooting. Credentials are provided as environment variables and are the same ones `datacontract test` uses: `DATACONTRACT_POSTGRES_USERNAME` and `DATACONTRACT_POSTGRES_PASSWORD` — see the [Postgres Reference](../reference/postgres.md). ### Key metadata and privileges Primary keys and foreign keys are read from the Postgres catalog, so a role with `SELECT` on the tables and `USAGE` on the schema is enough. A foreign key is included only when both of its tables are imported from the same schema. Working from a DDL file instead of a live database? Use [`datacontract import sql --dialect postgres`](./sql.md). All options: **[`datacontract import postgres`](../commands/import/postgres.md)**. --- ## Import: Power BI ## ![](/img/icons/powerbi.svg) Import: Power BI Creates a data contract from a Power BI semantic model (`.pbit`, `.bim`, or `.json`). ```bash datacontract import powerbi --source model.pbit --output datacontract.yaml ``` All options: **[`datacontract import powerbi`](../commands/import/powerbi.md)**. --- ## Import: Protobuf ## ![](/img/icons/custom.svg) Import: Protobuf Creates a data contract from a [Protobuf](https://protobuf.dev/) `.proto` schema file. Given this [`orders.proto`](https://github.com/datacontract/datacontract-cli/blob/main/examples/imports/protobuf/orders.proto): ```protobuf syntax = "proto3"; package checkout; // One row per customer order. message Orders { string order_id = 1; string order_timestamp = 2; string customer_id = 3; int64 order_total = 4; string status = 5; } ``` Run: ```bash datacontract import protobuf --source orders.proto ``` to produce the data contract: ```yaml version: 1.0.0 kind: DataContract apiVersion: v3.2.0 id: my-data-contract name: My Data Contract status: draft schema: - name: Orders physicalType: message description: Details of Orders. logicalType: object physicalName: Orders properties: - name: order_id physicalType: '9' description: Field order_id logicalType: string required: false - name: order_timestamp physicalType: '9' description: Field order_timestamp logicalType: string required: false - name: customer_id physicalType: '9' description: Field customer_id logicalType: string required: false - name: order_total physicalType: '3' description: Field order_total logicalType: integer required: false - name: status # … ``` All options: **[`datacontract import protobuf`](../commands/import/protobuf.md)**. --- ## Import: Pydantic Model ## ![](/img/icons/pydantic.svg) Import: Pydantic Model Creates a data contract from [Pydantic](https://docs.pydantic.dev/) model classes, the counterpart to [`datacontract export pydantic-model`](../exports/pydantic-model.md). The module is read statically with Python's `ast`, never imported, so a contract can be derived from a service's models without installing that service's dependencies or executing any of its code. Module-level models become schema objects, except those used as the type of another model's field: those are inlined as nested objects. `Field(...)` constraints become `logicalTypeOptions`, and `Literal` types and `Enum` classes become an `invalidValues` quality rule listing the valid values. Given this [`orders.py`](https://github.com/datacontract/datacontract-cli/blob/main/examples/imports/pydantic-model/orders.py): ```python import datetime from enum import Enum from typing import Optional from pydantic import BaseModel, Field class Status(str, Enum): PENDING = "pending" SHIPPED = "shipped" DELIVERED = "delivered" class Orders(BaseModel): """One row per customer order.""" order_id: str = Field(max_length=32) order_timestamp: datetime.datetime customer_id: Optional[str] = None order_total: int = Field(description="Order total in cents.", ge=0) status: Status ``` Run: ```bash datacontract import pydantic-model --source orders.py ``` to produce the data contract: ```yaml version: 1.0.0 kind: DataContract apiVersion: v3.2.0 id: my-data-contract name: My Data Contract status: draft schema: - name: Orders physicalType: object description: One row per customer order. logicalType: object physicalName: Orders properties: - name: order_id physicalType: str logicalType: string logicalTypeOptions: maxLength: 32 required: true - name: order_timestamp physicalType: datetime logicalType: timestamp required: true - name: customer_id physicalType: str logicalType: string required: false - name: order_total physicalType: int description: Order total in cents. logicalType: integer logicalTypeOptions: minimum: 0 required: true # … ``` All options: **[`datacontract import pydantic-model`](../commands/import/pydantic-model.md)**. --- ## Import: Amazon Redshift ## ![](/img/icons/redshift.svg) Import: Amazon Redshift Creates a data contract from an Amazon Redshift schema by reading table metadata from the `SVV_TABLES` and `SVV_COLUMNS` catalog views — including column types with length and precision, nullability, primary keys, and comments. Works with provisioned clusters and Redshift Serverless, and covers local tables, views, and external (Spectrum) tables. ```bash datacontract import redshift \ --source my-workgroup.123456789012.us-east-1.redshift-serverless.amazonaws.com \ --database dev \ --schema analytics \ --output datacontract.yaml ``` `--source` is the endpoint host of your cluster or serverless workgroup. Add `--port` if it doesn't listen on the default `5439`, and repeat `--table` to import only specific tables (by default every table in the schema is imported). The generated contract includes a ready-to-test `servers` block, so you can run `datacontract test datacontract.yaml` immediately afterwards — see the **[Redshift connection guide](../testing/redshift.md)** for credentials, the full 5-minute walkthrough, and troubleshooting. Credentials are provided as environment variables and are the same ones `datacontract test` uses: either IAM (`DATACONTRACT_REDSHIFT_AUTHENTICATION=iam`, which requests temporary credentials from your AWS session) or a database user (`DATACONTRACT_REDSHIFT_USERNAME`, `DATACONTRACT_REDSHIFT_PASSWORD`) — see the [Redshift Reference](../reference/redshift.md). All options: **[`datacontract import redshift`](../commands/import/redshift.md)**. --- ## Import: Amazon S3 ## ![](/img/icons/s3.svg) Import: Amazon S3 Creates a data contract from files in S3, in CSV, JSON, Parquet, or Delta format. Works with S3 and any S3-compatible endpoint (e.g. MinIO). ```bash datacontract import s3 \ --source s3://my-bucket/orders/*.json \ --output datacontract.yaml ``` The format is taken from the file suffix; pass `--format` for Delta tables, which have none, or to override the guess. Add `--endpoint-url` for an S3-compatible store, and `--delimiter` to pin how a JSON file is laid out (`new_line`, `array`, or `none`) instead of letting it be detected. The objects are read with duckdb through the same connection the test path uses, so the import authenticates identically and infers the column types from exactly the reader that later verifies them. The generated contract includes a ready-to-test `servers` block, so you can run `datacontract test datacontract.yaml` immediately afterwards — see the **[S3 connection guide](../testing/s3.md)** for the full 5-minute walkthrough and troubleshooting. Credentials are the same ones `datacontract test` uses: an existing AWS session, or `DATACONTRACT_S3_ACCESS_KEY_ID` and `DATACONTRACT_S3_SECRET_ACCESS_KEY` — see the [S3 Reference](../reference/s3.md). Working from a local file instead? Use [`datacontract import csv`](./csv.md), [`json`](./json.md), or [`parquet`](./parquet.md). All options: **[`datacontract import s3`](../commands/import/s3.md)**. --- ## Import: Snowflake ## ![](/img/icons/snowflake.svg) Import: Snowflake Creates a data contract from a Snowflake workspace by reading table metadata from `INFORMATION_SCHEMA` — including schemas, column types, primary keys, and comments. ```bash datacontract import snowflake \ --source - \ --database ORDER_DB \ --schema PUBLIC \ --output datacontract.yaml ``` `--source` is your [account identifier](https://docs.snowflake.com/en/user-guide/admin-account-identifier). The generated contract includes a ready-to-test `servers` block, so you can run `datacontract test datacontract.yaml` immediately afterwards — see the **[Snowflake connection guide](../testing/snowflake.md)** for credentials, the full 5-minute walkthrough, and troubleshooting. Credentials are provided as environment variables (`DATACONTRACT_SNOWFLAKE_USERNAME`, `DATACONTRACT_SNOWFLAKE_PASSWORD`, `DATACONTRACT_SNOWFLAKE_ROLE`, `DATACONTRACT_SNOWFLAKE_WAREHOUSE`). If no password is set, the import falls back to browser-based SSO. Key-pair auth and `connections.toml` are also supported — see the [Snowflake Reference](../reference/snowflake.md). All options: **[`datacontract import snowflake`](../commands/import/snowflake.md)**. --- ## Import: Spark ## ![](/img/icons/spark.svg) Import: Spark Creates a data contract from a Spark schema. This importer is typically used programmatically from within a Spark context. It needs PySpark, which the CLI does not install (you supply the Spark session), and the `databricks` extra: ```bash pip install pyspark 'datacontract-cli[databricks]' ``` Import tables or views registered in the current Spark session: ```bash datacontract import spark --tables orders,line_items --output datacontract.yaml ``` Or import a DataFrame from Python, naming the schema with `source`: ```python from datacontract.data_contract import DataContract odcs = DataContract.import_from_source("spark", source="orders", dataframe=df) ``` A table description can be supplied alongside the table or dataframe to enrich the generated contract. All options: **[`datacontract import spark`](../commands/import/spark.md)**. --- ## Import: SQL DDL ## ![](/img/icons/database.svg) Import: SQL Creates a data contract from a SQL DDL file. Pass the SQL dialect with `--dialect`. Given this [`orders.sql`](https://github.com/datacontract/datacontract-cli/blob/main/examples/imports/sql/orders.sql): ```sql CREATE TABLE orders ( order_id TEXT PRIMARY KEY, order_timestamp TIMESTAMPTZ NOT NULL, customer_id TEXT NOT NULL, order_total NUMERIC NOT NULL, status TEXT NOT NULL ); ``` Run: ```bash datacontract import sql --source orders.sql --dialect postgres ``` to produce the data contract: ```yaml version: 1.0.0 kind: DataContract apiVersion: v3.2.0 id: my-data-contract name: My Data Contract status: draft servers: - server: postgres type: postgres database: my_database host: my_host port: 5432 schema: public schema: - name: orders physicalType: table logicalType: object physicalName: orders properties: - name: order_id physicalType: TEXT primaryKey: true primaryKeyPosition: 1 logicalType: string required: true unique: true - name: order_timestamp physicalType: TIMESTAMPTZ logicalType: timestamp required: true - name: customer_id physicalType: TEXT logicalType: string required: true # … ``` All options: **[`datacontract import sql`](../commands/import/sql.md)**. --- ## Import: SQL Server ## ![](/img/icons/sqlserver.svg) Import: SQL Server Creates a data contract from a SQL Server database by reading `information_schema` — including column types with length and precision, nullability, and primary keys. ```bash datacontract import sqlserver \ --source localhost \ --database mydb \ --schema dbo \ --output datacontract.yaml ``` `--source` is the host of your SQL Server instance. Add `--port` if it doesn't listen on the default `1433`, repeat `--table` to import only specific tables, and set `--schema` if your tables don't live in `dbo`. The connection uses the same helper as `datacontract test`, so every authentication mode works for the import too: SQL logins, Windows integrated auth, the Entra ID modes, and `az login`. Types are reconstructed exactly as the test path reads them back, so `varchar(36)`, `decimal(10,2)`, `datetime2` and `varchar(max)` all match on the first run. The generated contract includes a ready-to-test `servers` block, so you can run `datacontract test datacontract.yaml` immediately afterwards — see the **[SQL Server connection guide](../testing/sqlserver.md)** for the full 5-minute walkthrough and troubleshooting. Credentials are the same ones `datacontract test` uses: `DATACONTRACT_SQLSERVER_USERNAME` and `DATACONTRACT_SQLSERVER_PASSWORD` — see the [SQL Server Reference](../reference/sqlserver.md). Only have a DDL script? Use [`datacontract import sql --dialect sqlserver`](./sql.md). All options: **[`datacontract import sqlserver`](../commands/import/sqlserver.md)**. --- ## Import: Trino ## ![](/img/icons/trino.svg) Import: Trino Creates a data contract from a Trino catalog by reading `information_schema`, including the declared column types and nullability. ```bash datacontract import trino \ --source localhost \ --catalog my_catalog \ --schema my_schema \ --output datacontract.yaml ``` `--source` is the host of your Trino coordinator. Add `--port` if it doesn't listen on the default `8080`, and repeat `--table` to import only specific tables. Trino reports a complete type string, so `varchar(36)`, `decimal(10,2)` and `array(varchar)` are taken verbatim and match what `datacontract test` reads back. The generated contract includes a ready-to-test `servers` block, so you can run `datacontract test datacontract.yaml` immediately afterwards — see the **[Trino connection guide](../testing/trino.md)** for the full 5-minute walkthrough and troubleshooting. Credentials are the same ones `datacontract test` uses — see the [Trino Reference](../reference/trino.md). Only have a DDL script? Use [`datacontract import sql --dialect postgres`](./sql.md); Trino's ANSI-style DDL generally parses with that dialect. All options: **[`datacontract import trino`](../commands/import/trino.md)**. --- ## Import: Unity Catalog This importer is now documented as **[Import: Databricks](./databricks.md)**. The `unity` format name still works. --- ## Import: XML Schema ## ![](/img/icons/custom.svg) Import: XML Schema Creates a data contract from an [XML Schema](https://www.w3.org/XML/Schema) (XSD) file. Given this [`orders.xsd`](https://github.com/datacontract/datacontract-cli/blob/main/examples/imports/xsd/orders.xsd): ```xml Unique identifier of the order. ``` Run: ```bash datacontract import xsd --source orders.xsd ``` to produce the data contract: ```yaml version: 1.0.0 kind: DataContract apiVersion: v3.2.0 id: my-data-contract name: orders status: draft schema: - name: orders physicalType: object logicalType: object physicalName: orders properties: - name: order_id physicalType: string description: Unique identifier of the order. logicalType: string required: true - name: order_timestamp physicalType: dateTime logicalType: timestamp required: true - name: customer_id physicalType: string logicalType: string required: true - name: order_total physicalType: integer logicalType: integer required: true - name: status physicalType: string logicalType: string required: true ``` ### Install The import needs the `xml` extra, which brings [xmlschema](https://pypi.org/project/xmlschema/) to resolve includes, imports, groups, and type derivation: ```bash pip install 'datacontract-cli[xml]' ``` ### Schemas Each global element that no other element references becomes a schema, named after the element; its child elements and attributes become the schema's properties. A global element used only through `ref` is part of the element that references it, not a schema of its own. The contract is named after the single root element, or after the file when there are several. ### Mapping | XML Schema | Data contract | |---|---| | Global element | Schema with `physicalType: object` | | Element with a complex type | `logicalType: object` with `properties` | | Element with `maxOccurs` greater than 1, or in an `xs:sequence` or `xs:choice` that repeats | `logicalType: array` with `items`; an element's own `minOccurs`/`maxOccurs` above 1 as `minItems`/`maxItems` | | `minOccurs="0"`, `nillable="true"`, inside `xs:choice` or an optional `xs:sequence` | not `required` | | `xs:string`, `xs:token`, `xs:anyURI`, `xs:duration`, `xs:gYear`, … | `string` | | `xs:int`, `xs:long`, `xs:integer`, `xs:positiveInteger`, `xs:unsignedByte`, … | `integer`, with the range of the type as `minimum`/`maximum` (`xs:unsignedInt`: 0 to 4294967295) unless a facet narrows it; `xs:unsignedLong` gets only its minimum, as its maximum is beyond the 64-bit integers the checks compare with. Sized types also get their ODCS `format`: `xs:byte` `i8`, `xs:short` `i16`, `xs:int` `i32`, `xs:long` `i64`, and `u8` to `u64` for the unsigned ones | | `xs:decimal`, `xs:float`, `xs:double` | `number`; `xs:float` and `xs:double` with the `format` `f32` and `f64` | | `xs:boolean` | `boolean` | | `xs:date` / `xs:dateTime` / `xs:time` | `date` / `timestamp` / `time` | | `xs:enumeration` | `enum` | | `xs:pattern` (several are alternatives) | `logicalTypeOptions.pattern`, anchored as `^(…)$` because XSD patterns match the whole value | | `xs:length`, `xs:minLength`, `xs:maxLength` | `logicalTypeOptions.minLength` / `maxLength` | | `xs:minInclusive`, `xs:maxInclusive`, `xs:minExclusive`, `xs:maxExclusive` | `logicalTypeOptions.minimum` / `maximum` / `exclusiveMinimum` / `exclusiveMaximum`; `INF` bounds nothing and is left out | | `fixed` on an element or attribute | `enum` with that one value | | `xs:totalDigits`, `xs:fractionDigits` | `precision` / `scale` custom properties, which the SQL exports read too (ODCS has no field for them) | | `xs:union` | `string`, with the member types in `physicalType` (`date\|string\|integer`) | | `xs:list` | `string` with `physicalType: list`: one space-separated text value | | `xs:documentation` | `description` | Every property keeps the built-in XSD type it derives from in `physicalType`, such as `dateTime` or `positiveInteger`. A user-defined simple type contributes its facets and those of the types it derives from; a complex type contributes everything it inherits through `xs:extension`, `xs:restriction`, `xs:group`, and `xs:attributeGroup`. A type that contains itself stops at its first repetition, as an `object` without properties. ### Attributes, text, and namespaces ODCS has no notion of attributes, so the import marks them with custom properties. Given an element with an attribute and text, in a target namespace: ```xml ``` the import creates: ```yaml schema: - name: product physicalType: object customProperties: - property: xmlNamespace value: urn:example:shop logicalType: object physicalName: product properties: - name: price physicalType: object logicalType: object required: true properties: - name: value physicalType: decimal customProperties: - property: xmlNode value: text logicalType: number - name: currency physicalType: string customProperties: - property: xmlNode value: attribute logicalType: string required: true - name: sku physicalType: string customProperties: - property: xmlNode value: attribute logicalType: string required: true ``` - `xmlNode: attribute` marks a property that is an attribute rather than a child element. - `xmlNode: text` marks the text of an element that has attributes; it is always named `value`. - `xmlNamespace` on a schema holds the target namespace of its element. On a property, it holds the namespace of an element that is not in the schema's, and `""` an element in no namespace, such as the local elements of a schema without `elementFormDefault="qualified"`. - An attribute named like a child element of the same element, or `value` next to the element's text, gets an `@` prefix (`@id`), with a warning. [Testing XML files](../testing/xml.md) reads the text through `xmlNode: text`, and [`datacontract export xsd`](../exports/xsd.md) turns all three back into attributes, text content, and a target namespace, so a contract exports back to an equivalent XML Schema. ### Includes and imports `xs:include`, `xs:import`, and `xs:redefine` are followed for local files, relative to the schema that names them. Remote schemas (`http://`, `https://`) are never downloaded: they are skipped with a warning, and an element of a type they define becomes an `object` without properties. Rules of XML Schema the import does not depend on, such as an invalid restriction, are reported as warnings instead of failing the import. ### Simplifications ODCS cannot express everything an XML Schema can. The import warns about each simplification it makes and names the properties concerned, for example: ``` These properties are unions; ODCS has no union type, so they are strings with the member types in physicalType: order.reference These properties contain their own type, so the repetition is an object without properties: category.parent ``` It warns about recursive types, unions, lists, wildcards, mixed content, identity constraints, substitution groups, XSD 1.1 assertions, attributes renamed with an `@` prefix, and patterns with syntax only XSD has (`\i`, `\c`, character class subtraction), which are left out because `datacontract test` cannot run them. ### Not imported - Wildcards (`xs:any`, `xs:anyAttribute`) and the text of mixed content. - Identity constraints (`xs:key`, `xs:keyref`, `xs:unique`) and default or fixed values. - Substitution groups: an element is imported as declared, not with its substitutes. - XSD 1.1 assertions (`xs:assert`, `xs:assertion`). XSD 1.1 schemas are read, but their assertions are not imported. All options: **[`datacontract import xsd`](../commands/import/xsd.md)**. --- ## Exports `datacontract export` converts a data contract to a target format. Use it to generate DDL, schemas, models, documentation, and data-quality artifacts directly from the contract. ```bash datacontract export html datacontract.yaml --output datacontract.html ``` Run `datacontract export --help` to see the format-specific options (e.g. `datacontract export sql --help`). If a format you need is missing, [open an issue on GitHub](https://github.com/datacontract/datacontract-cli/issues). For SQL dialects (`postgres`, `mysql`, `snowflake`, `databricks`, `sqlserver`, `trino`, `oracle`, `clickhouse`), use `datacontract export sql --dialect `. ### Example contract The examples on each export page are generated from the same data contract, so you can compare formats side by side: - [`examples/orders/orders.odcs.yaml`](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) — an `orders` and `line_items` schema with a Snowflake and a BigQuery server. Used by most pages. - [`examples/user_interactions/user_interactions.odcs.yaml`](https://github.com/datacontract/datacontract-cli/blob/main/examples/user_interactions/user_interactions.odcs.yaml) — a Databricks contract with DQX quality rules. Used by the [`dqx`](./dqx.md) page. Download a file and run the commands below against it to reproduce the output. ### Available exporters ![](/img/icons/avro.svg) avroAvro schema. ![](/img/icons/avro.svg) avro-idlAvro IDL. ![](/img/icons/bigquery.svg) bigqueryBigQuery schema. ![](/img/icons/custom.svg) customAny format, via a custom Jinja template. ![](/img/icons/data-caterer.svg) data-catererData Caterer data-generation task. ![](/img/icons/dbml.svg) dbmlDBML. ![](/img/icons/dbt.svg) dbt-modelsdbt model schema YAML. ![](/img/icons/dbt.svg) dbt-sourcesdbt sources YAML. ![](/img/icons/dbt.svg) dbt-staging-sqlA dbt staging SQL file. ![](/img/icons/custom.svg) dcsDCS format. ![](/img/icons/dqx.svg) dqxDatabricks DQX checks. ![](/img/icons/excel.svg) excelODCS Excel template. ![](/img/icons/go.svg) goGo structs. ![](/img/icons/great-expectations.svg) great-expectationsGreat Expectations suite. ![](/img/icons/custom.svg) htmlA standalone HTML page. ![](/img/icons/iceberg.svg) icebergIceberg schema. ![](/img/icons/jsonschema.svg) jsonschemaJSON Schema. ![](/img/icons/markdown.svg) markdownMarkdown documentation. ![](/img/icons/mermaid.svg) mermaidA Mermaid diagram. ![](/img/icons/odcs.svg) odcsODCS format. ![](/img/icons/custom.svg) protobufProtobuf schema. ![](/img/icons/pydantic.svg) pydantic-modelA Pydantic model. ![](/img/icons/rdf.svg) rdfRDF representation. ![](/img/icons/soda.svg) sodaclSodaCL checks. ![](/img/icons/spark.svg) sparkSpark StructType schema. ![](/img/icons/database.svg) sqlSQL DDL (CREATE TABLE). ![](/img/icons/database.svg) sql-queryA SQL SELECT query. ![](/img/icons/sqlalchemy.svg) sqlalchemySQLAlchemy models. ![](/img/icons/custom.svg) xsdAn XML Schema (XSD). See the [`export` command reference](../commands/export/index.md) for the common signature. --- ## Export: Avro IDL ## ![](/img/icons/avro.svg) Export: Avro IDL Converts the data contract to [Avro IDL](https://avro.apache.org/docs/1.11.1/idl-language/). ```bash datacontract export avro-idl orders.odcs.yaml --output orders.avdl ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```text /** Tracks customer orders and their line items for analytics and reporting. */ protocol Orders { /** One row per customer order. */ record orders { /** Unique identifier of the order. */ string order_id; /** Timestamp when the order was placed. */ string order_timestamp; /** Reference to the customer who placed the order. */ string customer_id; /** Total amount of the order in cents. */ int order_total; /** Current fulfilment status of the order. */ string status; } /** One row per line item within an order. */ record line_items { /** Unique identifier of the line item. */ string line_item_id; /** Reference to the parent order. */ string order_id; /** Stock keeping unit of the purchased product. */ string sku; /** Number of units purchased. */ int quantity; } } ``` All options: **[`datacontract export avro-idl`](../commands/export/avro-idl.md)**. --- ## Export: Avro ## ![](/img/icons/avro.svg) Export: Avro Converts the data contract into an Avro schema. It supports specifying custom Avro properties for logical types and default values. ```bash datacontract export avro orders.odcs.yaml --schema-name orders --output orders.avsc ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```json { "type": "record", "name": "orders", "doc": "One row per customer order.", "fields": [ { "name": "order_id", "doc": "Unique identifier of the order.", "type": "string" }, { "name": "order_timestamp", "doc": "Timestamp when the order was placed.", "type": { "type": "long", "logicalType": "timestamp-millis" } }, { "name": "customer_id", "doc": "Reference to the customer who placed the order.", "type": "string" }, { "name": "order_total", "doc": "Total amount of the order in cents.", "type": "int" }, { "name": "status", "doc": "Current fulfilment status of the order.", "type": "string" } ] } ``` ### Custom Avro properties **`customProperties`** may set additional Avro attributes. At the moment, [`logicalType`](https://avro.apache.org/docs/1.11.0/spec.html#Logical+Types), `default` and [`aliases`](https://avro.apache.org/docs/1.11.0/spec.html#Aliases) are supported. ```yaml schema: - name: orders properties: - name: my_field_1 description: Example for AVRO with Timestamp (microsecond precision) logicalType: integer physicalType: long examples: - 1672534861000000 # 2023-01-01 01:01:01 in microseconds required: true customProperties: - property: avroLogicalType value: local-timestamp-micros - property: avroDefault value: "1672534861000000" ``` - `avroLogicalType` — the Avro logical type of the property (here `local-timestamp-micros`). - `avroDefault` — the default value for the property in Avro, as a string; `true`, `false`, `null` and numbers are converted to their Avro type. - `avroAliases` — the field's alternate names, as a list. On the schema object, it sets the aliases of the top-level record. All options: **[`datacontract export avro`](../commands/export/avro.md)**. --- ## Export: BigQuery ## ![](/img/icons/bigquery.svg) Export: BigQuery Converts the data contract to a [BigQuery table schema](https://cloud.google.com/bigquery/docs/schemas) JSON. ```bash datacontract export bigquery orders.odcs.yaml --schema-name orders --server bigquery --output orders.bigquery.json ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```json { "kind": "bigquery#table", "tableReference": { "datasetId": "orders", "projectId": "my-gcp-project", "tableId": "orders" }, "description": "One row per customer order.", "schema": { "fields": [ { "name": "order_id", "type": "STRING", "mode": "REQUIRED", "description": "Unique identifier of the order.", "maxLength": null }, { "name": "order_timestamp", "type": "TIMESTAMP", "mode": "REQUIRED", "description": "Timestamp when the order was placed." }, { "name": "customer_id", "type": "STRING", "mode": "REQUIRED", "description": "Reference to the customer who placed the order.", "maxLength": null }, { "name": "order_total", "type": "INTEGER", "mode": "REQUIRED", "description": "Total amount of the order in cents." }, { "name": "status", "type": "STRING", "mode": "REQUIRED", "description": "Current fulfilment status of the order.", "maxLength": null } ] } } ``` All options: **[`datacontract export bigquery`](../commands/export/bigquery.md)**. --- ## Export: Custom (Jinja) ## ![](/img/icons/custom.svg) Export: Custom (Jinja) Converts the data contract into any custom format using a [Jinja](https://jinja.palletsprojects.com/) template. Specify the template path with `--template`. ```bash datacontract export custom --template template.txt orders.odcs.yaml ``` ### Template variables You can use the ODCS object directly. `data_contract` is an `OpenDataContractStandard` instance; top-level fields include `name`, `id`, `version`, `schema_` (the list of schemas), `servers`, `team`, etc. Given this `template.txt`: ```text title: {{ data_contract.name }} schemas: {%- for schema in data_contract.schema_ %} - name: {{ schema.name }} {%- endfor %} ``` Running it against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```text title: Orders schemas: - name: orders - name: line_items ``` ### Per-schema templates Add `--schema-name` to render a single schema. The template then also receives: - `schema_name` (str) - `schema` (the `SchemaObject` from ODCS) ```bash datacontract export custom datacontract.odcs.yaml --template template.sql --schema-name orders ``` All options: **[`datacontract export custom`](../commands/export/custom.md)**. --- ## Export: Data Caterer ## ![](/img/icons/data-caterer.svg) Export: Data Caterer Converts the data contract to a data-generation task in YAML that can be ingested by [Data Caterer](https://github.com/data-catering/data-caterer). This lets you generate production-like data in any environment based on your contract. ```bash datacontract export data-caterer orders.odcs.yaml ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```yaml name: Orders steps: - name: orders type: snowflake options: {} fields: - name: order_id type: string options: isUnique: true isPrimaryKey: true - name: order_timestamp type: timestamp - name: customer_id type: string - name: order_total type: integer - name: status type: string - name: line_items type: snowflake options: {} fields: - name: line_item_id type: string options: isUnique: true isPrimaryKey: true - name: order_id type: string - name: sku type: string - name: quantity type: integer ``` You can further customize generation by adding [additional metadata in the YAML](https://data.catering/latest/docs/generator/data-generator/). All options: **[`datacontract export data-caterer`](../commands/export/data-caterer.md)**. --- ## Export: DBML ## ![](/img/icons/dbml.svg) Export: DBML Converts the data contract to [DBML](https://dbml.dbdiagram.io/) (Database Markup Language). ```bash datacontract export dbml orders.odcs.yaml --output orders.dbml ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```text Project "Orders" { note: '''Tracks customer orders and their line items for analytics and reporting.''' } Table orders { note: "One row per customer order." order_id string [pk, unique, not null, note: "Unique identifier of the order."] order_timestamp timestamp [not null, note: "Timestamp when the order was placed."] customer_id string [not null, note: "Reference to the customer who placed the order."] order_total integer [not null, note: "Total amount of the order in cents."] status string [not null, note: "Current fulfilment status of the order."] } Table line_items { note: "One row per line item within an order." line_item_id string [pk, unique, not null, note: "Unique identifier of the line item."] order_id string [not null, note: "Reference to the parent order."] sku string [not null, note: "Stock keeping unit of the purchased product."] quantity integer [not null, note: "Number of units purchased."] } ``` If a server is selected via `--server`, the logical data types are converted to that database's specific types (based on the server `type`). If no server is selected, the logical data types are exported. All options: **[`datacontract export dbml`](../commands/export/dbml.md)**. --- ## Export: dbt Models ## ![](/img/icons/dbt.svg) Export: dbt Models Converts the data contract to dbt models in YAML format, with support for SQL dialects. ```bash datacontract export dbt-models orders.odcs.yaml ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces (excerpt): ```yaml version: 2 models: - name: orders config: meta: data_contract: urn:datacontract:checkout:orders materialized: table contract: enforced: true description: One row per customer order. columns: - name: order_id data_type: VARCHAR description: Unique identifier of the order. constraints: - type: not_null - type: unique - name: order_timestamp data_type: TIMESTAMP_TZ description: Timestamp when the order was placed. constraints: - type: not_null - name: customer_id data_type: VARCHAR description: Reference to the customer who placed the order. constraints: - type: not_null - name: order_total data_type: NUMBER description: Total amount of the order in cents. constraints: - type: not_null - name: status data_type: VARCHAR description: Current fulfilment status of the order. constraints: - type: not_null - name: line_items config: meta: # … ``` If a server is selected via `--server` (based on its `type`), the dbt column `data_types` match the expected data types of that server. If no server is selected, it defaults to `snowflake`. A property's `classification` and its `meta` custom property are written to the column's `config.meta`, its tags to `config.tags`. The generated tests need dbt 1.10 or later. See the [dbt Integration](../dbt.md) guide for the full picture, including `datacontract dbt sync`. All options: **[`datacontract export dbt-models`](../commands/export/dbt-models.md)**. --- ## Export: dbt Sources ## ![](/img/icons/dbt.svg) Export: dbt Sources Converts the data contract to a dbt `sources` YAML definition. ```bash datacontract export dbt-sources orders.odcs.yaml ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces (excerpt): ```yaml version: 2 sources: - name: urn:datacontract:checkout:orders description: Tracks customer orders and their line items for analytics and reporting. tables: - name: orders description: One row per customer order. columns: - name: order_id data_tests: - not_null - unique data_type: VARCHAR description: Unique identifier of the order. - name: order_timestamp data_tests: - not_null data_type: TIMESTAMP_TZ description: Timestamp when the order was placed. - name: customer_id data_tests: - not_null data_type: VARCHAR description: Reference to the customer who placed the order. - name: order_total data_tests: - not_null data_type: NUMBER description: Total amount of the order in cents. - name: status data_tests: - not_null data_type: VARCHAR description: Current fulfilment status of the order. - name: line_items description: One row per line item within an order. columns: - name: line_item_id data_tests: - not_null # … ``` As with [`dbt-models`](./dbt-models.md), selecting a server maps logical types to that server's data types; otherwise `snowflake` is used. A property's `classification` and its `meta` custom property are written to the column's `config.meta`, its tags to `config.tags`. The generated tests need dbt 1.10 or later. All options: **[`datacontract export dbt-sources`](../commands/export/dbt-sources.md)**. --- ## Export: dbt Staging SQL ## ![](/img/icons/dbt.svg) Export: dbt Staging SQL Generates a dbt staging SQL file that selects the contract's fields. ```bash datacontract export dbt-staging-sql orders.odcs.yaml --schema-name orders ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```sql select order_id, order_timestamp, customer_id, order_total, status from {{ source('urn:datacontract:checkout:orders', 'orders') }} ``` For fully customized staging models, use the [`custom`](./custom.md) exporter with a Jinja template. All options: **[`datacontract export dbt-staging-sql`](../commands/export/dbt-staging-sql.md)**. --- ## Export: DCS ## ![](/img/icons/custom.svg) Export: DCS Exports the data contract to the DCS (Data Contract Specification) format, for downstream tools that still expect it. For the other direction, see [Migrate from DCS to ODCS](../migrate-dcs-to-odcs.md). ```bash datacontract export dcs orders.odcs.yaml --output datacontract.yaml ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces (excerpt): ```yaml id: urn:datacontract:checkout:orders info: title: Orders version: 1.0.0 status: active description: Tracks customer orders and their line items for analytics and reporting. servers: production: type: snowflake account: my-account database: ANALYTICS schema: ORDERS bigquery: type: bigquery project: my-gcp-project dataset: orders terms: usage: Used by the analytics team to build revenue dashboards and by finance for reconciliation. limitations: Not suitable for real-time fraud detection; data is loaded in hourly batches. description: Tracks customer orders and their line items for analytics and reporting. models: orders: description: One row per customer order. type: table fields: order_id: type: string required: true primaryKey: true unique: true description: Unique identifier of the order. order_timestamp: # … ``` All options: **[`datacontract export dcs`](../commands/export/dcs.md)**. --- ## Export: DQX ## ![](/img/icons/dqx.svg) Export: DQX Converts the contract's [quality rules](../quality-rules/index.md) to [Databricks DQX](https://databrickslabs.github.io/dqx/) checks. ```bash datacontract export dqx user_interactions.odcs.yaml --schema-name user_interactions ``` Running this against the [example `user_interactions` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/user_interactions/user_interactions.odcs.yaml) produces: ```yaml - criticality: error check: function: is_not_null arguments: column: interaction_id - criticality: error check: function: is_not_null arguments: column: user_id - criticality: error check: function: is_in_list arguments: allowed: - click - view - purchase column: interaction_type - criticality: warn check: function: is_in_range arguments: min_limit: 0 max_limit: 1000 column: interaction_value ``` All options: **[`datacontract export dqx`](../commands/export/dqx.md)**. --- ## Export: Excel ## ![](/img/icons/excel.svg) Export: Excel Converts a data contract into an ODCS Excel template — a user-friendly spreadsheet for authoring, sharing, and managing data contracts. ```bash datacontract export excel orders.odcs.yaml --output orders.xlsx ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces an `orders.xlsx` workbook with the ODCS template sheets — fundamentals, servers, the `orders` and `line_items` schemas, quality, and service levels — pre-filled from the contract. The Excel format enables: - **User-friendly authoring** in Excel's familiar interface. - **Easy sharing** as standard Excel files. - **Collaboration** with non-technical stakeholders. - **Round-trip conversion** back to YAML via [`import excel`](../imports/excel.md). ### Templates The official ODCS Excel templates ship with the CLI, so the export works offline. For the template structure, see the [ODCS Excel Template repository](https://github.com/datacontract/open-data-contract-standard-excel-template). Use `--template` to export into your own workbook instead — a local path or a URL: ```bash datacontract export excel orders.odcs.yaml --template ./my-odcs-template.xlsx --output orders.xlsx ``` You can customize the template as you like. Start from a copy of the official template and adapt it (branding, extra sheets, additional columns). Note that if you drop or rename sheet names or column headers, the export will drop the associated information from the contract. All options: **[`datacontract export excel`](../commands/export/excel.md)**. --- ## Export: Go ## ![](/img/icons/go.svg) Export: Go Generates Go `struct` types from the data contract. ```bash datacontract export go orders.odcs.yaml --output orders.go ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```go package main type Orders struct { OrderId string `json:"order_id" avro:"order_id"` // Unique identifier of the order. OrderTimestamp time.Time `json:"order_timestamp" avro:"order_timestamp"` // Timestamp when the order was placed. CustomerId string `json:"customer_id" avro:"customer_id"` // Reference to the customer who placed the order. OrderTotal int `json:"order_total" avro:"order_total"` // Total amount of the order in cents. Status string `json:"status" avro:"status"` // Current fulfilment status of the order. } type LineItems struct { LineItemId string `json:"line_item_id" avro:"line_item_id"` // Unique identifier of the line item. OrderId string `json:"order_id" avro:"order_id"` // Reference to the parent order. Sku string `json:"sku" avro:"sku"` // Stock keeping unit of the purchased product. Quantity int `json:"quantity" avro:"quantity"` // Number of units purchased. } ``` All options: **[`datacontract export go`](../commands/export/go.md)**. --- ## Export: Great Expectations ## ![](/img/icons/great-expectations.svg) Export: Great Expectations Transforms a data contract into a comprehensive [Great Expectations](https://greatexpectations.io/) JSON suite. If the contract includes multiple models, specify the model with `--schema-name`. ```bash datacontract export great-expectations orders.odcs.yaml --schema-name orders ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```json { "name": "orders.1.0.0", "expectations": [ { "type": "expect_table_columns_to_match_set", "description": "orders must contain exactly the contracted columns", "kwargs": { "column_set": [ "order_id", "order_timestamp", "customer_id", "order_total", "status" ] }, "meta": { "expectation_id": "urn:datacontract:checkout:orders.column_set", "data_contract_rule_location": { "origin": "schema_inferred", "scope": "table" }, "name": "orders must contain exactly the contracted columns", "dimension": "conformity" } }, { "type": "expect_column_values_to_be_of_type", "description": "order_id must be of type VARCHAR", "kwargs": { "column": "order_id", "type_": "VARCHAR" }, "meta": { "expectation_id": "urn:datacontract:checkout:orders.order_id.order_id_must_be_of_type_varchar", "data_contract_rule_location": { "origin": "schema_inferred", "scope": "column" }, "name": "order_id must be of type VARCHAR", "dimension": "conformity" } }, { "type": "expect_column_values_to_not_be_null", "description": "order_id is a primary key and must not contain null values", "kwargs": { "column": "order_id" }, "meta": { "expectation_id": "urn:datacontract:checkout:orders.order_id.order_id_must_be_filled_primary_key", "data_contract_rule_location": { "origin": "schema_inferred", "scope": "column" }, "name": "order_id must be filled (primary key)", "dimension": "completeness" } }, { "type": "expect_column_values_to_be_unique", "description": "order_id is a primary key and must contain unique values", "kwargs": { "column": "order_id" }, "meta": { "expectation_id": "urn:datacontract:checkout:orders.order_id.order_id_must_be_unique_primary_key", "data_contract_rule_location": { "origin": "schema_inferred", "scope": "column" }, "name": "order_id must be unique (primary key)", "dimension": "uniqueness" } }, { "type": "expect_column_values_to_be_of_type", "description": "order_timestamp must be of type TIMESTAMP", "kwargs": { "column": "order_timestamp", "type_": "TIMESTAMP" }, "meta": { "expectation_id": "urn:datacontract:checkout:orders.order_timestamp.order_timestamp_must_be_of_type_timestamp", "data_contract_rule_location": { "origin": "schema_inferred", "scope": "column" }, "name": "order_timestamp must be of type TIMESTAMP", "dimension": "conformity" } }, { "type": "expect_column_values_to_not_be_null", "description": "order_timestamp must be not null values", "kwargs": { "column": "order_timestamp" }, "meta": { "expectation_id": "urn:datacontract:checkout:orders.order_timestamp.order_timestamp_must_be_filled", "data_contract_rule_location": { "origin": "schema_inferred", "scope": "column" }, "name": "order_timestamp must be filled", "dimension": "completeness" } }, { "type": "expect_column_values_to_be_of_type", "description": "customer_id must be of type VARCHAR", "kwargs": { "column": "customer_id", "type_": "VARCHAR" }, "meta": { "expectation_id": "urn:datacontract:checkout:orders.customer_id.customer_id_must_be_of_type_varchar", "data_contract_rule_location": { "origin": "schema_inferred", "scope": "column" }, "name": "customer_id must be of type VARCHAR", "dimension": "conformity" } }, { "type": "expect_column_values_to_not_be_null", "description": "customer_id must be not null values", "kwargs": { "column": "customer_id" }, "meta": { "expectation_id": "urn:datacontract:checkout:orders.customer_id.customer_id_must_be_filled", "data_contract_rule_location": { "origin": "schema_inferred", "scope": "column" }, "name": "customer_id must be filled", "dimension": "completeness" } }, { "type": "expect_column_values_to_be_of_type", "description": "order_total must be of type INTEGER", "kwargs": { "column": "order_total", "type_": "INTEGER" }, "meta": { "expectation_id": "urn:datacontract:checkout:orders.order_total.order_total_must_be_of_type_integer", "data_contract_rule_location": { "origin": "schema_inferred", "scope": "column" }, "name": "order_total must be of type INTEGER", "dimension": "conformity" } }, { "type": "expect_column_values_to_not_be_null", "description": "order_total must be not null values", "kwargs": { "column": "order_total" }, "meta": { "expectation_id": "urn:datacontract:checkout:orders.order_total.order_total_must_be_filled", "data_contract_rule_location": { "origin": "schema_inferred", "scope": "column" }, "name": "order_total must be filled", "dimension": "completeness" } }, { "type": "expect_column_values_to_be_of_type", "description": "status must be of type VARCHAR", "kwargs": { "column": "status", "type_": "VARCHAR" }, "meta": { "expectation_id": "urn:datacontract:checkout:orders.status.status_must_be_of_type_varchar", "data_contract_rule_location": { "origin": "schema_inferred", "scope": "column" }, "name": "status must be of type VARCHAR", "dimension": "conformity" } }, { "type": "expect_column_values_to_not_be_null", "description": "status must be not null values", "kwargs": { "column": "status" }, "meta": { "expectation_id": "urn:datacontract:checkout:orders.status.status_must_be_filled", "data_contract_rule_location": { "origin": "schema_inferred", "scope": "column" }, "name": "status must be filled", "dimension": "completeness" } } ], "meta": { "contract_id": "urn:datacontract:checkout:orders", "contract_version": "1.0.0" } } ``` The export builds expectations from the model definition (with a fixed mapping) and from the `quality` rules of each model (see the [expectations gallery](https://greatexpectations.io/expectations/)). ### Additional options - `--suite-name` — the name of the expectation suite. Defaults to a name derived from the model name(s). - `--engine` — the execution engine: `pandas` (in-memory dataframes), `spark` (Spark dataframes), or `sql` (SQL databases). - `--dialect` — the SQL dialect when `--engine` is `sql`. `auto` (default) detects it from the contract's servers. - `--checks` — comma-separated list of check categories to export: `properties` (constraints inferred from logical types) and/or `quality` (rules from the contract's `quality` blocks). Omit to export both. All options: **[`datacontract export great-expectations`](../commands/export/great-expectations.md)**. --- ## Export: HTML ## ![](/img/icons/custom.svg) Export: HTML Generates a standalone HTML page documenting the data contract. ```bash datacontract export html orders.odcs.yaml --output orders.html ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces a single `orders.html` file that renders the following (the diagram's scripts load from a CDN): - the contract metadata (name, version, status, description), - each schema (`orders`, `line_items`) as a table of fields with their types, constraints, and descriptions, - the configured servers, quality checks, and service-level properties. Open the file in any browser or publish it as static documentation. All options: **[`datacontract export html`](../commands/export/html.md)**. --- ## Export: Iceberg ## ![](/img/icons/iceberg.svg) Export: Iceberg Exports to an [Iceberg Table JSON Schema Definition](https://iceberg.apache.org/spec/#appendix-c-json-serialization). This export supports a single model at a time, because Iceberg's schema definition is for a single table (1 model → 1 table). Use `--schema-name` to select the model. ```bash datacontract export iceberg orders.odcs.yaml --schema-name orders --output orders.iceberg.json ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```json {"type":"struct","fields":[{"id":1,"name":"order_id","type":"string","required":true},{"id":2,"name":"order_timestamp","type":"timestamptz","required":true},{"id":3,"name":"customer_id","type":"string","required":true},{"id":4,"name":"order_total","type":"int","required":true},{"id":5,"name":"status","type":"string","required":true}],"schema-id":0,"identifier-field-ids":[1]} ``` All options: **[`datacontract export iceberg`](../commands/export/iceberg.md)**. --- ## Export: JSON Schema ## ![](/img/icons/jsonschema.svg) Export: JSON Schema Converts the data contract to a [JSON Schema](https://json-schema.org/) document. ```bash datacontract export jsonschema orders.odcs.yaml --schema-name orders --output orders.schema.json ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```json { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "order_id": { "type": "string", "primaryKey": true, "unique": true, "description": "Unique identifier of the order." }, "order_timestamp": { "type": "string", "format": "date-time", "description": "Timestamp when the order was placed." }, "customer_id": { "type": "string", "description": "Reference to the customer who placed the order." }, "order_total": { "type": "integer", "description": "Total amount of the order in cents." }, "status": { "type": "string", "description": "Current fulfilment status of the order." } }, "required": [ "order_id", "order_timestamp", "customer_id", "order_total", "status" ], "description": "One row per customer order." } ``` All options: **[`datacontract export jsonschema`](../commands/export/jsonschema.md)**. --- ## Export: Markdown ## ![](/img/icons/markdown.svg) Export: Markdown Generates Markdown documentation for the data contract. ```bash datacontract export markdown orders.odcs.yaml --output orders.md ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces (excerpt): ```markdown # urn:datacontract:checkout:orders ## Info *Tracks customer orders and their line items for analytics and reporting.* - **name:** Orders - **version:** 1.0.0 - **status:** active ## Terms of Use ### Usage Used by the analytics team to build revenue dashboards and by finance for reconciliation. ### Purpose Tracks customer orders and their line items for analytics and reporting. ### Limitations Not suitable for real-time fraud detection; data is loaded in hourly batches. ## Servers | Name | Type | Attributes | | ---- | ---- | ---------- | | production | snowflake | *No description.*
• **account:** my-account
• **database:** ANALYTICS
• **schema_:** ORDERS | | bigquery | bigquery | *No description.*
• **dataset:** orders
• **project:** my-gcp-project | ## Schema ### orders *One row per customer order.* | Field | Type | Attributes | | ----- | ---- | ---------- | | order_id | string | *Unique identifier of the order.*
• `primaryKey`
• `required`
• `unique`
• **examples:** ['ORD-1001', 'ORD-1002'] | | order_timestamp | timestamp | *Timestamp when the order was placed.*
• `required` | | customer_id | string | *Reference to the customer who placed the order.*
• `required` | | order_total | integer | *Total amount of the order in cents.*
• `required`
• **quality:** [{'description': 'Order total is never negative.', 'type': 'sql', 'mustBe': 0, 'query': 'SELECT COUNT(*) FROM orders WHERE order_total < 0'}] | | status | string | *Current fulfilment status of the order.*
• `required`
• **examples:** ['pending', 'shipped', 'delivered'] | ``` All options: **[`datacontract export markdown`](../commands/export/markdown.md)**. --- ## Export: Mermaid ## ![](/img/icons/mermaid.svg) Export: Mermaid Generates a [Mermaid](https://mermaid.js.org/) entity-relationship diagram of the contract's schemas. ```bash datacontract export mermaid orders.odcs.yaml ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```mermaid erDiagram "**orders**" { order_id🔑🔒 string order_timestamp timestamp customer_id string order_total integer status string } "**line_items**" { line_item_id🔑🔒 string order_id string sku string quantity integer } ``` All options: **[`datacontract export mermaid`](../commands/export/mermaid.md)**. --- ## Export: ODCS ## ![](/img/icons/odcs.svg) Export: ODCS Exports the data contract to the [Open Data Contract Standard](../open-data-contract-standard.md) (ODCS) YAML format. Useful for normalizing or converting an in-memory contract back to canonical ODCS. This is also the command that converts a legacy DCS contract — see [Migrate from DCS to ODCS](../migrate-dcs-to-odcs.md). ```bash datacontract export odcs orders.odcs.yaml --output orders.normalized.yaml ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces (excerpt): ```yaml version: 1.0.0 kind: DataContract apiVersion: v3.2.0 id: urn:datacontract:checkout:orders name: Orders tags: - checkout - orders status: active servers: - server: production type: snowflake account: my-account database: ANALYTICS schema: ORDERS - server: bigquery type: bigquery dataset: orders project: my-gcp-project description: usage: Used by the analytics team to build revenue dashboards and by finance for reconciliation. purpose: Tracks customer orders and their line items for analytics and reporting. limitations: Not suitable for real-time fraud detection; data is loaded in hourly batches. schema: - name: orders physicalType: table description: One row per customer order. physicalName: orders properties: - name: order_id # … ``` All options: **[`datacontract export odcs`](../commands/export/odcs.md)**. --- ## Export: Protobuf ## ![](/img/icons/custom.svg) Export: Protobuf Converts the data contract to a [Protocol Buffers](https://protobuf.dev/) schema. ```bash datacontract export protobuf orders.odcs.yaml --output orders.proto ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```protobuf syntax = "proto3"; package example; // One row per customer order. message Orders { // Unique identifier of the order. string order_id = 1; // Timestamp when the order was placed. string order_timestamp = 2; // Reference to the customer who placed the order. string customer_id = 3; // Total amount of the order in cents. int32 order_total = 4; // Current fulfilment status of the order. string status = 5; } // One row per line item within an order. message LineItems { // Unique identifier of the line item. string line_item_id = 1; // Reference to the parent order. string order_id = 2; // Stock keeping unit of the purchased product. string sku = 3; // Number of units purchased. int32 quantity = 4; } ``` ### Configuration You can customize the generated Protobuf package name using the data contract's `customProperties`: ```yaml kind: DataContract apiVersion: v3.1.0 id: my-contract version: 1.0.0 customProperties: - property: protoPackageName value: com.example.mydata schema: - name: Orders properties: # ... fields ... ``` #### Supported Properties - **`protoPackageName`** (optional) — The package name for the generated Protobuf file. Defaults to `"example"` if not specified. All options: **[`datacontract export protobuf`](../commands/export/protobuf.md)**. --- ## Export: Pydantic Model ## ![](/img/icons/pydantic.svg) Export: Pydantic Model Generates [Pydantic](https://docs.pydantic.dev/) model classes from the data contract. ```bash datacontract export pydantic-model orders.odcs.yaml --output orders.py ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```python import datetime, typing, pydantic, decimal 'Tracks customer orders and their line items for analytics and reporting.' class Orders(pydantic.BaseModel): """One row per customer order.""" order_id: str 'Unique identifier of the order.' order_timestamp: datetime.datetime 'Timestamp when the order was placed.' customer_id: str 'Reference to the customer who placed the order.' order_total: int 'Total amount of the order in cents.' status: str 'Current fulfilment status of the order.' class Line_items(pydantic.BaseModel): """One row per line item within an order.""" line_item_id: str 'Unique identifier of the line item.' order_id: str 'Reference to the parent order.' sku: str 'Stock keeping unit of the purchased product.' quantity: int 'Number of units purchased.' ``` ### Defaults and constraints Fields that are not `required` are typed `typing.Optional[...]` and default to `None`. `logicalTypeOptions` become `pydantic.Field` constraints, so the generated model rejects what `datacontract test` would flag: | logicalType | logicalTypeOptions | `pydantic.Field` keyword | |---|---|---| | string | `pattern`, `minLength`, `maxLength` | `pattern`, `min_length`, `max_length` | | integer, number, date, timestamp, time | `minimum`, `maximum`, `exclusiveMinimum`, `exclusiveMaximum` | `ge`, `le`, `gt`, `lt` | ```python label: typing.Optional[str] = pydantic.Field(default=None, pattern='^[^,]+(,\\s*[^,]+)*$') order_total: int = pydantic.Field(ge=0) ``` All options: **[`datacontract export pydantic-model`](../commands/export/pydantic-model.md)**. --- ## Export: RDF ## ![](/img/icons/rdf.svg) Export: RDF Converts the data contract into an RDF representation. You can add a base URL used as the default prefix to resolve relative IRIs inside the document. ```bash datacontract export rdf orders.odcs.yaml --output orders.ttl ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces (excerpt): ```turtle @prefix odcs: . @prefix odcsx: . @prefix xsd: . a odcs:DataContract ; odcs:apiVersion "v3.2.0" ; odcs:id "urn:datacontract:checkout:orders" ; odcs:info [ a odcs:Info ; odcs:description "Tracks customer orders and their line items for analytics and reporting." ; odcs:name "Orders" ; odcs:version "1.0.0" ] ; odcs:kind "DataContract" ; odcs:schema_ , ; odcs:server , . a odcs:Server ; odcsx:dataset "orders" ; odcsx:project "my-gcp-project" ; odcsx:server "bigquery" ; odcs:type "bigquery" . a odcs:Schema ; odcs:description "One row per line item within an order." ; # … ``` The contract is mapped onto concepts of a Data Contract Ontology (`DataContract`, `Server`, `Model`). Having the contract as an RDF graph enables interoperability with other formats, storage in a knowledge graph, semantic search, linking to established ontologies, OWL reasoning, and graph algorithms across multiple contracts. All options: **[`datacontract export rdf`](../commands/export/rdf.md)**. --- ## Export: SodaCL ## ![](/img/icons/soda.svg) Export: SodaCL Converts the contract's schema and [quality rules](../quality-rules/index.md) into [SodaCL](https://docs.soda.io/soda-cl/soda-cl-overview.html) checks. ```bash datacontract export sodacl orders.odcs.yaml --output sodacl.yaml ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces (excerpt): ```yaml checks for line_items: - schema: fail: when required column missing: - line_item_id name: line_items__line_item_id__field_is_present - schema: fail: when wrong column type: line_item_id: VARCHAR name: line_items__line_item_id__field_type - missing_count(line_item_id) = 0: name: line_items__line_item_id__field_required - duplicate_count(line_item_id) = 0: name: line_items__line_item_id__field_unique - schema: fail: when required column missing: - order_id name: line_items__order_id__field_is_present - schema: fail: when wrong column type: order_id: VARCHAR name: line_items__order_id__field_type - missing_count(order_id) = 0: name: line_items__order_id__field_required - schema: fail: when required column missing: - sku name: line_items__sku__field_is_present # … ``` All options: **[`datacontract export sodacl`](../commands/export/sodacl.md)**. --- ## Export: Spark ## ![](/img/icons/spark.svg) Export: Spark Converts the data contract into a Spark `StructType` schema. The returned value is Python code representing the model schemas. ```bash datacontract export spark orders.odcs.yaml ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces (excerpt): ```python orders = StructType([ StructField("order_id", StringType(), False, {"comment": "Unique identifier of the order."} ), StructField("order_timestamp", TimestampType(), False, {"comment": "Timestamp when the order was placed."} ), StructField("customer_id", StringType(), False, {"comment": "Reference to the customer who placed the order."} ), StructField("order_total", IntegerType(), False, {"comment": "Total amount of the order in cents."} ), StructField("status", StringType(), False, {"comment": "Current fulfilment status of the order."} ) ]) line_items = StructType([ StructField("line_item_id", StringType(), False, {"comment": "Unique identifier of the line item."} ), # … ``` For Spark data types, see the [Spark documentation](https://spark.apache.org/docs/latest/sql-ref-datatypes.html). All options: **[`datacontract export spark`](../commands/export/spark.md)**. --- ## Export: SQL Query ## ![](/img/icons/database.svg) Export: SQL Query Exports a data contract to a SQL `SELECT` query over the contract's fields. ```bash datacontract export sql-query orders.odcs.yaml --schema-name orders ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```sql -- Data Contract: urn:datacontract:checkout:orders -- SQL Dialect: snowflake select order_id, order_timestamp, customer_id, order_total, status from orders ``` All options: **[`datacontract export sql-query`](../commands/export/sql-query.md)**. --- ## Export: SQL DDL ## ![](/img/icons/database.svg) Export: SQL Converts a data contract into a SQL data definition language (DDL) file. ```bash datacontract export sql orders.odcs.yaml --output orders.sql ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```sql -- Data Contract: urn:datacontract:checkout:orders -- SQL Dialect: snowflake CREATE TABLE orders ( order_id VARCHAR not null primary key COMMENT 'Unique identifier of the order.', order_timestamp TIMESTAMP_TZ not null COMMENT 'Timestamp when the order was placed.', customer_id VARCHAR not null COMMENT 'Reference to the customer who placed the order.', order_total NUMBER not null COMMENT 'Total amount of the order in cents.', status VARCHAR not null COMMENT 'Current fulfilment status of the order.' ) COMMENT='One row per customer order.'; CREATE TABLE line_items ( line_item_id VARCHAR not null primary key COMMENT 'Unique identifier of the line item.', order_id VARCHAR not null COMMENT 'Reference to the parent order.', sku VARCHAR not null COMMENT 'Stock keeping unit of the purchased product.', quantity NUMBER not null COMMENT 'Number of units purchased.' ) COMMENT='One row per line item within an order.'; ``` The SQL dialect is determined from the `servers` block in the data contract (e.g. `type: postgres`, `type: snowflake`). Alternatively, pass it explicitly: ```bash datacontract export sql datacontract.yaml --dialect postgres --output output.sql ``` Supported dialects: `postgres`, `mysql`, `snowflake`, `databricks`, `sqlserver`, `trino`, `oracle`, `clickhouse` (and `auto`, the default, which detects the dialect from the server type). :::note[Databricks `variant` columns] If an error is thrown when deploying SQL DDLs with `variant` columns on Databricks, set: ```python spark.conf.set("spark.databricks.delta.schema.typeCheck.enabled", "false") ``` ::: ### ClickHouse ```bash datacontract export sql datacontract.yaml --dialect clickhouse --output ddl.sql ``` ClickHouse requires a table engine (default `ENGINE = MergeTree()`) and uses `ORDER BY` as its primary key mechanism. Primary key fields in the contract are translated to the `ORDER BY` clause. ```bash # Custom engine datacontract export sql datacontract.yaml --dialect clickhouse \ --clickhouse-engine "ReplicatedMergeTree('/clickhouse/tables/{shard}/table', '{replica}')" # Custom ORDER BY (defaults to primary key columns if defined) datacontract export sql datacontract.yaml --dialect clickhouse \ --clickhouse-order-by "event_date DESC, event_id" ``` | Option | Description | |---|---| | `--clickhouse-engine` | The table engine. Default: `MergeTree()`. | | `--clickhouse-order-by` | Comma-separated `ORDER BY` columns. Defaults to primary key columns. | Override any field's ClickHouse type individually by setting the custom property `clickhouseType` in the data contract. All options: **[`datacontract export sql`](../commands/export/sql.md)**. --- ## Export: SQLAlchemy ## ![](/img/icons/sqlalchemy.svg) Export: SQLAlchemy Generates [SQLAlchemy](https://www.sqlalchemy.org/) ORM models from the data contract. ```bash datacontract export sqlalchemy orders.odcs.yaml --output orders_models.py ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces: ```python from sqlalchemy.orm import DeclarativeBase from sqlalchemy import Column, Date, Integer, Numeric, String, Text, VARCHAR, BigInteger, Float, Double, Boolean, Date, ARRAY, JSON, LargeBinary from sqlalchemy import TIMESTAMP 'Tracks customer orders and their line items for analytics and reporting.' class Base(DeclarativeBase): pass class Orders(Base): """One row per customer order.""" __tablename__ = 'orders' __table_args__ = {'comment': 'One row per customer order.', 'schema': None} order_id = Column(String(None), nullable=False, comment='Unique identifier of the order.', primary_key=True) order_timestamp = Column(TIMESTAMP, nullable=False, comment='Timestamp when the order was placed.', primary_key=None) customer_id = Column(String(None), nullable=False, comment='Reference to the customer who placed the order.', primary_key=None) order_total = Column(Integer, nullable=False, comment='Total amount of the order in cents.', primary_key=None) status = Column(String(None), nullable=False, comment='Current fulfilment status of the order.', primary_key=None) class Line_items(Base): """One row per line item within an order.""" __tablename__ = 'line_items' __table_args__ = {'comment': 'One row per line item within an order.', 'schema': None} line_item_id = Column(String(None), nullable=False, comment='Unique identifier of the line item.', primary_key=True) order_id = Column(String(None), nullable=False, comment='Reference to the parent order.', primary_key=None) sku = Column(String(None), nullable=False, comment='Stock keeping unit of the purchased product.', primary_key=None) quantity = Column(Integer, nullable=False, comment='Number of units purchased.', primary_key=None) ``` All options: **[`datacontract export sqlalchemy`](../commands/export/sqlalchemy.md)**. --- ## Export: XML Schema ## ![](/img/icons/custom.svg) Export: XML Schema Converts the data contract to an [XML Schema](https://www.w3.org/XML/Schema) (XSD) with one global element per schema. ```bash datacontract export xsd orders.odcs.yaml --output orders.xsd ``` Running this against the [example `orders` contract](https://github.com/datacontract/datacontract-cli/blob/main/examples/orders/orders.odcs.yaml) produces (excerpt): ```xml One row per customer order. Unique identifier of the order. Timestamp when the order was placed. Reference to the customer who placed the order. Total amount of the order in cents. Current fulfilment status of the order. One row per line item within an order. … ``` - Objects become nested complex types, arrays become elements that repeat (`minItems`/`maxItems` set `minOccurs`/`maxOccurs`), and a property that is not `required` gets `minOccurs="0"`. - The `logicalType` sets the XSD type (`timestamp` → `xs:dateTime`, `number` → `xs:decimal`, …), unless the `physicalType` names a built-in XSD type of the same logical type, such as `positiveInteger` or `token`, or the ODCS `format` gives a size: `i8` → `xs:byte`, `i16` → `xs:short`, `i32` → `xs:int`, `i64` → `xs:long`, `u8` to `u64` → `xs:unsignedByte` to `xs:unsignedLong`, `f32` → `xs:float`, `f64` → `xs:double`. - `enum`, `pattern`, `minLength`/`maxLength`, `minimum`/`maximum` (and their exclusive forms), and the `precision`/`scale` of decimals become facets. A bound that only repeats the range of the XSD type, such as `minimum: 0` on an `unsignedInt`, is left out. XSD patterns always match the whole value, contract patterns anywhere unless anchored: `^ORD$` exports as `ORD`, `ORD` as `.*(ORD).*`. - A property with the custom property `xmlNode: attribute` becomes an attribute, without the `@` prefix the import gives an attribute named like another property, and one with `xmlNode: text` the text of an element with attributes. A schema's `xmlNamespace` custom property becomes the target namespace; a property's `xmlNamespace: ""` makes the element unqualified (`form="unqualified"`). An element in yet another namespace would need a schema of its own, so it stays in the target namespace, with a warning. [`datacontract import xsd`](../imports/xsd.md) sets all three, so an imported XML Schema exports back to an equivalent one. - An object without properties accepts any content (`xs:anyType`). A `map` becomes repeated `entry` elements with a `key` and a `value`. - A `string` with `physicalType: list` exports as an `xs:list`, and one with a union of built-in XSD types in `physicalType` (`integer|date`) as an `xs:union`, the way the import writes them. - XSD allows one lower and one upper bound per type, so when a property has both `minimum` and `exclusiveMinimum` (or both maximums), the tighter one is exported. - Every schema becomes a global element of the same XML Schema, which has one target namespace: when schemas declare different `xmlNamespace`s, the first one is used, with a warning. `--schema-name` exports a single schema. - Names that are not valid XML names are changed to valid ones, with a warning. XML Schema has nothing for `primaryKey`, `unique`, string `format`s, `examples`, and quality rules, so they are not exported. The digits of a decimal come from the `precision` and `scale` custom properties; a `physicalType` such as `NUMERIC(12,2)` is not read. All options: **[`datacontract export xsd`](../commands/export/xsd.md)**. --- ## Data Source Reference Lookup material for every supported data source: all **authentication** options (environment variables) and how **data types** are mapped when importing a contract and checked when testing one. For task-oriented walkthroughs, see [Test your Data](../testing/index.md). ### How authentication works Connection details (host, catalog, location, …) live in the contract's `servers` block; credentials are provided as environment variables. Variables are also loaded from a `.env` file in the current working directory (or the nearest parent directory containing one); already-set environment variables take precedence. ### How data types work A contract property carries up to two type declarations: - **`logicalType`** — one of eleven portable ODCS types: `string`, `integer`, `number`, `boolean`, `date`, `timestamp`, `time`, `object`, `array`, `map`, `vector`. - **`physicalType`** — free text for the native type in the data source (e.g. `VARCHAR(255)`, `NUMBER(38,0)`). It is not validated by `datacontract lint`; at test time it is interpreted in the SQL dialect of the server under test. When you run `datacontract test`, type checks work in one of two modes: 1. **Native type check** (`Check that field x has physical type y`) — on sources with catalog introspection (Snowflake, BigQuery, Databricks, Postgres, Redshift, SQL Server, Oracle, Trino, Athena, Exasol, ClickHouse, Hive), the declared `physicalType` is compared against the actual column type from the catalog. Timezone variants of timestamps are interchangeable; length/precision is only enforced when the contract declares it (`varchar` matches `varchar(255)`, but `varchar(255)` does not match `varchar(100)`). A `physicalType` that can't be interpreted in the server's dialect degrades to the logical check or a warning — never a hard failure. 2. **Logical type check** (`Check that field x has type y`) — everywhere else (and as fallback), both the declared and the actual type are normalized to one of the ODCS logical types and compared. `integer` and `number` are mutually compatible; a bare `object` or `array` matches any structure with the same base. For file sources with `format: csv`, `json`, `xml`, or `avro`, no type checks are generated — the file is read *as* the contract's types, and violations surface as read errors (plus JSON Schema validation for `format: json`). Independent of the type checks, `logicalTypeOptions` (`minimum`, `maximum`, `minLength`, `maxLength`, `pattern`, `enum`, …) generate value checks that behave identically on every source. ### Data sources ![](/img/icons/athena.svg) Amazon AthenaAuthentication and data types ![](/img/icons/redshift.svg) Amazon RedshiftAuthentication and data types ![](/img/icons/s3.svg) Amazon S3Authentication and data types ![](/img/icons/database.svg) Apache HiveAuthentication and data types ![](/img/icons/iceberg.svg) Apache IcebergAuthentication and data types ![](/img/icons/impala.svg) Apache ImpalaAuthentication and data types ![](/img/icons/azure.svg) Azure Blob / ADLSAuthentication and data types ![](/img/icons/clickhouse.svg) ClickHouseAuthentication and data types ![](/img/icons/databricks.svg) DatabricksAuthentication and data types ![](/img/icons/exasol.svg) ExasolAuthentication and data types ![](/img/icons/bigquery.svg) Google BigQueryAuthentication and data types ![](/img/icons/gcs.svg) Google Cloud StorageAuthentication and data types ![](/img/icons/api.svg) HTTP APIAuthentication and data types ![](/img/icons/kafka.svg) KafkaAuthentication and data types ![](/img/icons/local.svg) Local filesData types for CSV, JSON, XML, Parquet, Delta ![](/img/icons/sqlserver.svg) Microsoft SQL ServerAuthentication and data types ![](/img/icons/mysql.svg) MySQLAuthentication and data types ![](/img/icons/oracle.svg) OracleAuthentication and data types ![](/img/icons/postgres.svg) PostgresAuthentication and data types ![](/img/icons/snowflake.svg) SnowflakeAuthentication and data types ![](/img/icons/spark.svg) Spark DataFrameData types for in-memory DataFrames ![](/img/icons/trino.svg) TrinoAuthentication and data types --- ## HTTP API Reference ## ![](/img/icons/api.svg) HTTP API Reference Authentication options and data type handling for [HTTP API connections](../testing/api.md). ### Server ```yaml servers: - server: api type: api location: "https://api.example.com/orders" delimiter: array # new_line, array, or none (default) ``` ### Authentication | Variable | Example | Description | |---|---|---| | `DATACONTRACT_API_HEADER_AUTHORIZATION` | `Bearer ` | Value for the `Authorization` header (optional) | | `DATACONTRACT_API_HEADER_X_API_KEY` | `` | Value for the `X-API-Key` header (optional) | The `location` (URL) and `delimiter` (`new_line`, `array`, or `none`) come from the contract's `servers` block. The `location` may contain [variables](../configuration.md#variables-in-the-data-contract), such as `https://api.example.com/orders/${orderId:-ORD-1001}`. ### Request Custom properties of the server shape the request: | Custom property | Example | Description | |---|---|---| | `method` | `POST` | `GET` (default) or `POST`. | | `params` | `{limit: 100, status: [placed, shipped]}` | The query parameters, each a string, number or boolean, or a list of them for a repeated parameter. | | `body` | `{query: {status: shipped}}` | The request body. A mapping or list is sent as JSON, a string as the JSON text it holds. Requires `method: POST`. | A `${VAR}` [variable](../configuration.md#variables-in-the-data-contract) may appear in any string of `params` and `body`; it resolves to a string, so write a number the API requires as a literal, or put the reference into a string `body` without quotes: `'{"size": ${SIZE:-100}}'`. ### Data types The response may be JSON or YAML, as its `Content-Type` states (`application/yaml`, `application/x-yaml`, or `text/yaml` for YAML). A YAML response is read as the JSON it holds; a stream of several YAML documents is one record per document. The response is then downloaded and tested like a local JSON file, so a property that is not required may be absent from every record: records are read as the contract's types and validated against a JSON Schema derived from the contract's `logicalType`s. The ODCS logical types map to JSON Schema types (`string`, `integer`, `number`, `boolean`, `object`, `array`; `date`/`timestamp`/`time` become `string` with `format: date`/`date-time`/`time`, `map` an `object`, and `vector` an `array` of numbers); non-required fields also accept `null`. `physicalType` is not checked. Value constraints come from `logicalTypeOptions` (`pattern`, `minimum`, `enum`, …). --- ## Amazon Athena Reference ## ![](/img/icons/athena.svg) Amazon Athena Reference Authentication options and data type handling for [Athena connections](../testing/athena.md). ### Server ```yaml servers: - server: athena type: athena catalog: awsdatacatalog # default schema: my_database # in Athena, this is called "database" regionName: eu-central-1 stagingDir: s3://my-bucket/athena-results/ ``` ### Authentication Athena authenticates as an AWS principal, so an existing AWS session is enough — `aws sso login`, `AWS_PROFILE`, EC2/ECS/EKS instance roles, or GitHub OIDC in CI. Unlike Redshift, no database user is involved, so nothing has to be minted or configured. Set the variables below only to override that with static keys: | Variable | Example | Description | |---|---|---| | `DATACONTRACT_S3_REGION` | `eu-central-1` | Region of the Athena service | | `DATACONTRACT_S3_ACCESS_KEY_ID` | `AKIAXV5Q5Q...` | AWS Access Key ID | | `DATACONTRACT_S3_SECRET_ACCESS_KEY` | `93S7LRrJ...` | AWS Secret Access Key | | `DATACONTRACT_S3_SESSION_TOKEN` | `AQoDYXdzEJr...` | AWS temporary session token (optional) | `catalog`, `schema` (the Athena database), `regionName`, and `stagingDir` (required, for query results) come from the contract's `servers` block; they can be overridden with `DATACONTRACT_ATHENA_CATALOG`, `DATACONTRACT_ATHENA_SCHEMA`, `DATACONTRACT_S3_REGION` (takes precedence over `regionName`), and `DATACONTRACT_ATHENA_STAGING_DIR`. The credentials need `athena:StartQueryExecution` plus read access to the data and write access to `stagingDir`, and `glue:GetTables` to import. ### Data types #### Importing `datacontract import athena` reads the table metadata from the AWS Glue Data Catalog, where Athena keeps it. Glue stores the Hive spelling of two types, which the import rewrites to what Athena reports back — `string` → `varchar` and `binary` → `varbinary` — so the physical type checks pass on the first test run. Every other type already compares equal in the Athena dialect (`int`/`integer`, `array`/`array(integer)`, `struct`/`row(...)`, `decimal`/`decimal(10,2)`). Types map through the shared Glue mapping: `string`/`varchar`/`char` → `string`, `int`/`bigint`/`smallint`/`tinyint` → `integer`, `float`/`double`/`decimal` → `number`, `boolean` → `boolean`, `date` → `date`, `timestamp` → `timestamp`, `array<...>` → `array`, `struct<...>` → `object`, `binary` → `string` (format `binary`), `map<...>` → no logical type. #### Testing Athena supports **native type introspection**: the declared `physicalType` is checked against the actual column type from the Athena catalog. Timezone variants of timestamps are interchangeable; parameters are only enforced when declared. A `physicalType` that isn't valid Athena SQL falls back to the logical type category comparison. --- ## Azure Blob / ADLS Reference ## ![](/img/icons/azure.svg) Azure Blob / ADLS Reference Authentication options and data type handling for [Azure storage connections](../testing/azure.md). ### Server ```yaml servers: - server: production type: azure location: abfss://datameshdatabricksdemo.dfs.core.windows.net/inventory_events/*.parquet format: parquet ``` ### Authentication Authentication uses an Azure Service Principal (App Registration) with a secret: | Variable | Example | Description | |---|---|---| | `DATACONTRACT_AZURE_TENANT_ID` | `79f5b80f-...` | The Azure Tenant ID | | `DATACONTRACT_AZURE_CLIENT_ID` | `3cf7ce49-...` | The Application/Client ID of the app registration | | `DATACONTRACT_AZURE_CLIENT_SECRET` | `yZK8Q~GWO1M...` | The client secret value | The service principal needs the **Storage Blob Data Reader** role. The [metadata-check path](../testing/azure.md#metadata-checks) alternatively accepts `DATACONTRACT_AZURE_CONNECTION_STRING` or `DATACONTRACT_AZURE_STORAGE_ACCOUNT_KEY`. Supported `location` URL formats (in the `servers` block): - `https://.blob.core.windows.net//` - `abfss://@.dfs.core.windows.net/` - `azure://@.blob.core.windows.net/` - `wasbs://@.blob.core.windows.net/` ### Data types Files on Azure storage are read with DuckDB. Type handling depends on the `format` in the `servers` block: | `format` | How types are handled | |---|---| | `csv` | The file is read **as the contract's types** — no type checks are generated; a value that can't be coerced surfaces as a read error. | | `json` | Same as CSV, plus every record is validated against a JSON Schema derived from the contract's `logicalType`s. | | `parquet` | The file is read **as the contract's types**, like CSV, so checking its types is not supported yet, a value that can't be cast surfaces as a read error. | | `delta` | Column types come from the Delta table; the contract's `logicalType` is checked by category. | `physicalType` is never checked against file sources. Schema objects with `logicalType: blob` / `physicalType: file` switch to [metadata checks](../testing/azure.md#metadata-checks) against blob properties instead of reading file contents. #### Logical type mapping When no `physicalType` is declared, the CLI derives the DuckDB column type from the `logicalType` when reading files. This table is generated from the converters in the CLI's code: | `logicalType` | CSV read type | Parquet read type | |---|---|---| | `string` | `VARCHAR` | `VARCHAR` | | `integer` | `BIGINT` | `INTEGER` | | `number` | `DOUBLE` | `DECIMAL` | | `boolean` | `BOOLEAN` | `BOOLEAN` | | `date` | `DATE` | `DATE` | | `timestamp` | `TIMESTAMP` | `TIMESTAMP WITH TIME ZONE` | | `time` | `TIME` | `TIME` | | `object` | `VARCHAR` | `STRUCT()` | | `array` | `VARCHAR` | `VARCHAR[]` | | `map` | `VARCHAR` | `MAP(VARCHAR, VARCHAR)` | | `vector` | `VARCHAR` | `FLOAT[]` | --- ## Google BigQuery Reference ## ![](/img/icons/bigquery.svg) Google BigQuery Reference Authentication options and data type handling for [BigQuery connections](../testing/bigquery.md). ### Server ```yaml servers: - server: production type: bigquery project: datameshexample-product dataset: datacontract_cli_test_dataset ``` ### Authentication Authentication uses a Service Account Key or Application Default Credentials (ADC) — including Workload Identity Federation (WIF), the GCE metadata server, and `gcloud auth application-default login`. The account needs the **BigQuery Job User**, **BigQuery Data Viewer** and **BigQuery Read Session User** roles. | Variable | Example | Description | |---|---|---| | `DATACONTRACT_BIGQUERY_ACCOUNT_INFO_JSON_PATH` | `~/service-access-key.json` | Service Account key JSON file used by `test`. If unset, ADC/WIF is used. | | `DATACONTRACT_BIGQUERY_BILLING_PROJECT` | `my-compute-project` | Optional. Project to bill query jobs to. Requires `bigquery.jobUser`, `bigquery.readSessionUser` on the billing project and `bigquery.dataViewer` on the data project. | | `DATACONTRACT_BIGQUERY_IMPERSONATION_ACCOUNT` | `runner@my-project.iam.gserviceaccount.com` | Optional. Service account to impersonate, using the key file above (or ADC) as the source principal. Requires `roles/iam.serviceAccountTokenCreator` on the target. | `project` and `dataset` come from the contract's `servers` block, and can be overridden with `DATACONTRACT_BIGQUERY_PROJECT` and `DATACONTRACT_BIGQUERY_DATASET`. `datacontract import bigquery` always uses ADC — set `GOOGLE_APPLICATION_CREDENTIALS` to point it at a key file. ### Data types #### Importing `datacontract import bigquery` maps BigQuery types as follows; the original BigQuery type is kept as `physicalType`. An unknown type fails the import with an error. | BigQuery type | `logicalType` | |---|---| | `STRING` | `string` (`maxLength` if declared) | | `BYTES` | `array` | | `INTEGER`, `INT64` | `integer` | | `FLOAT`, `FLOAT64` | `number` | | `NUMERIC`, `BIGNUMERIC` | `number` (precision/scale as custom properties) | | `BOOLEAN`, `BOOL` | `boolean` | | `TIMESTAMP`, `DATETIME` | `timestamp` | | `DATE` | `date` | | `TIME` | `time` | | `GEOGRAPHY`, `JSON` | `object` | | `INTERVAL` | `string` | | `RECORD`, `STRUCT` | `object` with nested `properties` | | `RANGE` | `array` with typed `items` | | any type with mode `REPEATED` | `array` with typed `items` | Fields with mode `REQUIRED` become `required: true`. Tables, external tables, and snapshots become schema `physicalType: table`; views and materialized views become `view`. #### Testing BigQuery supports **native type introspection**: the declared `physicalType` is checked against the actual column type from the BigQuery API. Timezone variants of timestamps are interchangeable; parameters (precision, length) are only enforced when the contract declares them. If the `physicalType` cannot be interpreted as a BigQuery type, the check falls back to the logical type category comparison. #### Logical type mapping When no `physicalType` is declared, the CLI derives the native type from the `logicalType` — for example in `datacontract export sql` and the dbt exports. This table is generated from the converter in the CLI's code: | `logicalType` | BigQuery type | |---|---| | `string` | `STRING` | | `integer` | `INTEGER` | | `number` | `NUMERIC` | | `boolean` | `BOOL` | | `date` | `DATE` | | `timestamp` | `TIMESTAMP` | | `time` | `TIME` | | `object` | `STRUCT<>` | | `array` | `ARRAY` | | `map` | `JSON` | | `vector` | `ARRAY` | --- ## ClickHouse Reference ## ![](/img/icons/clickhouse.svg) ClickHouse Reference Authentication options and data type handling for [ClickHouse connections](../testing/clickhouse.md). ### Server ```yaml servers: - server: production type: clickhouse host: clickhouse.acme.com port: 8123 database: sales ``` The CLI connects to ClickHouse's HTTP interface. The native protocol ports `9000` and `9440`, which `clickhouse-client` uses, are replaced by the HTTP ports `8123` and `8443`. ### Authentication | Variable | Example | Description | |---|---|---| | `DATACONTRACT_CLICKHOUSE_USERNAME` | `analyst` | User. Defaults to `default` | | `DATACONTRACT_CLICKHOUSE_PASSWORD` | `mysecretpassword` | Password. Defaults to empty | | `DATACONTRACT_CLICKHOUSE_SECURE` | `true` | Connect over HTTPS, as ClickHouse Cloud requires. The port then defaults to `8443`. Defaults to `false` | `host`, `port`, and `database` come from the contract's `servers` block, and can be overridden with `DATACONTRACT_CLICKHOUSE_HOST`, `DATACONTRACT_CLICKHOUSE_PORT`, and `DATACONTRACT_CLICKHOUSE_DATABASE`. ### Data types #### Importing `datacontract import clickhouse` reads `system.columns` and takes the declared type as the `physicalType`. `Nullable(...)` and `LowCardinality(...)` are looked through for the logical type: `String`/`FixedString`/`UUID`/`Enum8`/`Enum16`/`IPv4`/`IPv6` → `string`, `Int8`…`Int256`/`UInt8`…`UInt256` → `integer`, `Float32`/`Float64`/`Decimal` → `number`, `Bool` → `boolean`, `Date`/`Date32` → `date`, `DateTime`/`DateTime64` → `timestamp`, `Array(...)` → `array`, `Map(...)` → `map`, `Tuple(...)`/`JSON` → `object`. A column is `required` unless its type is `Nullable`. #### Testing ClickHouse supports **native type introspection**: the declared `physicalType` is checked against `system.columns`. The SQL spellings ClickHouse stores under another name match that name (`VARCHAR(255)` matches `String`, `TIMESTAMP` matches `DateTime`), and a `DateTime64(3)` without a time zone matches a `DateTime64(3, 'UTC')` column. `Nullable` is checked by `required`, not by the type: `String` matches a `Nullable(String)` column. `LowCardinality(String)` and `String` are distinct types. #### Logical type mapping When no `physicalType` is declared, the CLI derives the native type from the `logicalType` — for example in `datacontract export sql` and the dbt exports. This table is generated from the converter in the CLI's code: | `logicalType` | ClickHouse type | |---|---| | `string` | `String` | | `integer` | `Int32` | | `number` | `Decimal(38,0)` | | `boolean` | `Bool` | | `date` | `Date` | | `timestamp` | `DateTime64(6)` | | `time` | `String` | | `object` | `Tuple()` | | `array` | `Array(String)` | | `map` | `Map(String, String)` | | `vector` | `Array(Float32)` | --- ## Databricks Reference ## ![](/img/icons/databricks.svg) Databricks Reference Authentication options and data type handling for [Databricks connections](../testing/databricks.md). ### Server ```yaml servers: - server: production type: databricks catalog: acme_catalog_prod schema: orders_latest ``` ### Authentication | Variable | Example | Description | |---|---|---| | `DATACONTRACT_DATABRICKS_SERVER_HOSTNAME` | `dbc-abcdefgh-1234.cloud.databricks.com` | Host of the SQL warehouse or compute cluster | | `DATACONTRACT_DATABRICKS_HTTP_PATH` | `/sql/1.0/warehouses/b053a3ff...` | HTTP path to the SQL warehouse or compute cluster | | `DATACONTRACT_DATABRICKS_TOKEN` | `dapia0000...` | A personal access token (PAT) | | `DATACONTRACT_DATABRICKS_CLIENT_ID` | `00000000-...` | Service principal client ID for OAuth M2M auth | | `DATACONTRACT_DATABRICKS_CLIENT_SECRET` | `dose0000...` | Service principal OAuth secret (used with the client ID) | | `DATACONTRACT_DATABRICKS_PROFILE` | `my-profile` | A profile from `~/.databrickscfg` (Databricks SDK unified auth) | | `DATACONTRACT_DATABRICKS_AUTH_TYPE` | `databricks-oauth` | Explicit connector auth type, e.g. for interactive U2M browser login | The authentication method is selected from the variables you set, in this order: PAT → OAuth service principal (`CLIENT_ID` + `CLIENT_SECRET`) → config profile → explicit `AUTH_TYPE`. `catalog` and `schema` come from the contract's `servers` block, and can be overridden with `DATACONTRACT_DATABRICKS_CATALOG` and `DATACONTRACT_DATABRICKS_SCHEMA`; `DATACONTRACT_DATABRICKS_SERVER_HOSTNAME` overrides the contract's `host`. `datacontract import unity` needs only the hostname plus a PAT or profile; `datacontract test` additionally needs `DATACONTRACT_DATABRICKS_HTTP_PATH`. When a `spark` session is passed programmatically (notebook/pipeline), no credentials are needed at all. ### Data types #### Importing `datacontract import unity` keeps the full Unity Catalog type text as `physicalType` (e.g. `decimal(10,2)`, `struct`, `map`) and maps to `logicalType`: | Databricks type | `logicalType` | |---|---| | `STRING`, `VARCHAR`, `CHAR` | `string` | | `INT`, `BIGINT`, `SMALLINT`, `TINYINT` | `integer` | | `FLOAT`, `DOUBLE`, `DECIMAL` | `number` | | `BOOLEAN` | `boolean` | | `DATE` | `date` | | `TIMESTAMP` | `timestamp` | | `BINARY` | `string` (format `binary`) | | `ARRAY<...>` | `array` (nested `items` when pyspark is available) | | `STRUCT<...>` | `object` (nested `properties` when pyspark is available) | | `MAP<...>`, `INTERVAL`, `VARIANT` | *(unset)* — `physicalType` is still written | `datacontract import spark` maps Spark session types the same way, with two caveats: `TimestampType` and `TimestampNTZType` are currently imported as `date`, and `BinaryType` as `array`. #### Testing Databricks supports **native type introspection**: the declared `physicalType` is checked against the actual `information_schema` column type. Timezone variants of timestamps are interchangeable; parameters are only enforced when declared. A `physicalType` that isn't valid Databricks SQL falls back to the logical type category comparison. When testing a Spark DataFrame (`customType: dataframe`), only the logical type category is checked. #### Logical type mapping When no `physicalType` is declared, the CLI derives the native type from the `logicalType` — for example in `datacontract export sql` and the dbt exports. This table is generated from the converter in the CLI's code: | `logicalType` | Databricks type | |---|---| | `string` | `STRING` | | `integer` | `INT` | | `number` | `DECIMAL(38,0)` | | `boolean` | `BOOLEAN` | | `date` | `DATE` | | `timestamp` | `TIMESTAMP` | | `time` | `STRING` | | `object` | `STRUCT<>` | | `array` | `ARRAY` | | `map` | `MAP` | | `vector` | `ARRAY` | --- ## Spark DataFrame Reference ## ![](/img/icons/spark.svg) Spark DataFrame Reference Data type handling for [Spark DataFrame connections](../testing/dataframe.md). No environment variables are needed — the existing Spark session is passed programmatically. ### Data types #### Importing `datacontract import spark --tables ` maps Spark types as follows; `physicalType` is the Spark simple string (e.g. `bigint`, `decimal(10,2)`, `array`). | Spark type | `logicalType` | |---|---| | `StringType`, `VarcharType` | `string` | | `IntegerType`, `ShortType`, `LongType` | `integer` | | `FloatType`, `DoubleType`, `DecimalType` | `number` | | `BooleanType` | `boolean` | | `DateType` | `date` | | `TimestampType`, `TimestampNTZType` | `date` | | `StructType` | `object` with nested `properties` | | `ArrayType` | `array` with typed `items` | | `MapType`, `VariantType` | `object` | | `BinaryType` | `array` | #### Testing DataFrames are registered as temporary views named after the contract's schema objects. There is no catalog to introspect, so the `logicalType` is checked by category (`Check that field x has type y`) against the DataFrame's Spark types, with `integer` and `number` treated as compatible. #### Logical type mapping When no `physicalType` is declared, the CLI derives the native type from the `logicalType` — for example in `datacontract export sql` and the dbt exports. This table is generated from the converter in the CLI's code: | `logicalType` | Spark type | |---|---| | `string` | `STRING` | | `integer` | `INT` | | `number` | `DECIMAL(38,0)` | | `boolean` | `BOOLEAN` | | `date` | `DATE` | | `timestamp` | `TIMESTAMP` | | `time` | `STRING` | | `object` | `STRUCT<>` | | `array` | `ARRAY` | | `map` | `MAP` | | `vector` | `ARRAY` | --- ## Exasol Reference ## ![](/img/icons/exasol.svg) Exasol Reference Authentication options and data type handling for [Exasol connections](../testing/exasol.md). ### Server ```yaml servers: - server: production type: exasol host: exasol.acme.com port: 8563 schema: sales ``` `host` may be a cluster range (`n11..14.acme.com`). The `exasol` server type was added in ODCS v3.2.0, so the contract has to declare `apiVersion: v3.2.0`. ### Authentication | Variable | Example | Description | |---|---|---| | `DATACONTRACT_EXASOL_USERNAME` | `sys` | Database user | | `DATACONTRACT_EXASOL_PASSWORD` | `mysecretpassword` | Password | | `DATACONTRACT_EXASOL_FINGERPRINT` | `135A1D2D...` | SHA-256 fingerprint of the server certificate, see [TLS](#tls) | | `DATACONTRACT_EXASOL_VALIDATE_CERTIFICATE` | `false` | Set to `false` to skip the certificate check. Defaults to `true` | `host`, `port`, and `schema` come from the contract's `servers` block, and can be overridden with `DATACONTRACT_EXASOL_HOST`, `DATACONTRACT_EXASOL_PORT`, and `DATACONTRACT_EXASOL_SCHEMA`. `port` defaults to `8563`. #### TLS Connections are always encrypted, and the server certificate is verified against the system CA store. For a cluster with a self-signed certificate, pin it by its SHA-256 fingerprint instead — the value Exasol clients accept after the host in a connection string: ```bash # .env DATACONTRACT_EXASOL_FINGERPRINT=135A1D2DCE102DE866F58267521F4232153545A075DC85F8F7596F57E588A181 ``` `DATACONTRACT_EXASOL_VALIDATE_CERTIFICATE=false` skips the verification altogether. A CA bundle of your own goes into the `WEBSOCKET_CLIENT_CA_BUNDLE` environment variable. --- ## Google Cloud Storage Reference ## ![](/img/icons/gcs.svg) Google Cloud Storage Reference Authentication options and data type handling for [GCS connections](../testing/gcs.md). GCS is accessed through the S3 interoperability layer (`type: s3` with `endpointUrl: https://storage.googleapis.com`). ### Server GCS is an S3-compatible server: ODCS defines no `gcs` server type, so the contract uses `type: s3` pointed at the [interoperability](https://cloud.google.com/storage/docs/interoperability) endpoint. ```yaml servers: - server: production type: s3 endpointUrl: https://storage.googleapis.com location: s3://my-bucket/orders/*.json # the S3 endpoint needs the s3:// scheme, not gs:// format: json delimiter: new_line # new_line, array, or none ``` ### Authentication | Variable | Example | Description | |---|---|---| | `DATACONTRACT_S3_ACCESS_KEY_ID` | `GOOG1EZZZ...` | The GCS [HMAC Key](https://cloud.google.com/storage/docs/authentication/hmackeys) ID | | `DATACONTRACT_S3_SECRET_ACCESS_KEY` | `PDWWpb...` | The GCS [HMAC Key](https://cloud.google.com/storage/docs/authentication/hmackeys) secret | The `location` in the `servers` block must use the `s3://` scheme (not `gs://`). ### Data types Same as [Amazon S3](./s3.md#data-types): CSV and JSON files are read as the contract's types (no type checks; JSON additionally validated against a JSON Schema derived from the contract), Parquet files are read as the contract's types too, so their type checks are reported as warnings (not supported yet), and Delta column types are checked against the contract's `logicalType` by category. `physicalType` is never checked against file sources. #### Logical type mapping When no `physicalType` is declared, the CLI derives the DuckDB column type from the `logicalType` when reading files. This table is generated from the converters in the CLI's code: | `logicalType` | CSV read type | Parquet read type | |---|---|---| | `string` | `VARCHAR` | `VARCHAR` | | `integer` | `BIGINT` | `INTEGER` | | `number` | `DOUBLE` | `DECIMAL` | | `boolean` | `BOOLEAN` | `BOOLEAN` | | `date` | `DATE` | `DATE` | | `timestamp` | `TIMESTAMP` | `TIMESTAMP WITH TIME ZONE` | | `time` | `TIME` | `TIME` | | `object` | `VARCHAR` | `STRUCT()` | | `array` | `VARCHAR` | `VARCHAR[]` | | `map` | `VARCHAR` | `MAP(VARCHAR, VARCHAR)` | | `vector` | `VARCHAR` | `FLOAT[]` | --- ## Apache Hive Reference ## ![](/img/icons/database.svg) Apache Hive Reference Authentication options and data type handling for [Hive connections](../testing/hive.md). ### Server ```yaml servers: - server: production type: hive host: hive.acme.com port: 10000 database: sales ``` ### Authentication | Variable | Example | Description | |---|---|---| | `DATACONTRACT_HIVE_USERNAME` | `analyst` | Username. Defaults to `hive` | | `DATACONTRACT_HIVE_PASSWORD` | `mysecretpassword` | Password. Defaults to `hive` | | `DATACONTRACT_HIVE_AUTH_MECHANISM` | `LDAP` | `PLAIN` (default), `NOSASL`, `LDAP`, or `GSSAPI` (Kerberos) | | `DATACONTRACT_HIVE_USE_SSL` | `true` | Whether to use TLS. Defaults to `false` | | `DATACONTRACT_HIVE_USE_HTTP_TRANSPORT` | `true` | Use HTTP transport (`hive.server2.transport.mode=http`) instead of binary thrift. Defaults to `false` | | `DATACONTRACT_HIVE_HTTP_PATH` | `cliservice` | HTTP path of HiveServer2 for HTTP transport. Defaults to empty | The defaults fit a HiveServer2 with `hive.server2.authentication=NONE`, which still expects a SASL `PLAIN` handshake with any user name. The connection goes through the [impyla](https://github.com/cloudera/impyla) driver. `host`, `port`, and `database` come from the contract's `servers` block, and can be overridden with `DATACONTRACT_HIVE_HOST`, `DATACONTRACT_HIVE_PORT`, and `DATACONTRACT_HIVE_DATABASE`. `port` defaults to `10000`. ### Data types #### Importing `datacontract import hive` reads `DESCRIBE FORMATTED` and takes the declared type as the `physicalType`: `STRING`/`VARCHAR`/`CHAR` → `string`, `INT`/`BIGINT`/`SMALLINT`/`TINYINT` → `integer`, `FLOAT`/`DOUBLE`/`DECIMAL` → `number`, `BOOLEAN` → `boolean`, `DATE` → `date`, `TIMESTAMP` → `timestamp`, `ARRAY<...>` → `array`, `STRUCT<...>` → `object`, `MAP<...>` → `map`, `BINARY` → `string`. Partition columns are imported with the other columns. #### Testing Hive supports **native type introspection**: the declared `physicalType` is checked against `DESCRIBE`. Length/precision is only enforced when the contract declares it (`varchar` matches `varchar(10)`), and the fields of a `struct` and the element type of an `array` are compared too. --- ## Apache Iceberg Reference ## ![](/img/icons/iceberg.svg) Apache Iceberg Reference Authentication and data type handling for [Apache Iceberg connections](../testing/iceberg.md). ### Server ```yaml servers: - server: production type: iceberg catalog: main # the catalog's name catalogUrl: https://polaris.example.com/api/catalog # the REST catalog endpoint namespace: sales # optional; prefixed to every table name warehouse: s3://warehouse # optional; passed to the catalog ``` ### Configuration | Environment variable | Purpose | |---|---| | `DATACONTRACT_ICEBERG_CREDENTIAL` | OAuth2 client credential for the catalog, as `client_id:client_secret` | | `DATACONTRACT_ICEBERG_TOKEN` | Bearer token for the catalog, instead of a credential | | `DATACONTRACT_ICEBERG_CATALOG_URL`, `DATACONTRACT_ICEBERG_CATALOG`, `DATACONTRACT_ICEBERG_NAMESPACE`, `DATACONTRACT_ICEBERG_WAREHOUSE` | Override the matching field of the contract's `servers` block | | `DATACONTRACT_ICEBERG_CATALOG_TYPE` | The pyiceberg catalog implementation: `rest` (default), `sql`, `glue`, `hive`, `dynamodb`. For `sql`, `catalogUrl` is the SQLAlchemy connection URI | | `DATACONTRACT_S3_ACCESS_KEY_ID`, `DATACONTRACT_S3_SECRET_ACCESS_KEY`, `DATACONTRACT_S3_SESSION_TOKEN`, `DATACONTRACT_S3_REGION` | Credentials for S3 data files and AWS catalog signing; omit to use the AWS credential chain with S3 Tables or Glue | | `DATACONTRACT_ICEBERG_S3_ENDPOINT` | Endpoint of an S3-compatible store (MinIO, Ceph) holding the data files | | `DATACONTRACT_ICEBERG_SIGNING_NAME` | Sign catalog requests with SigV4 for this AWS service (`s3tables`, `glue`); detected from `catalogUrl` for the AWS endpoints, so only needed behind a proxy | | `DATACONTRACT_ICEBERG_PROPERTIES` | Extra pyiceberg catalog properties, as a JSON object or `key=value,key=value` | ### Amazon S3 Tables S3 Tables exposes each table bucket as an Iceberg REST catalog. `catalogUrl` is the regional endpoint, `warehouse` the **table bucket ARN** (not an `s3://` location), and `namespace` the S3 Tables namespace. The CLI detects the signing service and region from the endpoint. AWS uses SigV4, not OAuth, for this endpoint; see the [AWS endpoint documentation](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-integrating-open-source.html). For installation, AWS SSO sign-in, import/test commands, and a quality-check example, follow the dedicated [Amazon S3 Tables testing guide](../testing/s3-tables.md). Use your own region, account, bucket, namespace, and an existing populated table. No Glue integration, Athena workgroup, or OAuth token is required for this direct endpoint. For import and testing, the identity needs `s3tables:GetTableBucket` on the bucket and `s3tables:GetTableMetadataLocation` and `s3tables:GetTableData` on the table. Listing, creating, or modifying tables requires additional permissions; AWS documents the [operation-to-permission mapping](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-integrating-open-source.html#endpoint-supported-api). The CLI resolves the AWS credential chain, including `AWS_PROFILE`, and forwards credentials to the Arrow data-file reader as well as signing catalog requests. Explicit `DATACONTRACT_S3_*` credentials take precedence; temporary credentials require `DATACONTRACT_S3_SESSION_TOKEN`. Credentials are not written into the imported contract. Data-file credentials are captured when the catalog is opened, so they must remain valid for the scan; reauthenticate and rerun if they expire. Do not rely on this direct endpoint vending data-file credentials. The generated server looks like this (the local catalog label defaults to `default`; use `--catalog s3tables` to name it): ```yaml servers: - server: production type: iceberg catalog: s3tables catalogUrl: https://s3tables.eu-central-1.amazonaws.com/iceberg warehouse: arn:aws:s3tables:eu-central-1:123456789012:bucket/my-table-bucket namespace: sales ``` You can also pass `--table sales.orders` without `--namespace`. The importer preserves that qualified name in the schema object's `physicalName`. S3 Tables supports one namespace level. The AWS Glue Data Catalog's Iceberg REST endpoint (`https://glue..amazonaws.com/iceberg`) also uses SigV4, but has different warehouse identifiers and authorization requirements. For S3 Tables through Glue, follow the [AWS Glue endpoint setup](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-integrating-glue-endpoint.html); it is a separate path from the direct S3 Tables endpoint above. #### Run the AWS integration test The testing guide documents [running the opt-in AWS integration suite](../testing/s3-tables.md#run-the-clis-aws-integration-suite), including its resource creation, cleanup, and permission requirements. The full list is on the [Configuration](../configuration.md) page. ### Data types #### Importing `datacontract import iceberg` reads the table schema from the catalog (or from a schema JSON file with `--source`): | Iceberg type | `logicalType` | |---|---| | `string`, `uuid` | `string` | | `int`, `long` | `integer` | | `float`, `double`, `decimal(p,s)` | `number` | | `boolean` | `boolean` | | `date` | `date` | | `timestamp`, `timestamptz` | `timestamp` | | `time` | `time` | | `struct<...>` | `object` with `properties` | | `list<...>` | `array` with `items` | | `map` | `map` with `key` and `value` | | `binary`, `fixed` | No `logicalType`; retained as `physicalType` because ODCS has no binary logical type | The Iceberg type is kept as `physicalType`, and the field id as the `icebergFieldId` custom property. #### Testing The table is scanned to Arrow and registered in DuckDB, so the type checks compare the declared `logicalType` against the DuckDB type of the Arrow column: structs become `STRUCT`, lists `LIST`, and maps `MAP`, so nested properties are type-checked too. `physicalType` checks against the catalog's declared type are not run for Iceberg; the logical type check runs instead. Binary fields still get presence and applicable quality checks, but no logical type check. SQL quality queries address the schema object's logical `name`, even when `physicalName` points to a different or qualified catalog table. The CLI reads the full selected tables into memory before checking them; use tables that fit in available memory. SQL rules and `--filters` do not reduce the Iceberg scan. #### Exporting `datacontract export iceberg` writes an Iceberg schema JSON from the contract. See the [Iceberg export guide](../exports/iceberg.md) for the mapping; import/export is not a lossless round-trip of every physical type. --- ## Apache Impala Reference ## ![](/img/icons/impala.svg) Apache Impala Reference Authentication options and data type handling for [Impala connections](../testing/impala.md). ### Server ```yaml servers: - server: production type: impala host: my-impala-host port: 21050 # 443 for a Cloudera Virtual Warehouse database: my_database ``` ### Authentication | Variable | Example | Description | |---|---|---| | `DATACONTRACT_IMPALA_USERNAME` | `analytics_user` | Username | | `DATACONTRACT_IMPALA_PASSWORD` | `mysecretpassword` | Password | | `DATACONTRACT_IMPALA_USE_SSL` | `true` | Whether to use SSL. Defaults to `true` | | `DATACONTRACT_IMPALA_AUTH_MECHANISM` | `LDAP` | `NOSASL` (default), `PLAIN`, `GSSAPI`, or `LDAP` | | `DATACONTRACT_IMPALA_USE_HTTP_TRANSPORT` | `true` | Whether to use HTTP transport instead of binary thrift. Defaults to `false` | | `DATACONTRACT_IMPALA_HTTP_PATH` | `cliservice` | HTTP path for the Impala service. Defaults to empty | Apart from `use_ssl`, these default to the same values as the underlying [impyla](https://github.com/cloudera/impyla) driver. `host`, `port`, and `database` come from the contract's `servers` block, and can be overridden with `DATACONTRACT_IMPALA_HOST`, `DATACONTRACT_IMPALA_PORT`, and `DATACONTRACT_IMPALA_DATABASE`. `port` defaults to `21050`, Impala's binary thrift port. #### Cloudera Virtual Warehouse A Cloudera Virtual Warehouse terminates LDAP over HTTPS on port 443, so all three transport options need setting — otherwise the client speaks binary thrift to an HTTPS endpoint and the handshake fails with `TSocket read 0 bytes`: ```bash # .env DATACONTRACT_IMPALA_USERNAME=analytics_user DATACONTRACT_IMPALA_PASSWORD=mysecretpassword DATACONTRACT_IMPALA_AUTH_MECHANISM=LDAP DATACONTRACT_IMPALA_USE_HTTP_TRANSPORT=true DATACONTRACT_IMPALA_HTTP_PATH=cliservice ``` with `port: 443` in the contract's `servers` block. ### Data types #### Importing There is no direct Impala importer. Import a `SHOW CREATE TABLE` DDL with `datacontract import sql --dialect spark` (Impala DDL is Hive-compatible): `STRING`/`VARCHAR`/`CHAR` → `string`, `INT`/`BIGINT`/`SMALLINT`/`TINYINT` → `integer`, `FLOAT`/`DOUBLE`/`DECIMAL` → `number`, `BOOLEAN` → `boolean`, `DATE` → `date`, `TIMESTAMP` → `timestamp`, `ARRAY<...>` → `array`, `STRUCT<...>` → `object`, `BINARY` → `string` (format `binary`), `MAP<...>` → no logical type. #### Testing Impala does **not** support native type introspection — the declared `physicalType` is not compared against the catalog. Instead, the `logicalType` is checked by category (`Check that field x has type y`): the actual column type is normalized to one of the ODCS logical types and compared, with `integer` and `number` treated as compatible. --- ## Kafka Reference ## ![](/img/icons/kafka.svg) Kafka Reference Authentication options and data type handling for [Kafka connections](../testing/kafka.md). ### Server ```yaml servers: - server: production type: kafka host: abc-12345.eu-central-1.aws.confluent.cloud:9092 format: json # or avro ``` ### Authentication | Variable | Example | Description | |---|---|---| | `DATACONTRACT_KAFKA_SASL_USERNAME` | `xxx` | The SASL username (key) | | `DATACONTRACT_KAFKA_SASL_PASSWORD` | `xxx` | The SASL password (secret) | | `DATACONTRACT_KAFKA_SASL_MECHANISM` | `PLAIN` | Default `PLAIN`; also `SCRAM-SHA-256`, `SCRAM-SHA-512` | If no username/password is set, the CLI connects without authentication (e.g. a local broker). `host` and `format` come from the contract's `servers` block; the topic is the first schema's `physicalName`, or its `name` if unset. A username and password switch the connection to `SASL_SSL`. ### Reading the topic | Variable | Example | Description | |---|---|---| | `DATACONTRACT_KAFKA_MAX_MESSAGES` | `100000` | Stop after this many messages. Unset by default: the whole topic is read | | `DATACONTRACT_KAFKA_TIMEOUT` | `30` | Seconds to wait for a message before giving up. Default `30`; the timer resets whenever a message arrives, so a slow read is never cut short | | `DATACONTRACT_KAFKA_GROUP_PREFIX` | `my-team-` | Replaces the default `datacontract-cli-` consumer group prefix. A UUID is still appended so each run gets a unique group ID. Useful when the service account's ACLs restrict which consumer group prefixes it may use | Every partition is read from its earliest offset up to the latest offset at the time the run starts, so messages produced while the checks are running are not included. Offsets are never committed, and each run uses its own consumer group, so testing a topic does not disturb a real consumer. Messages with no value (compaction tombstones) are skipped. All messages are held in memory. `DATACONTRACT_KAFKA_MAX_MESSAGES` bounds that on a large topic, at the cost of checking a sample rather than the whole topic — the run reports when it has done so. ### Schema Registry For `format: avro`, the messages have to be decoded with the exact Avro schema they were _written_ with — Avro is positionally encoded, so a schema that merely looks similar decodes to garbage. Topics written through the Confluent Schema Registry carry the id of that schema in a 5-byte prefix on every message. Point the CLI at the registry so it resolves the id, instead of falling back to the schema derived from the data contract: | Variable | Example | Description | |---|---|---| | `DATACONTRACT_KAFKA_SCHEMA_REGISTRY_URL` | `https://psrc-12345.eu-central-1.aws.confluent.cloud` | The schema registry base URL | | `DATACONTRACT_KAFKA_SCHEMA_REGISTRY_USERNAME` | `xxx` | The registry API key (optional) | | `DATACONTRACT_KAFKA_SCHEMA_REGISTRY_PASSWORD` | `xxx` | The registry API secret (optional) | Messages without the prefix (plain Avro) are decoded with the schema derived from the data contract, so a topic that mixes both still works, as does a topic whose schema evolved across several registry ids. ### Data types #### Importing For Avro-encoded topics, import the schema with `datacontract import avro`. The Avro type is kept as `physicalType`: | Avro type | `logicalType` | |---|---| | `string` | `string` | | `int`, `long` | `integer` | | `float`, `double` | `number` | | `boolean` | `boolean` | | `record` | `object` with nested `properties` | | `array` | `array` | | `map` | `object` (values not expanded) | | `enum` | `string` (symbols in the `avroSymbols` custom property) | | `bytes`, `fixed` | `array` | Avro logical type annotations take precedence: `decimal` → `number` (with precision/scale), `date` → `date`, `uuid`/`duration`/`time-millis`/`time-micros` → `string`. Unions must be `[null, T]` and make the field optional. #### Testing For `format: json` and `format: avro`, no type checks are generated — violations surface as decode errors or as value-check failures from `logicalTypeOptions`. JSON messages are decoded as the types the contract declares; a message that is not a JSON object becomes a row of nulls and is reported as missing values. Avro messages are decoded with the schema they were written with — from the [schema registry](#schema-registry) when the message carries a schema id, otherwise the one derived from the contract — and keep that schema's types: | Avro type | Read as | |---|---| | `boolean` | `BOOLEAN` | | `int`, `long` | `INTEGER`, `BIGINT` | | `float`, `double` | `FLOAT`, `DOUBLE` | | `string`, `enum` | `VARCHAR` | | `bytes`, `fixed` | `BLOB` | | `record` | `STRUCT` | | `array` | `LIST` | | `map` | `MAP(VARCHAR, …)` | | `decimal` (on `bytes`/`fixed`) | `DECIMAL(precision, scale)` | | `date` | `DATE` | | `time-millis`, `time-micros` | `TIME` | | `timestamp-millis`, `timestamp-micros` | `TIMESTAMP WITH TIME ZONE` | | `local-timestamp-millis`, `local-timestamp-micros` | `TIMESTAMP` | A union must be `[null, T]`: which type of a wider union a message carries is known only per message, and a column has one type. Such a union is rejected with an error rather than guessed at. --- ## Local Files Reference ## ![](/img/icons/local.svg) Local Files Reference Data type handling for [local file connections](../testing/local.md). No environment variables are needed. ### Server ```yaml servers: - server: local type: local path: ./*.parquet # glob patterns and a {model} placeholder are supported format: parquet # parquet, json, csv, xml, xlsx, or delta ``` ### Data types #### Importing `datacontract import csv` samples the file with DuckDB and infers per column: | Inferred DuckDB type | `logicalType` | |---|---| | `BOOLEAN` | `boolean` | | `INTEGER`, `BIGINT` | `integer` | | `DOUBLE` | `number` | | `VARCHAR` | `string` | It also profiles the data: `required` (no nulls), `unique` (all distinct), `examples`, and `minimum`/`maximum` for numeric columns. Values that all match an email or UUID pattern get `logicalTypeOptions.format`. `datacontract import json` inspects the first 20 records: booleans, integers, and floats map to `boolean`/`integer`/`number`; strings map to `string` (with a detected `format` of `date`, `date-time`, `email`, or `uuid` in `logicalTypeOptions`); objects and arrays map to `object`/`array` with nested properties. When records disagree, the wider type wins (integer + float → `number`; anything + string → `string`). `datacontract import parquet` reads types from the Parquet metadata. #### Testing Type handling depends on the `format` in the `servers` block: | `format` | How types are handled | |---|---| | `csv` | The file is read **as the contract's types** — no type checks are generated; a value that can't be coerced surfaces as a read error. | | `json` | Same as CSV, plus every record is validated against a JSON Schema derived from the contract's `logicalType`s (with `format` options like `date-time`, `uuid`). | | `xml` | Like CSV: the documents are read **as the contract's types**, with nested elements as structs and repeated elements as lists. | | `xlsx` | Each cell is read as the contract's type of its column, including Excel dates stored as numbers. A cell that has a value of another type fails the type check of its column. | | `parquet` | The file is read **as the contract's types**, like CSV, so checking its types is not supported yet, and a value that can't be cast surfaces as a read error. | | `delta` | Column types come from the Delta table; the contract's `logicalType` is checked by category. | **Presence.** For `json` and `xml`, only the required top-level properties are checked for presence: a document may leave out a property that is not required, even in every record. For the other formats, every column of the contract must exist in the file. `physicalType` is never checked against file sources — declare `logicalType` (and `logicalTypeOptions` for value constraints). The `path` supports glob patterns and a `{model}` placeholder. #### Logical type mapping When no `physicalType` is declared, the CLI derives the DuckDB column type from the `logicalType` when reading files. This table is generated from the converters in the CLI's code: | `logicalType` | CSV read type | Parquet read type | |---|---|---| | `string` | `VARCHAR` | `VARCHAR` | | `integer` | `BIGINT` | `INTEGER` | | `number` | `DOUBLE` | `DECIMAL` | | `boolean` | `BOOLEAN` | `BOOLEAN` | | `date` | `DATE` | `DATE` | | `timestamp` | `TIMESTAMP` | `TIMESTAMP WITH TIME ZONE` | | `time` | `TIME` | `TIME` | | `object` | `VARCHAR` | `STRUCT()` | | `array` | `VARCHAR` | `VARCHAR[]` | | `map` | `VARCHAR` | `MAP(VARCHAR, VARCHAR)` | | `vector` | `VARCHAR` | `FLOAT[]` | --- ## MySQL Reference ## ![](/img/icons/mysql.svg) MySQL Reference Authentication options and data type handling for [MySQL connections](../testing/mysql.md). ### Server ```yaml servers: - server: mysql type: mysql host: localhost port: 3306 database: mydb ``` ### Authentication | Variable | Example | Description | |---|---|---| | `DATACONTRACT_MYSQL_USERNAME` | `root` | Username | | `DATACONTRACT_MYSQL_PASSWORD` | `mysecretpassword` | Password | `host`, `port` (default 3306), and `database` come from the contract's `servers` block, and can be overridden with `DATACONTRACT_MYSQL_HOST`, `DATACONTRACT_MYSQL_PORT`, and `DATACONTRACT_MYSQL_DATABASE`. ### Data types #### Importing `datacontract import sql --dialect mysql` maps DDL types as follows; the normalized SQL type is kept as `physicalType`. | MySQL type | `logicalType` | |---|---| | `VARCHAR`, `CHAR`, `TEXT` | `string` (`maxLength` for varchar/char) | | `INT`, `INTEGER`, `BIGINT`, `SMALLINT` | `integer` | | `NUMERIC`, `DECIMAL`, `FLOAT`, `DOUBLE` | `number` | | `BOOLEAN` | `boolean` | | `DATE` | `date` | | `TIME` | `time` | | `DATETIME`, `TIMESTAMP` | `timestamp` | | `BLOB`, `BINARY`, `VARBINARY` | `string` (format `binary`) | | `JSON` | `object` | | `ENUM(...)`, `YEAR`, `TINYINT(1)`, `INT UNSIGNED` | *(unset)* — `physicalType` is still written | #### Testing MySQL is reached through DuckDB's MySQL scanner and does **not** support native type introspection — the declared `physicalType` is not compared against the catalog. Instead, the `logicalType` is checked by category (`Check that field x has type y`), with `integer` and `number` treated as compatible. #### Logical type mapping When no `physicalType` is declared, the CLI derives the native type from the `logicalType` — for example in `datacontract export sql` and the dbt exports. This table is generated from the converter in the CLI's code: | `logicalType` | MySQL type | |---|---| | `string` | `varchar(255)` | | `integer` | `int` | | `number` | `decimal` | | `boolean` | `boolean` | | `date` | `date` | | `timestamp` | `timestamp` | | `time` | `time` | | `object` | `json` | | `array` | `json` | | `map` | `json` | | `vector` | `vector(1536)` | --- ## Oracle Reference ## ![](/img/icons/oracle.svg) Oracle Reference Authentication options and data type handling for [Oracle connections](../testing/oracle.md). ### Server ```yaml servers: - server: oracle type: oracle host: localhost port: 1521 serviceName: ORCL schema: ADMIN ``` ### Authentication | Variable | Example | Description | |---|---|---| | `DATACONTRACT_ORACLE_USERNAME` | `system` | Username | | `DATACONTRACT_ORACLE_PASSWORD` | `0x162e53` | Password | | `DATACONTRACT_ORACLE_CLIENT_DIR` | `C:\oracle\client` | Path to an Oracle Instant Client installation (required for thick mode) | `host`, `port` (default 1521), `service_name`, and `schema` come from the contract's `servers` block; `host`, `port`, and `service_name` can be overridden with `DATACONTRACT_ORACLE_HOST`, `DATACONTRACT_ORACLE_PORT`, and `DATACONTRACT_ORACLE_SERVICE_NAME`. ### Data types #### Importing `datacontract import sql --dialect oracle` maps DDL types as follows; the normalized SQL type is kept as `physicalType`. | Oracle type | `logicalType` | |---|---| | `VARCHAR2`, `NVARCHAR2`, `CHAR`, `NCHAR` | `string` (`maxLength` for varchar/char) | | `CLOB`, `NCLOB` | `string` | | `NUMBER` | `number` (precision/scale as custom properties) | | `BINARY_FLOAT`, `BINARY_DOUBLE`, `FLOAT` | `number` | | `DATE` | `date` | | `TIMESTAMP`, `TIMESTAMP WITH (LOCAL) TIME ZONE` | `timestamp` | | `RAW`, `BLOB`, `BFILE` | `string` (format `binary`) | | `XMLTYPE`, `ROWID`, `INTERVAL` types | *(unset)* — `physicalType` is still written | #### Testing Oracle supports **native type introspection**: the declared `physicalType` is checked against `ALL_TAB_COLUMNS`. Length is reconstructed for character and `RAW` types, precision/scale for `NUMBER`; parameters are only enforced when the contract declares them. Types that sqlglot can't parse (`ROWID`, `INTERVAL`) are compared by name. A `physicalType` that can't be interpreted falls back to the logical type category comparison. #### Logical type mapping When no `physicalType` is declared, the CLI derives the native type from the `logicalType` — for example in `datacontract export sql` and the dbt exports. This table is generated from the converter in the CLI's code: | `logicalType` | Oracle type | |---|---| | `string` | `NVARCHAR2(2000)` | | `integer` | `NUMBER` | | `number` | `NUMBER` | | `boolean` | `CHAR(1)` | | `date` | `DATE` | | `timestamp` | `TIMESTAMP(6) WITH TIME ZONE` | | `time` | `DATE` | | `object` | `CLOB` | | `array` | `CLOB` | | `map` | `CLOB` | | `vector` | — | --- ## Postgres Reference ## ![](/img/icons/postgres.svg) Postgres Reference Authentication options and data type handling for [Postgres connections](../testing/postgres.md). ### Server ```yaml servers: - server: postgres type: postgres host: localhost port: 5432 database: postgres schema: public ``` ### Authentication | Variable | Example | Description | |---|---|---| | `DATACONTRACT_POSTGRES_USERNAME` | `postgres` | Username | | `DATACONTRACT_POSTGRES_PASSWORD` | `mysecretpassword` | Password | `host`, `port` (default 5432), `database`, and `schema` come from the contract's `servers` block, and can be overridden with `DATACONTRACT_POSTGRES_HOST`, `DATACONTRACT_POSTGRES_PORT`, `DATACONTRACT_POSTGRES_DATABASE`, and `DATACONTRACT_POSTGRES_SCHEMA`. For `datacontract import postgres`, they come from `--source`, `--port`, `--database`, and `--schema`. ### Data types #### Importing `datacontract import postgres` reads the declared type from `information_schema.columns` and keeps it as `physicalType` exactly as the test path reads it back (`character varying(36)`, `numeric(10,2)`, `timestamp with time zone`), so an imported contract passes `datacontract test` without hand-editing. `datacontract import sql --dialect postgres` maps DDL types as follows; the normalized SQL type is kept as `physicalType`. | Postgres type | `logicalType` | |---|---| | `VARCHAR`, `CHAR`, `TEXT` | `string` (`maxLength` for varchar/char) | | `SMALLINT`, `INT`, `INTEGER`, `BIGINT` | `integer` | | `NUMERIC`, `DECIMAL`, `REAL`, `DOUBLE PRECISION`, `MONEY` | `number` | | `BOOLEAN` | `boolean` | | `DATE` | `date` | | `TIME` | `time` | | `TIMESTAMP`, `TIMESTAMPTZ` | `timestamp` | | `BYTEA` | `string` (format `binary`) | | `JSON` | `object` | | `XML` | `string` | | `UUID`, `SERIAL`, `BIGSERIAL`, `JSONB`, `INTERVAL`, `INET`, array types (`INT[]`) | *(unset)* — `physicalType` is still written | #### Testing Postgres supports **native type introspection**: the declared `physicalType` is checked against `information_schema.columns`. Note that on Postgres `text` and `varchar` are **distinct types** — declare the one the column actually has. `decimal` and `numeric` are interchangeable, as are timezone variants of timestamps; length/precision is only enforced when declared (`varchar` matches `varchar(255)`, `varchar(255)` does not match `varchar(100)`). A `physicalType` that isn't valid Postgres SQL falls back to the logical type category comparison. The same applies to Postgres-compatible databases tested with `type: postgres` (e.g. RisingWave). #### Logical type mapping When no `physicalType` is declared, the CLI derives the native type from the `logicalType` — for example in `datacontract export sql` and the dbt exports. This table is generated from the converter in the CLI's code: | `logicalType` | Postgres type | |---|---| | `string` | `text` | | `integer` | `integer` | | `number` | `numeric` | | `boolean` | `boolean` | | `date` | `date` | | `timestamp` | `timestamptz` | | `time` | `time` | | `object` | `jsonb` | | `array` | `text[]` | | `map` | `jsonb` | | `vector` | `vector(1536)` | --- ## Amazon Redshift Reference ## ![](/img/icons/redshift.svg) Amazon Redshift Reference Authentication options and data type handling for [Redshift connections](../testing/redshift.md). ### Server ```yaml servers: - server: redshift type: redshift host: my-workgroup.123456789012.us-east-1.redshift-serverless.amazonaws.com port: 5439 database: dev schema: analytics ``` ### Authentication `datacontract test` and `datacontract import redshift` authenticate identically. `host`, `port` (default 5439), `database`, and `schema` come from the contract's `servers` block, and can be overridden with `DATACONTRACT_REDSHIFT_HOST`, `DATACONTRACT_REDSHIFT_PORT`, `DATACONTRACT_REDSHIFT_DATABASE`, and `DATACONTRACT_REDSHIFT_SCHEMA`; for the import they are passed as `--source`, `--port`, `--database`, and `--schema`. | Variable | Example | Description | |---|---|---| | `DATACONTRACT_REDSHIFT_AUTHENTICATION` | `iam` | `password` or `iam`; only needed to override what is inferred | | `DATACONTRACT_REDSHIFT_USERNAME` | `awsuser` | Database user. Required for `password`; in `iam` mode it selects the legacy API (see below) | | `DATACONTRACT_REDSHIFT_DB_USER` | `analytics_user` | Database user for `iam` mode only, taking precedence over `DATACONTRACT_REDSHIFT_USERNAME` | | `DATACONTRACT_REDSHIFT_PASSWORD` | `mysecretpassword` | Password (`password` mode only) | | `DATACONTRACT_REDSHIFT_SSLMODE` | `verify-full` | TLS mode passed to the driver. Defaults to `require` in `iam` mode, and to the driver's own default (`prefer`) otherwise | #### IAM authentication The method is inferred: setting `DATACONTRACT_REDSHIFT_PASSWORD` selects a database login, otherwise resolvable AWS credentials select IAM. Set `DATACONTRACT_REDSHIFT_AUTHENTICATION` explicitly only to override that. With IAM, the CLI asks AWS for temporary database credentials and uses them to log in — no database password anywhere. The AWS credentials themselves come from the same variables Athena uses (`DATACONTRACT_S3_ACCESS_KEY_ID`, `DATACONTRACT_S3_SECRET_ACCESS_KEY`, `DATACONTRACT_S3_SESSION_TOKEN`, `DATACONTRACT_S3_REGION`), and fall back to the standard AWS chain: `aws sso login`, `AWS_PROFILE`, EC2/ECS/EKS instance roles, or GitHub OIDC in CI. Which AWS API is called depends on the endpoint and whether a database user is set — either `DATACONTRACT_REDSHIFT_DB_USER` or, failing that, `DATACONTRACT_REDSHIFT_USERNAME`: | Endpoint | Database user set | API | |---|---|---| | Serverless | — | `redshift-serverless:GetCredentials` | | Provisioned | no | `redshift:GetClusterCredentialsWithIAM` — the database user is derived from your IAM identity | | Provisioned | yes | `redshift:GetClusterCredentials` — credentials are requested for that database user | Use `DATACONTRACT_REDSHIFT_DB_USER` when `DATACONTRACT_REDSHIFT_USERNAME` is already set for something else, such as a database login used by a different environment: it names the IAM database user explicitly, rather than inheriting a username that was never meant to select the legacy API. The workgroup or cluster identifier and the region are derived from the endpoint host in the `servers` block, so a standard endpoint needs no extra configuration. Custom domains and VPC endpoints don't follow that naming, so they need an override: | Variable | Example | Description | |---|---|---| | `DATACONTRACT_REDSHIFT_WORKGROUP` | `my-workgroup` | Serverless workgroup name | | `DATACONTRACT_REDSHIFT_CLUSTER_IDENTIFIER` | `my-cluster` | Provisioned cluster identifier | | `DATACONTRACT_REDSHIFT_REGION` | `eu-central-1` | AWS region (otherwise `DATACONTRACT_S3_REGION`, then the host) | | `DATACONTRACT_REDSHIFT_DURATION_SECONDS` | `3600` | Lifetime of the temporary credentials (900–3600) | | `DATACONTRACT_REDSHIFT_AUTO_CREATE` | `true` | Create the database user if it doesn't exist (`GetClusterCredentials` only) | | `DATACONTRACT_REDSHIFT_DB_GROUPS` | `readers,analysts` | Database groups the user joins for the session (`GetClusterCredentials` only) | The AWS identity needs permission to call the API from the table above — `redshift-serverless:GetCredentials` on the workgroup, or `redshift:GetClusterCredentialsWithIAM` / `redshift:GetClusterCredentials` on the `dbname:`/`dbuser:` resources (plus `redshift:JoinGroup` when using `DB_GROUPS`). Credentials are minted per connection and expire, which is why a long-running process may need to reconnect. ### Data types #### Importing `datacontract import redshift` reads the declared types from the `SVV_COLUMNS` catalog view and writes them as `physicalType` in the catalog's own spelling, including length and precision (`character varying(36)`, `numeric(10,2)`). That is exactly what the physical type check reads back during a test, so an imported contract passes on the first run. The `logicalType` is derived with the same mapping as the SQL import below. `datacontract import sql --dialect redshift` maps DDL types like the [Postgres dialect](./postgres.md#importing): `VARCHAR`/`CHAR`/`TEXT` → `string`, integer types → `integer`, `NUMERIC`/`DECIMAL`/`REAL`/`DOUBLE PRECISION` → `number`, `BOOLEAN` → `boolean`, `DATE` → `date`, `TIME` → `time`, `TIMESTAMP`/`TIMESTAMP WITH TIME ZONE` → `timestamp`. Redshift-specific types without a portable equivalent (`SUPER`, `GEOMETRY`) get no `logicalType`; the `physicalType` is still written. #### Testing Redshift supports **native type introspection**: the declared `physicalType` is checked against `information_schema.columns`. `decimal` and `numeric` are interchangeable, as are timezone variants of timestamps; length/precision is only enforced when declared. A `physicalType` that can't be interpreted falls back to the logical type category comparison. #### Logical type mapping When no `physicalType` is declared, the CLI derives the native type from the `logicalType` — for example in `datacontract export sql` and the dbt exports. This table is generated from the converter in the CLI's code: | `logicalType` | Redshift type | |---|---| | `string` | `text` | | `integer` | `integer` | | `number` | `numeric` | | `boolean` | `boolean` | | `date` | `date` | | `timestamp` | `timestamptz` | | `time` | `time` | | `object` | `jsonb` | | `array` | `text[]` | | `map` | `jsonb` | | `vector` | `vector(1536)` | --- ## Amazon S3 Reference ## ![](/img/icons/s3.svg) Amazon S3 Reference Authentication options and data type handling for [S3 connections](../testing/s3.md). ### Server #### JSON ```yaml servers: - server: production type: s3 endpointUrl: https://minio.example.com # not needed with AWS S3 location: s3://bucket-name/path/*/*.json format: json delimiter: new_line # new_line, array, or none ``` #### Delta ```yaml servers: - server: production type: s3 endpointUrl: https://minio.example.com # not needed with AWS S3 location: s3://bucket-name/path/table.delta # folder with parquet files and _delta_log format: delta ``` ### Authentication An existing AWS session is used when no key is set — `aws sso login`, `AWS_PROFILE`, EC2/ECS/EKS instance roles, or GitHub OIDC in CI. With nothing resolvable the objects are read unsigned, which is what public buckets need. The variables below override that: | Variable | Example | Description | |---|---|---| | `DATACONTRACT_S3_REGION` | `eu-central-1` | Region of the S3 bucket | | `DATACONTRACT_S3_ACCESS_KEY_ID` | `AKIAXV5Q5Q...` | AWS Access Key ID | | `DATACONTRACT_S3_SECRET_ACCESS_KEY` | `93S7LRrJ...` | AWS Secret Access Key | | `DATACONTRACT_S3_SESSION_TOKEN` | `AQoDYXdzEJr...` | AWS temporary session token (optional) | If `DATACONTRACT_S3_ACCESS_KEY_ID` is not set, no credentials are configured — public buckets and ambient credentials still work. For S3-compatible storage (MinIO), set `endpointUrl` in the `servers` block; the CLI then uses path-style addressing. ### Data types Files on S3 are read with DuckDB. Type handling depends on the `format` in the `servers` block: | `format` | How types are handled | |---|---| | `csv` | The file is read **as the contract's types** — no type checks are generated; a value that can't be coerced surfaces as a read error. | | `json` | Same as CSV, plus every record is validated against a JSON Schema derived from the contract's `logicalType`s (with `format` options like `date-time`, `uuid`). | | `parquet` | The file is read **as the contract's types**, like CSV, so checking its types is not supported yet, and a value that can't be cast surfaces as a read error. | | `delta` | Column types come from the Delta table; the contract's `logicalType` is checked by category. | `physicalType` is never checked against file sources — declare `logicalType` (and `logicalTypeOptions` for value constraints). See [Local files](./local.md) for how CSV/JSON imports infer types. #### Logical type mapping When no `physicalType` is declared, the CLI derives the DuckDB column type from the `logicalType` when reading files. This table is generated from the converters in the CLI's code: | `logicalType` | CSV read type | Parquet read type | |---|---|---| | `string` | `VARCHAR` | `VARCHAR` | | `integer` | `BIGINT` | `INTEGER` | | `number` | `DOUBLE` | `DECIMAL` | | `boolean` | `BOOLEAN` | `BOOLEAN` | | `date` | `DATE` | `DATE` | | `timestamp` | `TIMESTAMP` | `TIMESTAMP WITH TIME ZONE` | | `time` | `TIME` | `TIME` | | `object` | `VARCHAR` | `STRUCT()` | | `array` | `VARCHAR` | `VARCHAR[]` | | `map` | `VARCHAR` | `MAP(VARCHAR, VARCHAR)` | | `vector` | `VARCHAR` | `FLOAT[]` | --- ## Snowflake Reference ## ![](/img/icons/snowflake.svg) Snowflake Reference Authentication options and data type handling for [Snowflake connections](../testing/snowflake.md). ### Server ```yaml servers: - server: snowflake type: snowflake account: abcdefg-xn12345 database: ORDER_DB schema: ORDERS_PII_V2 ``` ### Authentication The variables below are passed as connection parameters to the [snowflake-connector-python](https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-api#connect) driver. Set the ones required by your workspace's `authenticator` mode. Any other `DATACONTRACT_SNOWFLAKE_`-prefixed variable is ignored with a warning. | Connection parameter | Environment variable | |---|---| | `user` | `DATACONTRACT_SNOWFLAKE_USERNAME` | | `password` | `DATACONTRACT_SNOWFLAKE_PASSWORD` | | `warehouse` | `DATACONTRACT_SNOWFLAKE_WAREHOUSE` | | `role` | `DATACONTRACT_SNOWFLAKE_ROLE` | | `authenticator` | `DATACONTRACT_SNOWFLAKE_AUTHENTICATOR` | | `token` | `DATACONTRACT_SNOWFLAKE_TOKEN` | | `passcode` | `DATACONTRACT_SNOWFLAKE_PASSCODE` | | `private_key_file` | `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_FILE` | | `private_key_file_pwd` | `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_FILE_PWD` | | `private_key` | `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY` | | `login_timeout` | `DATACONTRACT_SNOWFLAKE_LOGIN_TIMEOUT` | | `network_timeout` | `DATACONTRACT_SNOWFLAKE_NETWORK_TIMEOUT` | | `socket_timeout` | `DATACONTRACT_SNOWFLAKE_SOCKET_TIMEOUT` | | `host` | `DATACONTRACT_SNOWFLAKE_HOST` | | `port` | `DATACONTRACT_SNOWFLAKE_PORT` | `account`, `database`, and `schema` come from the contract's `servers` block, and can be overridden with `DATACONTRACT_SNOWFLAKE_ACCOUNT`, `DATACONTRACT_SNOWFLAKE_DATABASE`, and `DATACONTRACT_SNOWFLAKE_SCHEMA`. For key-pair auth, set `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_FILE` to the path of the key file and `..._PRIVATE_KEY_FILE_PWD` to its passphrase, if it has one. `..._PRIVATE_KEY` takes the key itself rather than a path. `..._AUTHENTICATOR` can stay unset: the driver switches to key-pair auth whenever a key is given. #### Deprecated variables Earlier versions documented three names the driver has never accepted, so setting them had no effect. They now work as synonyms and log a deprecation warning; set the replacement instead. If both are set, the replacement wins. | Deprecated | Use instead | |---|---| | `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_PATH` | `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_FILE` | | `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_PASSPHRASE` | `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_FILE_PWD` | | `DATACONTRACT_SNOWFLAKE_CONNECTION_TIMEOUT` | `DATACONTRACT_SNOWFLAKE_LOGIN_TIMEOUT` | #### Import-specific options `datacontract import snowflake` additionally supports: | Variable | Description | |---|---| | `DATACONTRACT_SNOWFLAKE_HOME` | Directory containing a [connections.toml](https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-connect#connecting-using-the-connections-toml-file) | | `DATACONTRACT_SNOWFLAKE_CONNECTIONS_FILE` | Path to a connections.toml file | | `DATACONTRACT_SNOWFLAKE_DEFAULT_CONNECTION_NAME` | Connection name within connections.toml | The `SNOWFLAKE_`-prefixed equivalents work as fallbacks. If no password is set, the import falls back to browser-based SSO (`externalbrowser`). ### Data types #### Importing `datacontract import snowflake` reads `INFORMATION_SCHEMA.COLUMNS` and maps types as follows. The `physicalType` keeps the full native type including length/precision (e.g. `NUMBER(38, 0)`, `TEXT(16777216)`). | Snowflake type | `logicalType` | Notes | |---|---|---| | `TEXT`, `VARCHAR` | `string` | `maxLength` from `CHARACTER_MAXIMUM_LENGTH` | | `NUMBER` (incl. `INT`/`BIGINT` aliases) | `number` | precision/scale as custom properties | | `FLOAT`, `DOUBLE` | `number` | | | `BOOLEAN` | `boolean` | | | `DATE` | `date` | | | `TIMESTAMP_NTZ` / `_LTZ` / `_TZ` | `timestamp` | | | `TIME` | `time` | | | `BINARY` | `string` (format `binary`) | | | `ARRAY` | `array` | | | `VARIANT`, `OBJECT`, `GEOGRAPHY`, `GEOMETRY` | *(unset)* | `physicalType` is still written | Columns also get `required` (from `IS_NULLABLE`), `unique` (from `IS_IDENTITY`), and custom properties for `ordinalPosition`, `default`, `precision`, `scale`, `characterSet`, and `collation`. #### Testing Snowflake supports **native type introspection**: the declared `physicalType` is checked against the actual catalog type. Snowflake-specific leniency: `VARCHAR`/`TEXT`/`NVARCHAR` are treated as the same family, as are `DECIMAL`/`INT`/`BIGINT`/`SMALLINT`/`TINYINT` (Snowflake stores all of them as `NUMBER`), and `DOUBLE`/`FLOAT`. Structured `OBJECT(...)`/`ARRAY(...)`/`MAP(...)` columns are introspected via `SHOW COLUMNS` and compared including their nesting. #### Logical type mapping When no `physicalType` is declared, the CLI derives the native type from the `logicalType` — for example in `datacontract export sql` and the dbt exports. This table is generated from the converter in the CLI's code: | `logicalType` | Snowflake type | |---|---| | `string` | `STRING` | | `integer` | `NUMBER` | | `number` | `NUMBER` | | `boolean` | `BOOLEAN` | | `date` | `DATE` | | `timestamp` | `TIMESTAMP_TZ` | | `time` | `TIME` | | `object` | `OBJECT` | | `array` | `ARRAY` | | `map` | `MAP(VARCHAR, VARCHAR)` | | `vector` | `VECTOR(FLOAT, 1536)` | --- ## Microsoft SQL Server Reference ## ![](/img/icons/sqlserver.svg) Microsoft SQL Server Reference Authentication options and data type handling for [SQL Server connections](../testing/sqlserver.md). ### Server ```yaml servers: - server: production type: sqlserver host: localhost port: 1433 database: mydb schema: dbo driver: ODBC Driver 18 for SQL Server ``` ### Authentication | Variable | Example | Description | |---|---|---| | `DATACONTRACT_SQLSERVER_AUTHENTICATION` | `sql` | `sql` (default), `cli` (uses `az login`), `windows`, `ActiveDirectoryPassword`, `ActiveDirectoryServicePrincipal`, `ActiveDirectoryInteractive` (Windows only) | | `DATACONTRACT_SQLSERVER_USERNAME` | `root` | Username (for `sql`, `ActiveDirectoryPassword`, `ActiveDirectoryInteractive`) | | `DATACONTRACT_SQLSERVER_PASSWORD` | `toor` | Password (for `sql` and `ActiveDirectoryPassword`) | | `DATACONTRACT_SQLSERVER_CLIENT_ID` | `a3cf5d29-...` | Client ID (for `ActiveDirectoryServicePrincipal`) | | `DATACONTRACT_SQLSERVER_CLIENT_SECRET` | `kX9~Qr2Lm...` | Client secret (for `ActiveDirectoryServicePrincipal`) | | `DATACONTRACT_SQLSERVER_TRUST_SERVER_CERTIFICATE` | `True` | Trust self-signed certificate | | `DATACONTRACT_SQLSERVER_ENCRYPTED_CONNECTION` | `True` | Use SSL | | `DATACONTRACT_SQLSERVER_DRIVER` | `ODBC Driver 18 for SQL Server` | ODBC driver name | The `cli` mode reuses an `az login` session: the CLI fetches an Entra ID access token and hands it to the ODBC driver, so it works on macOS and Linux too; it needs the `azure` extra (`datacontract-cli[sqlserver,azure]`). `ActiveDirectoryInteractive` is only offered by the Microsoft ODBC driver on Windows. `host`, `port`, `database`, `schema`, and `driver` come from the contract's `servers` block; `host`, `port`, and `database` can be overridden with `DATACONTRACT_SQLSERVER_HOST`, `DATACONTRACT_SQLSERVER_PORT`, and `DATACONTRACT_SQLSERVER_DATABASE`. #### Deprecated variables | Deprecated | Use instead | |---|---| | `DATACONTRACT_SQLSERVER_TRUSTED_CONNECTION` | `DATACONTRACT_SQLSERVER_AUTHENTICATION=windows` | `DATACONTRACT_SQLSERVER_TRUSTED_CONNECTION=true` predates `DATACONTRACT_SQLSERVER_AUTHENTICATION` and selects the same Windows integrated auth. It applies only when `DATACONTRACT_SQLSERVER_AUTHENTICATION` is unset; setting a mode explicitly always wins, and the ignored flag is logged as a warning. ### Data types #### Importing `datacontract import sql --dialect sqlserver` maps DDL types as follows; the normalized SQL type is kept as `physicalType`. | SQL Server type | `logicalType` | |---|---| | `VARCHAR`, `NVARCHAR`, `CHAR`, `NCHAR`, `TEXT`, `NTEXT` | `string` (`maxLength` for varchar/char) | | `INT`, `INTEGER`, `BIGINT`, `SMALLINT`, `TINYINT` | `integer` | | `NUMERIC`, `DECIMAL`, `FLOAT`, `REAL`, `MONEY` | `number` | | `BIT` | `boolean` | | `DATE` | `date` | | `TIME` | `time` | | `DATETIME`, `DATETIME2`, `DATETIMEOFFSET`, `SMALLDATETIME` | `timestamp` | | `UNIQUEIDENTIFIER` | `string` (format `uuid`) | | `BINARY`, `VARBINARY`, `IMAGE` | `string` (format `binary`) | | `XML` | `string` | #### Testing SQL Server supports **native type introspection**: the declared `physicalType` is checked against `information_schema.columns`. `varchar` and `nvarchar` are distinct types, as are `text` and `varchar` — declare what the column actually is. Timezone/precision variants of the datetime family are interchangeable; length is only enforced when declared (`varchar(max)` is introspected as such). A `physicalType` that isn't valid T-SQL falls back to the logical type category comparison. #### Logical type mapping When no `physicalType` is declared, the CLI derives the native type from the `logicalType` — for example in `datacontract export sql` and the dbt exports. This table is generated from the converter in the CLI's code: | `logicalType` | SQL Server type | |---|---| | `string` | `varchar` | | `integer` | `INT` | | `number` | `numeric` | | `boolean` | `bit` | | `date` | `date` | | `timestamp` | `datetimeoffset` | | `time` | `time` | | `object` | `nvarchar(max)` | | `array` | *(not supported)* | | `map` | `nvarchar(max)` | | `vector` | `VECTOR(1536)` | --- ## Trino Reference ## ![](/img/icons/trino.svg) Trino Reference Authentication options and data type handling for [Trino connections](../testing/trino.md). ### Server ```yaml servers: - server: trino type: trino host: localhost port: 8080 catalog: my_catalog schema: my_schema ``` ### Authentication | Variable | Example | Description | |---|---|---| | `DATACONTRACT_TRINO_USERNAME` | `trino` | Username for `basic` auth | | `DATACONTRACT_TRINO_PASSWORD` | `mysecretpassword` | Password for `basic` auth | | `DATACONTRACT_TRINO_AUTHENTICATION` | `oauth2` | `basic` (default), `jwt`, or `oauth2` | | `DATACONTRACT_TRINO_JWT_TOKEN` | `eyJhbGciOi...` | JWT bearer token for `jwt` auth | `host`, `port`, `catalog`, and `schema` come from the contract's `servers` block, and can be overridden with `DATACONTRACT_TRINO_HOST`, `DATACONTRACT_TRINO_PORT`, `DATACONTRACT_TRINO_CATALOG`, and `DATACONTRACT_TRINO_SCHEMA`. ### Data types #### Importing There is no direct Trino importer. Import a `SHOW CREATE TABLE` DDL with `datacontract import sql --dialect postgres` (Trino's ANSI-style DDL generally parses with the postgres dialect): `VARCHAR`/`CHAR` → `string`, `INTEGER`/`BIGINT`/`SMALLINT`/`TINYINT` → `integer`, `DECIMAL`/`DOUBLE`/`REAL` → `number`, `BOOLEAN` → `boolean`, `DATE` → `date`, `TIME` → `time`, `TIMESTAMP` → `timestamp`, `ARRAY(...)` → `array`, `JSON` → `object`. #### Testing Trino supports **native type introspection**: the declared `physicalType` is checked against `information_schema.columns`. Length/precision is only enforced when the contract declares it (`varchar` matches `varchar(10)`); timezone variants of timestamps are interchangeable. A `physicalType` that isn't valid Trino SQL falls back to the logical type category comparison. #### Logical type mapping When no `physicalType` is declared, the CLI derives the native type from the `logicalType` — for example in `datacontract export sql` and the dbt exports. This table is generated from the converter in the CLI's code: | `logicalType` | Trino type | |---|---| | `string` | `varchar` | | `integer` | `integer` | | `number` | `decimal` | | `boolean` | `boolean` | | `date` | `date` | | `timestamp` | `timestamp(3) with time zone` | | `time` | `time` | | `object` | `json` | | `array` | `json` | | `map` | `map(varchar, varchar)` | | `vector` | `array(real)` | --- ## Commands The `datacontract` CLI groups its functionality into the commands below. Run `datacontract --help` or `datacontract --help` at any time to see the same information in your terminal. ```bash datacontract [OPTIONS] COMMAND [ARGS]... ``` ### Global options | Option | Default | Description | |---|---|---| | `--version` | — | Prints the current version. | | `--system-truststore` | off | Verify TLS using the operating system's certificate trust store instead of the bundled CA certificates (e.g. behind a corporate proxy or internal CA). | | `--config-file` | — | Path to a YAML file with credentials and connection options (sections per data source, $\{VAR\} references resolve from the environment). Defaults to ./datacontract-config.yaml or ~/.datacontract/config.yaml if present. | Most commands additionally accept `--debug` for verbose logging. ### Commands | Command | Description | |---|---| | [`init`](./init.md) | Create an empty data contract. | | [`edit`](./edit.md) | Edit a data contract file in the Data Contract Editor (web UI). | | [`lint`](./lint.md) | Validate that the datacontract.yaml is correctly formatted. | | [`changelog`](./changelog.md) | Show a changelog between two data contracts. | | [`breaking`](./breaking.md) | Show compatibility impact between two data contracts. | | [`test`](./test.md) | Run schema and quality tests on configured servers. | | [`dbt`](./dbt/index.md) | Work with data contracts in your dbt project. | | [`ci`](./ci.md) | Run tests for CI/CD pipelines. | | [`export`](./export/index.md) | Convert a data contract to a target format. | | [`import`](./import/index.md) | Create a data contract from a source format. | | [`catalog`](./catalog.md) | Create an HTML catalog of data contracts. | | [`publish`](./publish.md) | Publish the data contract to Entropy Data. | | [`api`](./api.md) | Start the datacontract CLI as server application with REST API. | ### Common usage ```bash datacontract init datacontract.yaml datacontract edit datacontract.yaml datacontract lint datacontract.yaml datacontract changelog datacontract-v1.yaml datacontract-v2.yaml datacontract breaking datacontract-v1.yaml datacontract-v2.yaml datacontract test datacontract.yaml --server production datacontract dbt sync orders.odcs.yaml --project-dir ./warehouse datacontract ci datacontract.yaml --output test-results.xml --output-format junit datacontract export html datacontract.yaml --output datacontract.html datacontract import sql --source ddl.sql --dialect postgres --output datacontract.yaml datacontract catalog --files "**/*.yaml" --output catalog/ datacontract publish datacontract.yaml datacontract api --port 4242 --host 0.0.0.0 ``` --- ## api ## `datacontract api` Start the datacontract CLI as server application with REST API. The OpenAPI documentation as Swagger UI is available on http://localhost:4242. You can execute the commands directly from the Swagger UI. To protect the API, you can set the environment variable DATACONTRACT_CLI_API_KEY to a secret API key. To authenticate, requests must include the header 'x-api-key' with the correct API key. This is highly recommended, as data contract tests may be subject to SQL injections or leak sensitive information. To connect to servers (such as a Snowflake data source), set the credentials as environment variables as documented in https://cli.datacontract.com/configuration It is possible to run the API with extra arguments for `uvicorn.run()` as keyword arguments, e.g.: `datacontract api --port 1234 --root_path /datacontract`. ```bash datacontract api [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--port` | `4242` | Bind socket to this port. | | `--host` | `127.0.0.1` | Bind socket to this host. Hint: For running in docker, set it to 0.0.0.0 | | `--reload` / `--no-reload` | `--no-reload` | Watch the source files and restart the server on changes. For development only; off by default. | | `--contract-variables` | — | Environment variables a posted data contract may read through $\{VAR\} references, as comma-separated fnmatch globs (e.g. 'TABLE_*,CUTOFF_DATE', or '*' to allow all). Empty by default. | | `--allow-local-files` / `--no-allow-local-files` | — | Let a posted data contract read the server's own disk through servers[].type: local. Off by default. | | `--allow-private-network` / `--no-allow-private-network` | — | Let a posted data contract's API requests (servers[].type: api) reach private network addresses, such as localhost or the cloud metadata endpoint. Off by default. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract api --port 4242 --host 0.0.0.0 ``` Guide: **[Run as a web server](../api.md)**. --- ## breaking ## `datacontract breaking` Show compatibility impact between two data contracts. ```bash datacontract breaking [OPTIONS] V1 V2 ``` | Argument | Default | Description | |---|---|---| | `V1` | required | The location (url, s3 url, or local path) of the source (before) data contract YAML. | | `V2` | required | The location (url, s3 url, or local path) of the target (after) data contract YAML. | | Option | Default | Description | |---|---|---| | `--perspective` | `consumer` | `consumer` flags changes that lead to unexpected data for consumers, `producer` flags changes that can make data contract tests fail. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract breaking datacontract-v1.yaml datacontract-v2.yaml ``` Guide: **[Compare contract versions](../compare-contract-versions.md)**. --- ## catalog ## `datacontract catalog` Create an HTML catalog of data contracts. ```bash datacontract catalog [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--files` | `*.yaml` | Glob pattern for the data contract files to include in the catalog. Applies recursively to any subfolders. | | `--output` | `catalog/` | Output directory for the catalog html files. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract catalog --files "**/*.yaml" --output catalog/ ``` --- ## changelog ## `datacontract changelog` Show a changelog between two data contracts. ```bash datacontract changelog [OPTIONS] V1 V2 ``` | Argument | Default | Description | |---|---|---| | `V1` | required | The location (url, s3 url, or local path) of the source (before) data contract YAML. | | `V2` | required | The location (url, s3 url, or local path) of the target (after) data contract YAML. | | Option | Default | Description | |---|---|---| | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract changelog datacontract-v1.yaml datacontract-v2.yaml ``` Guide: **[Compare contract versions](../compare-contract-versions.md)**. --- ## ci ## `datacontract ci` Run tests for CI/CD pipelines. Emits GitHub Actions annotations and step summary. ```bash datacontract ci [OPTIONS] [LOCATIONS]... ``` | Argument | Default | Description | |---|---|---| | `[LOCATIONS]...` | — | The location(s) (url, s3 url, or local path) of the data contract yaml file(s). | | Option | Default | Description | |---|---|---| | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--server` | `all` | The server configuration to run the schema and quality tests. Use the key of the server object in the data contract yaml file to refer to a server, e.g., `production`, or `all` for all servers (default). | | `--publish` | — | The url to publish the results after the test. | | `--output` | — | Specify the file path where the test results should be written to (e.g., './test-results/TEST-datacontract.xml'). | | `--output-format` | — | The target format for the test results. Accepted values: json, junit. | | `--metadata-only` / `--no-metadata-only` | `--no-metadata-only` | Run only checks that read the schema (field presence and types). Checks that read row values are skipped. | | `--logs` / `--no-logs` | `--no-logs` | Print logs | | `--json` | off | Print test results as JSON to stdout. | | `--fail-on` | `error` | Minimum severity that causes a non-zero exit code. | | `--ssl-verification` / `--no-ssl-verification` | `--ssl-verification` | SSL verification when publishing the data contract. | | `--custom-quality-checks` | — | Folder with the custom quality checks the contract's quality rules use, one YAML file per check. Defaults to DATACONTRACT_CUSTOM_QUALITY_CHECKS. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract ci datacontract.yaml --output test-results.xml --output-format junit ``` Guide: **[Scheduling](../scheduling/index.md)**. --- ## edit ## `datacontract edit` Edit a data contract file in the Data Contract Editor (web UI). Starts a local web server that opens the Data Contract Editor for the given file. The editor is bundled with the CLI, so no internet access is required. Saving in the editor writes directly back to the local file. If a URL is given, you are asked whether to download a local copy, which is then edited. The server also acts as the editor's test runner: "Run test" in the editor executes the data contract tests locally against the servers defined in the data contract. Credentials for the data sources must be provided as environment variables, see https://cli.datacontract.com/testing The editor's AI assistant is enabled by setting DATACONTRACT_EDITOR_AI_ENDPOINT, together with DATACONTRACT_EDITOR_AI_API_KEY, DATACONTRACT_EDITOR_AI_MODEL, DATACONTRACT_EDITOR_AI_PROVIDER (openai or anthropic), and DATACONTRACT_EDITOR_AI_AUTH_HEADER (bearer, api-key, or x-api-key). ```bash datacontract edit [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The path of the data contract yaml to edit. If the file does not exist, you are asked whether to initialize a new data contract. If a URL is given, you are asked whether to download a local copy to edit. | | Option | Default | Description | |---|---|---| | `--port` | `4243` | Bind socket to this port. | | `--host` | `127.0.0.1` | Bind socket to this host. Hint: For running in docker, set it to 0.0.0.0 | | `--editor-version` | — | Version of the datacontract-editor npm package to load from the CDN, e.g. '0.1.9' or 'latest'. By default, the editor version bundled with the CLI is used (works offline). | | `--editor-assets-url` | — | Base URL to load the Data Contract Editor assets (JS/CSS) from, e.g. a self-hosted editor build. Takes precedence over --editor-version. | | `--language` | — | UI language of the editor, one of: en, de, sv, fr. By default, the editor is shown in English, and the language can be switched in the editor. | | `--open` / `--no-open` | `--open` | Open the editor in the default browser. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract edit datacontract.yaml ``` Guide: **[Edit your Contract](../editor.md)**. --- ## init ## `datacontract init` Create an empty data contract. ```bash datacontract init [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location of the data contract file to create. | | Option | Default | Description | |---|---|---| | `--template` | — | URL of a template or data contract | | `--overwrite` / `--no-overwrite` | `--no-overwrite` | Replace the existing datacontract.yaml | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract init datacontract.yaml ``` --- ## lint ## `datacontract lint` Validate that the datacontract.yaml is correctly formatted. ```bash datacontract lint [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--output` | — | Specify the file path where the test results should be written to (e.g., './test-results/TEST-datacontract.xml'). If no path is provided, the output will be printed to stdout. | | `--output-format` | — | The target format for the test results. Accepted values: json, junit. | | `--all-errors` | off | Report all JSON Schema validation errors instead of stopping after the first one. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract lint datacontract.yaml ``` Guide: **[Open Data Contract Standard](../open-data-contract-standard.md)**. --- ## publish ## `datacontract publish` Publish the data contract to Entropy Data. ```bash datacontract publish [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--ssl-verification` / `--no-ssl-verification` | `--ssl-verification` | SSL verification when publishing the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract publish datacontract.yaml ``` Guide: **[Publish to Entropy Data](../entropy-data.md)**. --- ## test ## `datacontract test` Run schema and quality tests on configured servers. ```bash datacontract test [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--server` | `all` | The server configuration to run the schema and quality tests. Use the key of the server object in the data contract yaml file to refer to a server, e.g., `production`, or `all` for all servers (default). | | `--schema-name` | `all` | Which schema to test, e.g., `orders`, or `all` for all schemas (default). | | `--publish-test-results` / `--no-publish-test-results` | `--no-publish-test-results` | Deprecated. Use publish parameter. Publish the results after the test | | `--publish` | — | The url to publish the results after the test. | | `--output` | — | Specify the file path where the test results should be written to (e.g., './test-results/TEST-datacontract.xml'). | | `--output-format` | — | The target format for the test results. Accepted values: json, junit. | | `--checks` | — | Comma-separated list of check categories to run (available: properties, quality, slaProperties, custom; legacy aliases: schema, servicelevel). Omit to enable all. | | `--dimension` | — | Comma-separated list of quality dimensions to run (available: accuracy, completeness, conformity, consistency, coverage, timeliness, uniqueness). Runs the quality rules declaring a matching `dimension`, plus the schema and service level checks that measure it. Omit to run everything. | | `--quality-id` | — | Comma-separated list of quality rule ids to run, as defined in the `id` of the quality rule. Runs only those rules, no schema or service level checks. Fails if an id is not defined in the data contract. Omit to run everything. | | `--tag` | — | Comma-separated list of tags to run. Runs the quality rules declaring a matching tag in their `tags`, no schema or service level checks. Omit to run everything. | | `--filter` | — | A SQL predicate to filter the rows under test, in the dialect of the server, e.g., "ingested_at \>= CURRENT_DATE - 1". Only works if a single schema is tested; for contracts with multiple schemas, combine with --schema-name or use --filters. Schema checks and custom SQL queries are not filtered. | | `--filters` | — | Row filters per schema, as a JSON object mapping schema name to SQL predicate, e.g., '\{"orders": "ingested_at \>= CURRENT_DATE - 1"\}'. Schema checks and custom SQL queries are not filtered. | | `--metadata-only` / `--no-metadata-only` | `--no-metadata-only` | Run only checks that read the schema (field presence and types). Checks that read row values are skipped. | | `--dry-run` / `--no-dry-run` | `--no-dry-run` | Report the checks that would run, without connecting to the server or reading any data. Reported checks have the result 'skipped', or 'warning' where a check could not be planned. | | `--include-failed-samples` / `--no-include-failed-samples` | `--no-include-failed-samples` | Collect a small sample of rows that failed each missing/invalid/duplicate check (identifier + offending columns; sensitive columns omitted). Off by default. | | `--logs` / `--no-logs` | `--no-logs` | Print logs | | `--ssl-verification` / `--no-ssl-verification` | `--ssl-verification` | SSL verification when publishing the data contract. | | `--custom-quality-checks` | — | Folder with the custom quality checks the contract's quality rules use, one YAML file per check. Defaults to DATACONTRACT_CUSTOM_QUALITY_CHECKS. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract test datacontract.yaml --server production ``` Guide: **[Test your Data](../testing/index.md)**. --- ## dbt ## `datacontract dbt` Work with data contracts in your dbt project. ```bash datacontract dbt [OPTIONS] COMMAND [ARGS]... ``` | Subcommand | Description | |---|---| | [`sync`](./sync.md) | Generate dbt tests and model metadata from one or more ODCS contracts. | | [`test`](./test.md) | Run the contract-managed dbt tests that `datacontract dbt sync` generated. | Guide: **[Sync with dbt](../../dbt.md)**. --- ## export ## `datacontract export` Convert a data contract to a target format. ```bash datacontract export [OPTIONS] COMMAND [ARGS]... ``` | Subcommand | Description | |---|---| | [`avro`](./avro.md) | Export a data contract to Avro schema. | | [`avro-idl`](./avro-idl.md) | Export a data contract to Avro IDL. | | [`bigquery`](./bigquery.md) | Export a data contract to BigQuery schema. | | [`custom`](./custom.md) | Export a data contract using a custom Jinja template. | | [`data-caterer`](./data-caterer.md) | Export a data contract to Data Caterer format. | | [`dbml`](./dbml.md) | Export a data contract to DBML. | | [`dbt`](./dbt.md) | Removed: use `datacontract export dbt-models` instead. | | [`dbt-models`](./dbt-models.md) | Export a data contract to dbt model schema YAML. | | [`dbt-sources`](./dbt-sources.md) | Export a data contract to dbt sources YAML. | | [`dbt-staging-sql`](./dbt-staging-sql.md) | Export a data contract to a dbt staging SQL file. | | [`dcs`](./dcs.md) | Export a data contract to DCS format. | | [`dqx`](./dqx.md) | Export a data contract to DQX format. | | [`excel`](./excel.md) | Export a data contract to Excel. | | [`go`](./go.md) | Export a data contract to Go structs. | | [`great-expectations`](./great-expectations.md) | Export a data contract to Great Expectations suite. | | [`html`](./html.md) | Export a data contract to HTML. | | [`iceberg`](./iceberg.md) | Export a data contract to Iceberg schema. | | [`jsonschema`](./jsonschema.md) | Export a data contract to JSON Schema. | | [`markdown`](./markdown.md) | Export a data contract to Markdown. | | [`mermaid`](./mermaid.md) | Export a data contract to Mermaid diagram. | | [`odcs`](./odcs.md) | Export a data contract to ODCS format. | | [`protobuf`](./protobuf.md) | Export a data contract to Protobuf schema. | | [`pydantic-model`](./pydantic-model.md) | Export a data contract to a Pydantic model. | | [`rdf`](./rdf.md) | Export a data contract to RDF. | | [`sodacl`](./sodacl.md) | Export a data contract to SodaCL checks. | | [`spark`](./spark.md) | Export a data contract to Spark schema. | | [`sql`](./sql.md) | Export a data contract to SQL DDL. | | [`sql-query`](./sql-query.md) | Export a data contract to a SQL query. | | [`sqlalchemy`](./sqlalchemy.md) | Export a data contract to SQLAlchemy models. | | [`xsd`](./xsd.md) | Export a data contract to an XML Schema (XSD). | Guide: **[Exports](../../exports/index.md)**. --- ## import ## `datacontract import` Create a data contract from a source format. ```bash datacontract import [OPTIONS] COMMAND [ARGS]... ``` | Subcommand | Description | |---|---| | [`adls`](./adls.md) | Import a data contract from files in Azure Blob Storage / ADLS. | | [`athena`](./athena.md) | Import a data contract from an Amazon Athena database. | | [`avro`](./avro.md) | Import a data contract from an Avro schema file. | | [`bigquery`](./bigquery.md) | Import a data contract from BigQuery. | | [`clickhouse`](./clickhouse.md) | Import a data contract from a ClickHouse database. | | [`csv`](./csv.md) | Import a data contract from a CSV file. | | [`databricks`](./databricks.md) | Import a data contract from Databricks Unity Catalog. | | [`dbml`](./dbml.md) | Import a data contract from a DBML file. | | [`dbt`](./dbt.md) | Import a data contract from a dbt manifest file. | | [`excel`](./excel.md) | Import a data contract from an Excel file. | | [`gcs`](./gcs.md) | Import a data contract from files in Google Cloud Storage. | | [`glue`](./glue.md) | Import a data contract from AWS Glue. | | [`hive`](./hive.md) | Import a data contract from a Hive database. | | [`iceberg`](./iceberg.md) | Import a data contract from an Iceberg schema file or a REST catalog. | | [`json`](./json.md) | Import a data contract from a JSON file. | | [`jsonschema`](./jsonschema.md) | Import a data contract from a JSON Schema file. | | [`mysql`](./mysql.md) | Import a data contract from a MySQL database. | | [`odata`](./odata.md) | Import a data contract from OData 4.x CSDL XML or JSON metadata, using a URL or local file. | | [`odcs`](./odcs.md) | Import a data contract from an ODCS file. | | [`openapi`](./openapi.md) | Import a data contract from the response of a GET or POST operation in an OpenAPI 3.x document. | | [`oracle`](./oracle.md) | Import a data contract from an Oracle database. | | [`parquet`](./parquet.md) | Import a data contract from a Parquet file. | | [`postgres`](./postgres.md) | Import a data contract from a Postgres schema. | | [`powerbi`](./powerbi.md) | Import a data contract from a Power BI semantic model (.pbit, .bim, or .json) file. | | [`protobuf`](./protobuf.md) | Import a data contract from a Protobuf schema file. | | [`pydantic-model`](./pydantic-model.md) | Import a data contract from Pydantic models. | | [`redshift`](./redshift.md) | Import a data contract from an Amazon Redshift schema. | | [`s3`](./s3.md) | Import a data contract from files in S3. | | [`snowflake`](./snowflake.md) | Import a data contract from a Snowflake workspace. | | [`spark`](./spark.md) | Import a data contract from a Spark schema. | | [`sql`](./sql.md) | Import a data contract from a SQL DDL file. | | [`sqlserver`](./sqlserver.md) | Import a data contract from a SQL Server database. | | [`trino`](./trino.md) | Import a data contract from a Trino catalog. | | [`unity`](./unity.md) | Import a data contract from Databricks Unity Catalog (alias of `import databricks`). | | [`xsd`](./xsd.md) | Import a data contract from an XML Schema (XSD) file. | Guide: **[Imports](../../imports/index.md)**. --- ## dbt sync ## `datacontract dbt sync` Generate dbt tests and model metadata from one or more ODCS contracts. Modifies the existing dbt model YAML in place (preserving comments and formatting), and creates new model YAML files or singular SQL tests if needed (if not run in dry-run mode). Use `datacontract dbt test` or pass --run-tests to run the generated tests. ```bash datacontract dbt sync [OPTIONS] [CONTRACT]... ``` | Argument | Default | Description | |---|---|---| | `[CONTRACT]...` | — | Path(s) or glob(s) of ODCS contracts to sync. If omitted, syncs every `*.odcs.yaml` under --project-dir (default: current directory) and its subdirectories. | | Option | Default | Description | |---|---|---| | `--project-dir` | — | Path to the dbt project root (must contain `dbt_project.yml`). Defaults to the current directory. | | `--schema-name` | `all` | Which ODCS schema object to sync, by name. | | `--model-resolution` | `name` | How to map an ODCS schema to a dbt model name. | | `--target` | — | Forwarded to `dbt test --target`. | | `--profiles-dir` | — | Forwarded to `dbt test --profiles-dir`. | | `--prune` / `--no-prune` | `--no-prune` | Remove model columns and tags that are not declared in the contract. | | `--dry-run` / `--no-dry-run` | `--no-dry-run` | Run in dry-run mode, without applying any changes to the dbt project. | | `--run-tests` / `--skip-tests` | `--skip-tests` | Run `dbt test` on the generated tests after syncing. Default: skip (generate only). | | `--publish` | — | The url to publish the results after the test. | | `--server` | — | Selected ODCS server. Determines the dialect for data types and for the server label when publishing results. Default: Auto-selected if only one server exists, else --target. | | `--ssl-verification` / `--no-ssl-verification` | `--ssl-verification` | SSL verification when publishing test results. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract dbt sync orders.odcs.yaml --project-dir ./warehouse ``` --- ## dbt test ## `datacontract dbt test` Run the contract-managed dbt tests that `datacontract dbt sync` generated. Runs `dbt test` scoped to the specified data contract(s), reports the results, and optionally publishes them. Use `datacontract dbt sync` to create and update the tests first. ```bash datacontract dbt test [OPTIONS] [CONTRACT]... ``` | Argument | Default | Description | |---|---|---| | `[CONTRACT]...` | — | Path(s) or glob(s) of ODCS contracts to test. If omitted, tests every `*.odcs.yaml` under --project-dir (default: current directory) and its subdirectories. | | Option | Default | Description | |---|---|---| | `--project-dir` | — | Path to the dbt project root (must contain `dbt_project.yml`). Defaults to the current directory. | | `--target` | — | Forwarded to `dbt test --target`. | | `--profiles-dir` | — | Forwarded to `dbt test --profiles-dir`. | | `--publish` | — | The url to publish the results after the test. | | `--server` | — | Selected ODCS server, used as the server label when publishing results. Default: Auto-selected if only one server exists, else --target. | | `--ssl-verification` / `--no-ssl-verification` | `--ssl-verification` | SSL verification when publishing test results. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract dbt test orders.odcs.yaml --project-dir ./warehouse ``` --- ## export avro-idl ## `datacontract export avro-idl` Export a data contract to Avro IDL. ```bash datacontract export avro-idl [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export avro-idl datacontract.yaml --output schema.avdl ``` Guide: **[Export: Avro IDL](../../exports/avro-idl.md)**. --- ## export avro ## `datacontract export avro` Export a data contract to Avro schema. ```bash datacontract export avro [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export avro datacontract.yaml --output schema.avsc ``` Guide: **[Export: Avro](../../exports/avro.md)**. --- ## export bigquery ## `datacontract export bigquery` Export a data contract to BigQuery schema. ```bash datacontract export bigquery [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export bigquery datacontract.yaml --output schema.json ``` Guide: **[Export: BigQuery](../../exports/bigquery.md)**. --- ## export custom ## `datacontract export custom` Export a data contract using a custom Jinja template. ```bash datacontract export custom [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--template` | — | Path to Jinja template. | | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export custom datacontract.yaml --template template.j2 --output output.txt ``` Guide: **[Export: Custom (Jinja)](../../exports/custom.md)**. --- ## export data-caterer ## `datacontract export data-caterer` Export a data contract to Data Caterer format. ```bash datacontract export data-caterer [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export data-caterer datacontract.yaml --output plan.yaml ``` Guide: **[Export: Data Caterer](../../exports/data-caterer.md)**. --- ## export dbml ## `datacontract export dbml` Export a data contract to DBML. ```bash datacontract export dbml [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export dbml datacontract.yaml --output schema.dbml ``` Guide: **[Export: DBML](../../exports/dbml.md)**. --- ## export dbt-models ## `datacontract export dbt-models` Export a data contract to dbt model schema YAML. ```bash datacontract export dbt-models [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export dbt-models datacontract.yaml --output schema.yml ``` Guide: **[Export: dbt Models](../../exports/dbt-models.md)**. --- ## export dbt-sources ## `datacontract export dbt-sources` Export a data contract to dbt sources YAML. ```bash datacontract export dbt-sources [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export dbt-sources datacontract.yaml --output sources.yml ``` Guide: **[Export: dbt Sources](../../exports/dbt-sources.md)**. --- ## export dbt-staging-sql ## `datacontract export dbt-staging-sql` Export a data contract to a dbt staging SQL file. ```bash datacontract export dbt-staging-sql [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export dbt-staging-sql datacontract.yaml --output stg.sql ``` Guide: **[Export: dbt Staging SQL](../../exports/dbt-staging-sql.md)**. --- ## export dcs ## `datacontract export dcs` Export a data contract to DCS format. ```bash datacontract export dcs [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export dcs datacontract.yaml --output contract.dcs.yaml ``` Guide: **[Export: DCS](../../exports/dcs.md)**. --- ## export dqx ## `datacontract export dqx` Export a data contract to DQX format. ```bash datacontract export dqx [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export dqx datacontract.yaml --output checks.yaml ``` Guide: **[Export: DQX](../../exports/dqx.md)**. --- ## export excel ## `datacontract export excel` Export a data contract to Excel. ```bash datacontract export excel [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--template` | — | Path or URL to a custom Excel template. Defaults to the bundled ODCS template for the contract's apiVersion. | | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export excel datacontract.yaml --output datacontract.xlsx ``` Guide: **[Export: Excel](../../exports/excel.md)**. --- ## export go ## `datacontract export go` Export a data contract to Go structs. ```bash datacontract export go [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export go datacontract.yaml --output models.go ``` Guide: **[Export: Go](../../exports/go.md)**. --- ## export great-expectations ## `datacontract export great-expectations` Export a data contract to Great Expectations suite. ```bash datacontract export great-expectations [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--engine` | — | The engine used for Great Expectations run. | | `--dialect` | `auto` | The SQL dialect. Use `auto` (default) to detect the SQL dialect via the specified servers in the data contract. | | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | | `--suite-name` | — | The suite name for the Great Expectations run. | | `--checks` | — | Comma-separated list of check categories to export (available: properties, quality). Omit to export everything. | ```bash datacontract export great-expectations datacontract.yaml --engine sql --dialect postgres --output expectations.json ``` Guide: **[Export: Great Expectations](../../exports/great-expectations.md)**. --- ## export html ## `datacontract export html` Export a data contract to HTML. ```bash datacontract export html [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export html datacontract.yaml --output datacontract.html ``` Guide: **[Export: HTML](../../exports/html.md)**. --- ## export iceberg ## `datacontract export iceberg` Export a data contract to Iceberg schema. ```bash datacontract export iceberg [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export iceberg datacontract.yaml --output schema.json ``` Guide: **[Export: Iceberg](../../exports/iceberg.md)**. --- ## export jsonschema ## `datacontract export jsonschema` Export a data contract to JSON Schema. ```bash datacontract export jsonschema [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export jsonschema datacontract.yaml --output schema.json ``` Guide: **[Export: JSON Schema](../../exports/jsonschema.md)**. --- ## export markdown ## `datacontract export markdown` Export a data contract to Markdown. ```bash datacontract export markdown [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export markdown datacontract.yaml --output datacontract.md ``` Guide: **[Export: Markdown](../../exports/markdown.md)**. --- ## export mermaid ## `datacontract export mermaid` Export a data contract to Mermaid diagram. ```bash datacontract export mermaid [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export mermaid datacontract.yaml --output diagram.mmd ``` Guide: **[Export: Mermaid](../../exports/mermaid.md)**. --- ## export odcs ## `datacontract export odcs` Export a data contract to ODCS format. ```bash datacontract export odcs [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export odcs datacontract.yaml --output odcs-contract.yaml ``` Guide: **[Export: ODCS](../../exports/odcs.md)**. --- ## export protobuf ## `datacontract export protobuf` Export a data contract to Protobuf schema. ```bash datacontract export protobuf [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export protobuf datacontract.yaml --output schema.proto ``` Guide: **[Export: Protobuf](../../exports/protobuf.md)**. --- ## export pydantic-model ## `datacontract export pydantic-model` Export a data contract to a Pydantic model. ```bash datacontract export pydantic-model [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export pydantic-model datacontract.yaml --output models.py ``` Guide: **[Export: Pydantic Model](../../exports/pydantic-model.md)**. --- ## export rdf ## `datacontract export rdf` Export a data contract to RDF. ```bash datacontract export rdf [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--base` | — | The base URI used to generate the RDF graph. | | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export rdf datacontract.yaml --base https://example.com/ --output contract.ttl ``` Guide: **[Export: RDF](../../exports/rdf.md)**. --- ## export sodacl ## `datacontract export sodacl` Export a data contract to SodaCL checks. ```bash datacontract export sodacl [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export sodacl datacontract.yaml --output checks.yml ``` Guide: **[Export: SodaCL](../../exports/sodacl.md)**. --- ## export spark ## `datacontract export spark` Export a data contract to Spark schema. ```bash datacontract export spark [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export spark datacontract.yaml --output schema.py ``` Guide: **[Export: Spark](../../exports/spark.md)**. --- ## export sql-query ## `datacontract export sql-query` Export a data contract to a SQL query. ```bash datacontract export sql-query [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--dialect` | `auto` | The SQL dialect. Use `auto` (default) to detect the SQL dialect via the specified servers in the data contract. | | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export sql-query datacontract.yaml --dialect snowflake --output query.sql ``` Guide: **[Export: SQL Query](../../exports/sql-query.md)**. --- ## export sql ## `datacontract export sql` Export a data contract to SQL DDL. ```bash datacontract export sql [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--dialect` | `auto` | The SQL dialect. Use `auto` (default) to detect the SQL dialect via the specified servers in the data contract. | | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--clickhouse-engine` | — | The ClickHouse table engine. Default: MergeTree(). Example: ReplicatedMergeTree('/clickhouse/tables/\{shard\}/table', '\{replica\}') | | `--clickhouse-order-by` | — | ORDER BY columns for ClickHouse (comma-separated). ClickHouse uses ORDER BY as its primary/sorting key (not PRIMARY KEY). Default: primary key columns if defined, omitted otherwise. Example: --clickhouse-order-by "event_date DESC, event_id" | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export sql datacontract.yaml --dialect postgres --output ddl.sql ``` Guide: **[Export: SQL DDL](../../exports/sql.md)**. --- ## export sqlalchemy ## `datacontract export sqlalchemy` Export a data contract to SQLAlchemy models. ```bash datacontract export sqlalchemy [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export sqlalchemy datacontract.yaml --output models.py ``` Guide: **[Export: SQLAlchemy](../../exports/sqlalchemy.md)**. --- ## export xsd ## `datacontract export xsd` Export a data contract to an XML Schema (XSD). ```bash datacontract export xsd [OPTIONS] [LOCATION] ``` | Argument | Default | Description | |---|---|---| | `[LOCATION]` | `datacontract.yaml` | The location (url, s3 url, or local path) of the data contract yaml. | | Option | Default | Description | |---|---|---| | `--output` | — | File path where the exported data will be saved. If not provided, it will be printed to stdout. | | `--server` | — | The server name to export. | | `--schema-name` | `all` | Which schema to export, e.g., `orders`, or `all` for all schemas (default). | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema. | | `--inline-references` / `--no-inline-references` | `--inline-references` | Resolve authoritativeDefinitions of type definition, semantics or businessDefinition and inline them into the contract. Relative URLs and semantics IRIs are looked up on the ENTROPY_DATA_HOST (default https://api.entropy-data.com), other URLs are fetched directly, file references are read from disk. See https://cli.datacontract.com/semantics | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract export xsd datacontract.yaml --output schema.xsd ``` Guide: **[Export: XML Schema](../../exports/xsd.md)**. --- ## import adls ## `datacontract import adls` Import a data contract from files in Azure Blob Storage / ADLS. ```bash datacontract import adls [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | The location of the files, e.g. abfss://my-container/orders/*.json. | | `--format` | — | File format: json, csv, parquet or delta (inferred from the suffix). | | `--delimiter` | — | For JSON: new_line, array or none. Detected automatically when omitted. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import adls --source abfss://my-container/orders/*.json --output datacontract.yaml ``` Guide: **[Import: Azure Blob / ADLS](../../imports/adls.md)**. --- ## import athena ## `datacontract import athena` Import a data contract from an Amazon Athena database. ```bash datacontract import athena [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--schema` | — | The Athena database name (called schema in the contract). | | `--staging-dir` | — | S3 location where Athena writes query results, e.g. s3://my-bucket/athena-results/. | | `--region` | — | The AWS region the Glue Data Catalog lives in. | | `--catalog` | — | The Athena catalog (default awsdatacatalog). | | `--table` | — | Name of a table to import (repeat for multiple tables, omit for all tables in the schema). | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import athena --schema my_database --staging-dir s3://my-bucket/athena-results/ --output datacontract.yaml ``` Guide: **[Import: Amazon Athena](../../imports/athena.md)**. --- ## import avro ## `datacontract import avro` Import a data contract from an Avro schema file. ```bash datacontract import avro [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to the Avro schema file. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import avro --source schema.avsc --output datacontract.yaml ``` Guide: **[Import: Avro](../../imports/avro.md)**. --- ## import bigquery ## `datacontract import bigquery` Import a data contract from BigQuery. ```bash datacontract import bigquery [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to a BigQuery schema JSON file. If omitted, imports from the BigQuery API using --project/--dataset/--table. | | `--project` | — | The BigQuery project id. | | `--dataset` | — | The BigQuery dataset id. | | `--table` | — | List of table ids to import from the BigQuery API (repeat for multiple table ids, leave empty for all tables in the dataset). | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import bigquery --project my-project --dataset my_dataset --output datacontract.yaml ``` Guide: **[Import: BigQuery](../../imports/bigquery.md)**. --- ## import clickhouse ## `datacontract import clickhouse` Import a data contract from a ClickHouse database. ```bash datacontract import clickhouse [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | The host of the ClickHouse server. | | `--port` | — | The ClickHouse HTTP port (default 8123). | | `--database` | — | The database name. | | `--table` | — | Name of a table to import (repeat for multiple tables, omit for all tables). | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import clickhouse --source localhost --database default --output datacontract.yaml ``` Guide: **[Import: ClickHouse](../../imports/clickhouse.md)**. --- ## import csv ## `datacontract import csv` Import a data contract from a CSV file. ```bash datacontract import csv [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to the CSV file. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import csv --source data.csv --output datacontract.yaml ``` Guide: **[Import: CSV](../../imports/csv.md)**. --- ## import databricks ## `datacontract import databricks` Import a data contract from Databricks Unity Catalog. ```bash datacontract import databricks [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to a Unity Catalog TableInfo JSON file. If omitted, imports from the Unity API using --table. | | `--table` | — | Full name of a table in the Unity Catalog (repeat for multiple tables). | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import databricks --table catalog.schema.my_table --output datacontract.yaml ``` Guide: **[Import: Databricks](../../imports/databricks.md)**. --- ## import dbml ## `datacontract import dbml` Import a data contract from a DBML file. ```bash datacontract import dbml [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to the DBML file. | | `--dbml-schema` | — | List of schema names to import from the DBML file (repeat for multiple schema names, leave empty for all tables in the file). | | `--dbml-table` | — | List of table names to import from the DBML file (repeat for multiple table names, leave empty for all tables in the file). | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import dbml --source schema.dbml --output datacontract.yaml ``` Guide: **[Import: DBML](../../imports/dbml.md)**. --- ## import excel ## `datacontract import excel` Import a data contract from an Excel file. ```bash datacontract import excel [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to the Excel file. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import excel --source datacontract.xlsx --output datacontract.yaml ``` Guide: **[Import: Excel](../../imports/excel.md)**. --- ## import gcs ## `datacontract import gcs` Import a data contract from files in Google Cloud Storage. ```bash datacontract import gcs [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | The location of the files. duckdb reads GCS over the S3-compatible endpoint, so use s3://. | | `--format` | — | File format: json, csv, parquet or delta (inferred from the suffix). | | `--delimiter` | — | For JSON: new_line, array or none. Detected automatically when omitted. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import gcs --source s3://my-bucket/orders/*.json --output datacontract.yaml ``` Guide: **[Import: Google Cloud Storage](../../imports/gcs.md)**. --- ## import glue ## `datacontract import glue` Import a data contract from AWS Glue. ```bash datacontract import glue [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--database` | — | Name of the AWS Glue database. | | `--table` | — | List of table ids to import from the Glue Database (repeat for multiple table ids, leave empty for all tables in the dataset). | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import glue --database my_database --table orders --output datacontract.yaml ``` Guide: **[Import: AWS Glue](../../imports/glue.md)**. --- ## import hive ## `datacontract import hive` Import a data contract from a Hive database. ```bash datacontract import hive [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | The host of the HiveServer2. | | `--port` | — | The HiveServer2 port (default 10000). | | `--database` | — | The database name. | | `--table` | — | Name of a table to import (repeat for multiple tables, omit for all tables). | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import hive --source localhost --database default --output datacontract.yaml ``` Guide: **[Import: Hive](../../imports/hive.md)**. --- ## import iceberg ## `datacontract import iceberg` Import a data contract from an Iceberg schema file or a REST catalog. ```bash datacontract import iceberg [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to the Iceberg schema JSON file. Omit to read from a REST catalog. | | `--table` | — | Table name to assign to the model created from the Iceberg schema, or the table to load from the catalog. | | `--catalog-url` | — | REST catalog endpoint to load the table from (DATACONTRACT_ICEBERG_CATALOG_URL). | | `--catalog` | — | Name of the catalog (DATACONTRACT_ICEBERG_CATALOG). | | `--namespace` | — | Namespace of the table in the catalog (DATACONTRACT_ICEBERG_NAMESPACE). | | `--warehouse` | — | Warehouse passed to the catalog (DATACONTRACT_ICEBERG_WAREHOUSE). | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash Examples: datacontract import iceberg --source schema.json --table orders --output datacontract.yaml; datacontract import iceberg --catalog-url https://polaris.example.com/api/catalog --namespace sales --table orders ``` Guide: **[Import: Iceberg](../../imports/iceberg.md)**. --- ## import json ## `datacontract import json` Import a data contract from a JSON file. ```bash datacontract import json [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to the JSON data file. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import json --source data.json --output datacontract.yaml ``` Guide: **[Import: JSON](../../imports/json.md)**. --- ## import jsonschema ## `datacontract import jsonschema` Import a data contract from a JSON Schema file. ```bash datacontract import jsonschema [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to the JSON Schema file. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import jsonschema --source schema.json --output datacontract.yaml ``` Guide: **[Import: JSON Schema](../../imports/jsonschema.md)**. --- ## import mysql ## `datacontract import mysql` Import a data contract from a MySQL database. ```bash datacontract import mysql [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | The host of the MySQL server. | | `--port` | — | The MySQL port (default 3306). | | `--database` | — | The database name. | | `--table` | — | Name of a table to import (repeat for multiple tables, omit for all tables). | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import mysql --source localhost --database mydb --output datacontract.yaml ``` Guide: **[Import: MySQL](../../imports/mysql.md)**. --- ## import odata ## `datacontract import odata` Import a data contract from OData 4.x CSDL XML or JSON metadata, using a URL or local file. ```bash datacontract import odata [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--service-root-url` | required | HTTP(S) root URL of the OData service. | | `--service-document-file` | — | Local JSON service document. Ignored when --entity-set is supplied. | | `--entity-set` | — | EntitySet to import (repeat for multiple sets). If omitted, will import all sets from the service document. | | `--metadata-url` | — | CSDL XML or JSON URL. Defaults to SERVICE_ROOT_URL/$metadata. | | `--metadata-file` | — | Path to a local OData CSDL XML or JSON file. Use either --metadata-file or --metadata-url. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import odata --service-root-url https://example.com/odata/ --entity-set Products --output datacontract.yaml ``` Guide: **[Import: OData](../../imports/odata.md)**. --- ## import odcs ## `datacontract import odcs` Import a data contract from an ODCS file. ```bash datacontract import odcs [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to the ODCS data contract file. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import odcs --source odcs-contract.yaml --output datacontract.yaml ``` Guide: **[Import: ODCS](../../imports/odcs.md)**. --- ## import openapi ## `datacontract import openapi` Import a data contract from the response of a GET or POST operation in an OpenAPI 3.x document. ```bash datacontract import openapi [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to the OpenAPI 3.x document (YAML or JSON). | | `--operation` | — | The GET or POST operation to import, by operationId, method and path, or a GET operation's path (e.g. listOrders, 'POST /orders/search', or /orders). Required unless the document has exactly one GET operation. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import openapi --source openapi.yaml --operation listOrders --output datacontract.yaml ``` Guide: **[Import: OpenAPI](../../imports/openapi.md)**. --- ## import oracle ## `datacontract import oracle` Import a data contract from an Oracle database. ```bash datacontract import oracle [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | The host of the Oracle database. | | `--port` | — | The Oracle port (default 1521). | | `--service-name` | — | The Oracle service name, e.g. XEPDB1. | | `--schema` | — | The owning schema, e.g. ADMIN (Oracle upper-cases it). | | `--table` | — | Name of a table to import (repeat for multiple tables, omit for all tables). | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import oracle --source localhost --service-name XEPDB1 --schema ADMIN --output datacontract.yaml ``` Guide: **[Import: Oracle](../../imports/oracle.md)**. --- ## import parquet ## `datacontract import parquet` Import a data contract from a Parquet file. ```bash datacontract import parquet [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to the Parquet file. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import parquet --source data.parquet --output datacontract.yaml ``` Guide: **[Import: Parquet](../../imports/parquet.md)**. --- ## import postgres ## `datacontract import postgres` Import a data contract from a Postgres schema. ```bash datacontract import postgres [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | The host of the Postgres server. | | `--port` | — | The Postgres port (default 5432). | | `--database` | — | The database name. | | `--schema` | — | The Postgres schema name (default public). | | `--table` | — | Name of a table to import (repeat for multiple tables, omit for all tables in the schema). | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import postgres --source localhost --database postgres --schema public --output datacontract.yaml ``` Guide: **[Import: Postgres](../../imports/postgres.md)**. --- ## import powerbi ## `datacontract import powerbi` Import a data contract from a Power BI semantic model (.pbit, .bim, or .json) file. ```bash datacontract import powerbi [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to a Power BI .pbit, .bim, or .json semantic model file. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import powerbi --source SemanticModel.pbit --output datacontract.yaml ``` Guide: **[Import: Power BI](../../imports/powerbi.md)**. --- ## import protobuf ## `datacontract import protobuf` Import a data contract from a Protobuf schema file. ```bash datacontract import protobuf [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to the Protobuf .proto file. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import protobuf --source schema.proto --output datacontract.yaml ``` Guide: **[Import: Protobuf](../../imports/protobuf.md)**. --- ## import pydantic-model ## `datacontract import pydantic-model` Import a data contract from Pydantic models. ```bash datacontract import pydantic-model [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to the Python file defining the Pydantic models. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import pydantic-model --source models.py --output datacontract.yaml ``` Guide: **[Import: Pydantic Model](../../imports/pydantic-model.md)**. --- ## import redshift ## `datacontract import redshift` Import a data contract from an Amazon Redshift schema. ```bash datacontract import redshift [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | The Redshift endpoint host of the cluster or serverless workgroup. | | `--port` | — | The Redshift port (default 5439). | | `--database` | — | The database name. | | `--schema` | — | The Redshift schema name. | | `--table` | — | Name of a table to import (repeat for multiple tables, omit for all tables in the schema). | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import redshift --source my-cluster.abc123.us-east-1.redshift.amazonaws.com --database dev --schema public --output datacontract.yaml ``` Guide: **[Import: Amazon Redshift](../../imports/redshift.md)**. --- ## import s3 ## `datacontract import s3` Import a data contract from files in S3. ```bash datacontract import s3 [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | The S3 location of the files, e.g. s3://my-bucket/orders/*.json. | | `--format` | — | File format: json, csv, parquet or delta (inferred from the suffix). | | `--delimiter` | — | For JSON: new_line, array or none. Detected automatically when omitted. | | `--endpoint-url` | — | Endpoint of an S3-compatible store, e.g. http://localhost:9000. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import s3 --source s3://my-bucket/orders/*.json --output datacontract.yaml ``` Guide: **[Import: Amazon S3](../../imports/s3.md)**. --- ## import snowflake ## `datacontract import snowflake` Import a data contract from a Snowflake workspace. ```bash datacontract import snowflake [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Snowflake account name. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--database` | — | The database name. | | `--schema` | — | Snowflake schema name. | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import snowflake --source account --database DEMO_DB --schema PUBLIC --output datacontract.yaml ``` Guide: **[Import: Snowflake](../../imports/snowflake.md)**. --- ## import spark ## `datacontract import spark` Import a data contract from a Spark schema. ```bash datacontract import spark [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--tables` | — | Comma-separated list of Spark table names to import from the current Spark session. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import spark --tables orders,customers --output datacontract.yaml ``` Guide: **[Import: Spark](../../imports/spark.md)**. --- ## import sql ## `datacontract import sql` Import a data contract from a SQL DDL file. ```bash datacontract import sql [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to the SQL DDL file. | | `--dialect` | — | The SQL dialect. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import sql --source ddl.sql --dialect postgres --output datacontract.yaml ``` Guide: **[Import: SQL DDL](../../imports/sql.md)**. --- ## import sqlserver ## `datacontract import sqlserver` Import a data contract from a SQL Server database. ```bash datacontract import sqlserver [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | The host of the SQL Server instance. | | `--port` | — | The SQL Server port (default 1433). | | `--database` | — | The database name. | | `--schema` | — | The schema name (default dbo). | | `--table` | — | Name of a table to import (repeat for multiple tables, omit for all tables). | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import sqlserver --source localhost --database mydb --output datacontract.yaml ``` Guide: **[Import: SQL Server](../../imports/sqlserver.md)**. --- ## import trino ## `datacontract import trino` Import a data contract from a Trino catalog. ```bash datacontract import trino [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | The host of the Trino coordinator. | | `--port` | — | The Trino port (default 8080). | | `--catalog` | — | The Trino catalog. | | `--schema` | — | The Trino schema. | | `--table` | — | Name of a table to import (repeat for multiple tables, omit for all tables). | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import trino --source localhost --catalog my_catalog --schema my_schema --output datacontract.yaml ``` Guide: **[Import: Trino](../../imports/trino.md)**. --- ## import unity ## `datacontract import unity` Import a data contract from Databricks Unity Catalog (alias of `import databricks`). ```bash datacontract import unity [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to a Unity Catalog TableInfo JSON file. If omitted, imports from the Unity API using --table. | | `--table` | — | Full name of a table in the Unity Catalog (repeat for multiple tables). | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import databricks --table catalog.schema.my_table --output datacontract.yaml ``` --- ## import xsd ## `datacontract import xsd` Import a data contract from an XML Schema (XSD) file. ```bash datacontract import xsd [OPTIONS] ``` | Option | Default | Description | |---|---|---| | `--source` | — | Path to the XML Schema (XSD) file. | | `--output` | — | File path where the Data Contract will be saved. If not provided, it will be printed to stdout. | | `--json-schema` | — | The location (url or path) of the ODCS JSON Schema | | `--owner` | — | The owner or team responsible for managing the data contract. | | `--id` | — | The identifier for the data contract. | | `--debug` / `--no-debug` | — | Enable debug logging | ```bash datacontract import xsd --source schema.xsd --output datacontract.yaml ``` Guide: **[Import: XML Schema](../../imports/xsd.md)**. --- ## Configuration Connecting to a data source needs two kinds of input: **connection details** (host, database, catalog, …), which live in the contract's `servers` block, and **credentials and connection options** (usernames, passwords, tokens, timeouts, …), which are configuration. Every configuration option has a canonical name, its environment variable, such as `DATACONTRACT_SNOWFLAKE_USERNAME`. The [data source pages](./testing/index.md) list the options each source supports. There are five ways to provide configuration. All of them address options by the same names, and all of them end up in the same place: a `Config` object that is passed through to the connection. ### Environment variables The default, and the right choice for CI/CD: ```bash export DATACONTRACT_SNOWFLAKE_USERNAME=svc_test export DATACONTRACT_SNOWFLAKE_PASSWORD=... datacontract test datacontract.yaml ``` Environment variables work in every context: CLI, Python library, and API server. ### .env file The CLI loads a `.env` file from the current working directory, or the nearest parent directory containing one. Values from `.env` fill in missing environment variables; variables that are already set take precedence. ```bash # .env DATACONTRACT_POSTGRES_USERNAME=postgres DATACONTRACT_POSTGRES_PASSWORD=postgres ``` The `.env` values are exported into the process environment, so they also reach tools that read the environment directly, such as boto3 (`AWS_PROFILE`), the Databricks SDK, and Snowflake's `SNOWFLAKE_HOME`. ### Variables in the data contract Since ODCS v3.2.0, any string value in a data contract may reference an environment variable as `${VAR_NAME}`, with an optional default: `${VAR_NAME:-default}`. This keeps environment-specific values such as hostnames and bucket paths out of the contract. ```yaml servers: - server: production type: postgresql host: ${DB_HOST} port: ${DB_PORT:-5432} database: ${DB_NAME:-orders} schema: - name: orders quality: - type: sql query: SELECT COUNT(*) FROM orders WHERE created_at > '${CUTOFF_DATE}' mustBe: 0 ``` Values come from the environment, including a loaded `.env` file. If a variable is unset or empty and has no default, the run fails. References are resolved only when testing: `lint`, `export` and `publish` leave them as written. [Per-source override options](#all-options) such as `DATACONTRACT_POSTGRES_HOST` take precedence over the contract. On the [API server](./api.md), a posted contract can't read the operator's environment unless you allow it. Pass a comma-separated list of glob patterns with `--contract-variables` (or `DATACONTRACT_CLI_API_CONTRACT_VARIABLES`): ```bash datacontract api --contract-variables 'TABLE_*,CUTOFF_DATE' ``` Variables not on the list are treated as unset. `*` exposes every variable, credentials included. ### Config file (YAML) A YAML config file groups options in sections per data source. String values may contain `${VAR}` and `${VAR:-default}` references, resolved when the file is loaded. Pass it with the global `--config-file` option, or place it at one of the default locations: `./datacontract-config.yaml` or `~/.datacontract/config.yaml`. ```yaml # datacontract-config.yaml snowflake: username: svc_test password: ${SNOWFLAKE_PASSWORD} role: TESTER login_timeout: 30 max_errors: 10 ``` ```bash datacontract --config-file datacontract-config.yaml test datacontract.yaml ``` Section and key join to form the option name (`snowflake.username` is `DATACONTRACT_SNOWFLAKE_USERNAME`). `${VAR}` references resolve from the environment when the file is loaded, so the file can be committed without holding secrets; add it to `.gitignore` if it contains literal credentials. Unknown option names fail with an error naming the key, so typos surface immediately. In the Python library, load the same file with `Config.from_yaml("datacontract-config.yaml")`. ### Python library Pass configuration programmatically via the `config` argument, without touching the process environment: ```python from datacontract import Config from datacontract.data_contract import DataContract run = DataContract( data_contract_file="datacontract.yaml", server="production", config=Config( snowflake_username="svc_test", snowflake_password=get_secret("snowflake"), snowflake_role="TESTER", ), ).test() ``` The `Config` class declares every option as a typed field. Field names match the environment variable names (`snowflake_username` for `DATACONTRACT_SNOWFLAKE_USERNAME`), unknown keyword arguments raise immediately, and secrets are held as `SecretStr` so they stay out of logs and reprs. A plain dict keyed by the environment variable names is accepted as well. `DataContract.import_from_source()` takes the same argument. See [Python Library](./python-library.md#credentials) for details. Because the config object is passed explicitly through the connection layer, concurrent operations with different credentials in one process do not interfere. ### API server The [API server](./api.md) reads its configuration from environment variables set before startup. In addition, `POST /test` accepts per-request credentials via `datacontract-*` headers, matched case-insensitively and mapped to the option names: ```bash curl -X POST https://datacontract.example.com/test \ -H "Content-Type: application/yaml" \ -H "datacontract-snowflake-username: svc_test" \ -H "datacontract-snowflake-password: $SNOWFLAKE_PASSWORD" \ --data-binary @datacontract.yaml ``` Header values apply to that request only, so one server can test contracts for different tenants without sharing credentials through the process environment. Unknown option names are rejected with a 400. ### All options Every option, by its environment variable name and the matching `Config` field. The [data source pages](./testing/index.md) explain how each source uses them. `${VAR}`-style config file keys drop the `DATACONTRACT_` prefix and the section name (`snowflake.username` for `DATACONTRACT_SNOWFLAKE_USERNAME`); API headers are the environment variable name in lowercase with dashes (`datacontract-snowflake-username`). #### Entropy Data (publishing, remote contracts) | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `ENTROPY_DATA_API_KEY` | `entropy_data_api_key` | string (secret) | | | `ENTROPY_DATA_HOST` | `entropy_data_host` | string | | | `DATAMESH_MANAGER_API_KEY` | `datamesh_manager_api_key` | string (secret) | Deprecated, use `ENTROPY_DATA_API_KEY` | | `DATAMESH_MANAGER_HOST` | `datamesh_manager_host` | string | Deprecated, use `ENTROPY_DATA_HOST` | | `DATACONTRACT_MANAGER_API_KEY` | `datacontract_manager_api_key` | string (secret) | Deprecated, use `ENTROPY_DATA_API_KEY` | | `DATACONTRACT_MANAGER_HOST` | `datacontract_manager_host` | string | Deprecated, use `ENTROPY_DATA_HOST` | #### General | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_API_HEADER_AUTHORIZATION` | `api_header_authorization` | string (secret) | | | `DATACONTRACT_API_HEADER_X_API_KEY` | `api_header_x_api_key` | string (secret) | | | `DATACONTRACT_CUSTOM_QUALITY_CHECKS` | `custom_quality_checks` | string | | | `DATACONTRACT_MAX_ERRORS` | `max_errors` | integer | | | `DATACONTRACT_CONNECT_TIMEOUT` | `connect_timeout` | integer | | #### Athena | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_ATHENA_CATALOG` | `athena_catalog` | string | Overrides `catalog` from the contract's `servers` block | | `DATACONTRACT_ATHENA_SCHEMA` | `athena_schema` | string | Overrides `schema` from the contract's `servers` block | | `DATACONTRACT_ATHENA_STAGING_DIR` | `athena_staging_dir` | string | Overrides `stagingDir` from the contract's `servers` block | | `DATACONTRACT_ATHENA_WORKGROUP` | `athena_workgroup` | string | Overrides `workgroup` from the contract's `servers` block | #### Azure | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_AZURE_CONNECTION_STRING` | `azure_connection_string` | string (secret) | | | `DATACONTRACT_AZURE_STORAGE_ACCOUNT_KEY` | `azure_storage_account_key` | string (secret) | | | `DATACONTRACT_AZURE_TENANT_ID` | `azure_tenant_id` | string | | | `DATACONTRACT_AZURE_CLIENT_ID` | `azure_client_id` | string | | | `DATACONTRACT_AZURE_CLIENT_SECRET` | `azure_client_secret` | string (secret) | | #### BigQuery | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_BIGQUERY_ACCOUNT_INFO_JSON_PATH` | `bigquery_account_info_json_path` | string | | | `DATACONTRACT_BIGQUERY_BILLING_PROJECT` | `bigquery_billing_project` | string | | | `DATACONTRACT_BIGQUERY_IMPERSONATION_ACCOUNT` | `bigquery_impersonation_account` | string | | | `DATACONTRACT_BIGQUERY_PROJECT` | `bigquery_project` | string | Overrides `project` from the contract's `servers` block | | `DATACONTRACT_BIGQUERY_DATASET` | `bigquery_dataset` | string | Overrides `dataset` from the contract's `servers` block | #### ClickHouse | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_CLICKHOUSE_USERNAME` | `clickhouse_username` | string | | | `DATACONTRACT_CLICKHOUSE_PASSWORD` | `clickhouse_password` | string (secret) | | | `DATACONTRACT_CLICKHOUSE_SECURE` | `clickhouse_secure` | boolean | | | `DATACONTRACT_CLICKHOUSE_HOST` | `clickhouse_host` | string | Overrides `host` from the contract's `servers` block | | `DATACONTRACT_CLICKHOUSE_PORT` | `clickhouse_port` | integer | Overrides `port` from the contract's `servers` block | | `DATACONTRACT_CLICKHOUSE_DATABASE` | `clickhouse_database` | string | Overrides `database` from the contract's `servers` block | #### Databricks | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_DATABRICKS_SERVER_HOSTNAME` | `databricks_server_hostname` | string | Overrides `host` from the contract's `servers` block | | `DATACONTRACT_DATABRICKS_HTTP_PATH` | `databricks_http_path` | string | | | `DATACONTRACT_DATABRICKS_TOKEN` | `databricks_token` | string (secret) | | | `DATACONTRACT_DATABRICKS_CLIENT_ID` | `databricks_client_id` | string | | | `DATACONTRACT_DATABRICKS_CLIENT_SECRET` | `databricks_client_secret` | string (secret) | | | `DATACONTRACT_DATABRICKS_PROFILE` | `databricks_profile` | string | | | `DATACONTRACT_DATABRICKS_AUTH_TYPE` | `databricks_auth_type` | string | | | `DATACONTRACT_DATABRICKS_CATALOG` | `databricks_catalog` | string | Overrides `catalog` from the contract's `servers` block | | `DATACONTRACT_DATABRICKS_SCHEMA` | `databricks_schema` | string | Overrides `schema` from the contract's `servers` block | #### DuckDB | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_DUCKDB_DATABASE` | `duckdb_database` | string | Overrides `database` from the contract's `servers` block | | `DATACONTRACT_DUCKDB_SCHEMA` | `duckdb_schema` | string | Overrides `schema` from the contract's `servers` block | #### GCS | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_GCS_KEY_ID` | `gcs_key_id` | string | | | `DATACONTRACT_GCS_SECRET` | `gcs_secret` | string (secret) | | #### SAP HANA | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_HANA_USERNAME` | `hana_username` | string | | | `DATACONTRACT_HANA_PASSWORD` | `hana_password` | string (secret) | | | `DATACONTRACT_HANA_ENCRYPT` | `hana_encrypt` | boolean | | | `DATACONTRACT_HANA_SSL_VALIDATE_CERTIFICATE` | `hana_ssl_validate_certificate` | boolean | | | `DATACONTRACT_HANA_SSL_HOSTNAME_IN_CERTIFICATE` | `hana_ssl_hostname_in_certificate` | string | | #### Hive | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_HIVE_USERNAME` | `hive_username` | string | | | `DATACONTRACT_HIVE_PASSWORD` | `hive_password` | string (secret) | | | `DATACONTRACT_HIVE_AUTH_MECHANISM` | `hive_auth_mechanism` | string | | | `DATACONTRACT_HIVE_USE_SSL` | `hive_use_ssl` | boolean | | | `DATACONTRACT_HIVE_USE_HTTP_TRANSPORT` | `hive_use_http_transport` | boolean | | | `DATACONTRACT_HIVE_HTTP_PATH` | `hive_http_path` | string | | | `DATACONTRACT_HIVE_HOST` | `hive_host` | string | Overrides `host` from the contract's `servers` block | | `DATACONTRACT_HIVE_PORT` | `hive_port` | integer | Overrides `port` from the contract's `servers` block | | `DATACONTRACT_HIVE_DATABASE` | `hive_database` | string | Overrides `database` from the contract's `servers` block | #### Iceberg | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_ICEBERG_CATALOG_TYPE` | `iceberg_catalog_type` | string | | | `DATACONTRACT_ICEBERG_CREDENTIAL` | `iceberg_credential` | string (secret) | | | `DATACONTRACT_ICEBERG_TOKEN` | `iceberg_token` | string (secret) | | | `DATACONTRACT_ICEBERG_S3_ENDPOINT` | `iceberg_s3_endpoint` | string | | | `DATACONTRACT_ICEBERG_SIGNING_NAME` | `iceberg_signing_name` | string | | | `DATACONTRACT_ICEBERG_PROPERTIES` | `iceberg_properties` | string | | | `DATACONTRACT_ICEBERG_CATALOG_URL` | `iceberg_catalog_url` | string | Overrides `catalogUrl` from the contract's `servers` block | | `DATACONTRACT_ICEBERG_CATALOG` | `iceberg_catalog` | string | Overrides `catalog` from the contract's `servers` block | | `DATACONTRACT_ICEBERG_NAMESPACE` | `iceberg_namespace` | string | Overrides `namespace` from the contract's `servers` block | | `DATACONTRACT_ICEBERG_WAREHOUSE` | `iceberg_warehouse` | string | Overrides `warehouse` from the contract's `servers` block | #### Impala | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_IMPALA_USERNAME` | `impala_username` | string | | | `DATACONTRACT_IMPALA_PASSWORD` | `impala_password` | string (secret) | | | `DATACONTRACT_IMPALA_AUTH_MECHANISM` | `impala_auth_mechanism` | string | | | `DATACONTRACT_IMPALA_HTTP_PATH` | `impala_http_path` | string | | | `DATACONTRACT_IMPALA_USE_SSL` | `impala_use_ssl` | boolean | | | `DATACONTRACT_IMPALA_USE_HTTP_TRANSPORT` | `impala_use_http_transport` | boolean | | | `DATACONTRACT_IMPALA_HOST` | `impala_host` | string | Overrides `host` from the contract's `servers` block | | `DATACONTRACT_IMPALA_PORT` | `impala_port` | integer | Overrides `port` from the contract's `servers` block | | `DATACONTRACT_IMPALA_DATABASE` | `impala_database` | string | Overrides `database` from the contract's `servers` block | #### Kafka | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_KAFKA_SASL_USERNAME` | `kafka_sasl_username` | string | | | `DATACONTRACT_KAFKA_SASL_PASSWORD` | `kafka_sasl_password` | string (secret) | | | `DATACONTRACT_KAFKA_SASL_MECHANISM` | `kafka_sasl_mechanism` | string | | | `DATACONTRACT_KAFKA_SCHEMA_REGISTRY_URL` | `kafka_schema_registry_url` | string | | | `DATACONTRACT_KAFKA_SCHEMA_REGISTRY_USERNAME` | `kafka_schema_registry_username` | string | | | `DATACONTRACT_KAFKA_SCHEMA_REGISTRY_PASSWORD` | `kafka_schema_registry_password` | string (secret) | | | `DATACONTRACT_KAFKA_MAX_MESSAGES` | `kafka_max_messages` | integer | | | `DATACONTRACT_KAFKA_TIMEOUT` | `kafka_timeout` | integer | | | `DATACONTRACT_KAFKA_GROUP_PREFIX` | `kafka_group_prefix` | string | | #### MySQL | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_MYSQL_USERNAME` | `mysql_username` | string | | | `DATACONTRACT_MYSQL_PASSWORD` | `mysql_password` | string (secret) | | | `DATACONTRACT_MYSQL_HOST` | `mysql_host` | string | Overrides `host` from the contract's `servers` block | | `DATACONTRACT_MYSQL_PORT` | `mysql_port` | integer | Overrides `port` from the contract's `servers` block | | `DATACONTRACT_MYSQL_DATABASE` | `mysql_database` | string | Overrides `database` from the contract's `servers` block | #### Oracle | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_ORACLE_USERNAME` | `oracle_username` | string | | | `DATACONTRACT_ORACLE_PASSWORD` | `oracle_password` | string (secret) | | | `DATACONTRACT_ORACLE_CLIENT_DIR` | `oracle_client_dir` | string | | | `DATACONTRACT_ORACLE_HOST` | `oracle_host` | string | Overrides `host` from the contract's `servers` block | | `DATACONTRACT_ORACLE_PORT` | `oracle_port` | integer | Overrides `port` from the contract's `servers` block | | `DATACONTRACT_ORACLE_SERVICE_NAME` | `oracle_service_name` | string | Overrides `serviceName` from the contract's `servers` block | #### Postgres | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_POSTGRES_USERNAME` | `postgres_username` | string | | | `DATACONTRACT_POSTGRES_PASSWORD` | `postgres_password` | string (secret) | | | `DATACONTRACT_POSTGRES_HOST` | `postgres_host` | string | Overrides `host` from the contract's `servers` block | | `DATACONTRACT_POSTGRES_PORT` | `postgres_port` | integer | Overrides `port` from the contract's `servers` block | | `DATACONTRACT_POSTGRES_DATABASE` | `postgres_database` | string | Overrides `database` from the contract's `servers` block | | `DATACONTRACT_POSTGRES_SCHEMA` | `postgres_schema` | string | Overrides `schema` from the contract's `servers` block | #### Redshift | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_REDSHIFT_AUTHENTICATION` | `redshift_authentication` | string | | | `DATACONTRACT_REDSHIFT_USERNAME` | `redshift_username` | string | | | `DATACONTRACT_REDSHIFT_PASSWORD` | `redshift_password` | string (secret) | | | `DATACONTRACT_REDSHIFT_SSLMODE` | `redshift_sslmode` | string | | | `DATACONTRACT_REDSHIFT_DB_USER` | `redshift_db_user` | string | | | `DATACONTRACT_REDSHIFT_DB_GROUPS` | `redshift_db_groups` | string | | | `DATACONTRACT_REDSHIFT_AUTO_CREATE` | `redshift_auto_create` | boolean | | | `DATACONTRACT_REDSHIFT_WORKGROUP` | `redshift_workgroup` | string | | | `DATACONTRACT_REDSHIFT_CLUSTER_IDENTIFIER` | `redshift_cluster_identifier` | string | | | `DATACONTRACT_REDSHIFT_REGION` | `redshift_region` | string | | | `DATACONTRACT_REDSHIFT_DURATION_SECONDS` | `redshift_duration_seconds` | integer | | | `DATACONTRACT_REDSHIFT_HOST` | `redshift_host` | string | Overrides `host` from the contract's `servers` block | | `DATACONTRACT_REDSHIFT_PORT` | `redshift_port` | integer | Overrides `port` from the contract's `servers` block | | `DATACONTRACT_REDSHIFT_DATABASE` | `redshift_database` | string | Overrides `database` from the contract's `servers` block | | `DATACONTRACT_REDSHIFT_SCHEMA` | `redshift_schema` | string | Overrides `schema` from the contract's `servers` block | #### S3 / AWS (also Athena, Redshift IAM, Glue) | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_S3_ACCESS_KEY_ID` | `s3_access_key_id` | string | | | `DATACONTRACT_S3_SECRET_ACCESS_KEY` | `s3_secret_access_key` | string (secret) | | | `DATACONTRACT_S3_SESSION_TOKEN` | `s3_session_token` | string (secret) | | | `DATACONTRACT_S3_REGION` | `s3_region` | string | | #### Snowflake | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_SNOWFLAKE_USERNAME` | `snowflake_username` | string | | | `DATACONTRACT_SNOWFLAKE_PASSWORD` | `snowflake_password` | string (secret) | | | `DATACONTRACT_SNOWFLAKE_AUTHENTICATOR` | `snowflake_authenticator` | string | | | `DATACONTRACT_SNOWFLAKE_ROLE` | `snowflake_role` | string | | | `DATACONTRACT_SNOWFLAKE_TOKEN` | `snowflake_token` | string (secret) | | | `DATACONTRACT_SNOWFLAKE_PASSCODE` | `snowflake_passcode` | string (secret) | | | `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY` | `snowflake_private_key` | string (secret) | | | `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_FILE` | `snowflake_private_key_file` | string | | | `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_FILE_PWD` | `snowflake_private_key_file_pwd` | string (secret) | | | `DATACONTRACT_SNOWFLAKE_WAREHOUSE` | `snowflake_warehouse` | string | | | `DATACONTRACT_SNOWFLAKE_CREATE_OBJECT_UDFS` | `snowflake_create_object_udfs` | boolean | | | `DATACONTRACT_SNOWFLAKE_LOGIN_TIMEOUT` | `snowflake_login_timeout` | integer | | | `DATACONTRACT_SNOWFLAKE_NETWORK_TIMEOUT` | `snowflake_network_timeout` | integer | | | `DATACONTRACT_SNOWFLAKE_SOCKET_TIMEOUT` | `snowflake_socket_timeout` | integer | | | `DATACONTRACT_SNOWFLAKE_HOST` | `snowflake_host` | string | | | `DATACONTRACT_SNOWFLAKE_PORT` | `snowflake_port` | integer | | | `DATACONTRACT_SNOWFLAKE_HOME` | `snowflake_home` | string | | | `DATACONTRACT_SNOWFLAKE_CONNECTIONS_FILE` | `snowflake_connections_file` | string | | | `DATACONTRACT_SNOWFLAKE_DEFAULT_CONNECTION_NAME` | `snowflake_default_connection_name` | string | | | `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_PATH` | `snowflake_private_key_path` | string | Deprecated, use `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_FILE` | | `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_PASSPHRASE` | `snowflake_private_key_passphrase` | string (secret) | Deprecated, use `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_FILE_PWD` | | `DATACONTRACT_SNOWFLAKE_CONNECTION_TIMEOUT` | `snowflake_connection_timeout` | integer | Deprecated, use `DATACONTRACT_SNOWFLAKE_LOGIN_TIMEOUT` | | `DATACONTRACT_SNOWFLAKE_ACCOUNT` | `snowflake_account` | string | Overrides `account` from the contract's `servers` block | | `DATACONTRACT_SNOWFLAKE_DATABASE` | `snowflake_database` | string | Overrides `database` from the contract's `servers` block | | `DATACONTRACT_SNOWFLAKE_SCHEMA` | `snowflake_schema` | string | Overrides `schema` from the contract's `servers` block | #### SQL Server | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_SQLSERVER_AUTHENTICATION` | `sqlserver_authentication` | string | | | `DATACONTRACT_SQLSERVER_USERNAME` | `sqlserver_username` | string | | | `DATACONTRACT_SQLSERVER_PASSWORD` | `sqlserver_password` | string (secret) | | | `DATACONTRACT_SQLSERVER_CLIENT_ID` | `sqlserver_client_id` | string | | | `DATACONTRACT_SQLSERVER_CLIENT_SECRET` | `sqlserver_client_secret` | string (secret) | | | `DATACONTRACT_SQLSERVER_DRIVER` | `sqlserver_driver` | string | | | `DATACONTRACT_SQLSERVER_ENCRYPTED_CONNECTION` | `sqlserver_encrypted_connection` | boolean | | | `DATACONTRACT_SQLSERVER_TRUST_SERVER_CERTIFICATE` | `sqlserver_trust_server_certificate` | boolean | | | `DATACONTRACT_SQLSERVER_TRUSTED_CONNECTION` | `sqlserver_trusted_connection` | boolean | | | `DATACONTRACT_SQLSERVER_HOST` | `sqlserver_host` | string | Overrides `host` from the contract's `servers` block | | `DATACONTRACT_SQLSERVER_PORT` | `sqlserver_port` | integer | Overrides `port` from the contract's `servers` block | | `DATACONTRACT_SQLSERVER_DATABASE` | `sqlserver_database` | string | Overrides `database` from the contract's `servers` block | #### Exasol | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_EXASOL_USERNAME` | `exasol_username` | string | | | `DATACONTRACT_EXASOL_PASSWORD` | `exasol_password` | string (secret) | | | `DATACONTRACT_EXASOL_FINGERPRINT` | `exasol_fingerprint` | string | | | `DATACONTRACT_EXASOL_VALIDATE_CERTIFICATE` | `exasol_validate_certificate` | boolean | | | `DATACONTRACT_EXASOL_HOST` | `exasol_host` | string | Overrides `host` from the contract's `servers` block | | `DATACONTRACT_EXASOL_PORT` | `exasol_port` | integer | Overrides `port` from the contract's `servers` block | | `DATACONTRACT_EXASOL_SCHEMA` | `exasol_schema` | string | Overrides `schema` from the contract's `servers` block | #### Trino | Environment variable | `Config` field | Type | Notes | |---|---|---|---| | `DATACONTRACT_TRINO_AUTHENTICATION` | `trino_authentication` | string | | | `DATACONTRACT_TRINO_USERNAME` | `trino_username` | string | | | `DATACONTRACT_TRINO_PASSWORD` | `trino_password` | string (secret) | | | `DATACONTRACT_TRINO_JWT_TOKEN` | `trino_jwt_token` | string (secret) | | | `DATACONTRACT_TRINO_HOST` | `trino_host` | string | Overrides `host` from the contract's `servers` block | | `DATACONTRACT_TRINO_PORT` | `trino_port` | integer | Overrides `port` from the contract's `servers` block | | `DATACONTRACT_TRINO_CATALOG` | `trino_catalog` | string | Overrides `catalog` from the contract's `servers` block | | `DATACONTRACT_TRINO_SCHEMA` | `trino_schema` | string | Overrides `schema` from the contract's `servers` block | Process-level variables are not `Config` options and stay environment-only: `DATACONTRACT_CLI_DEBUG` (debug logging), `DATACONTRACT_CLI_API_KEY` (see [API](./api.md)), and `DATACONTRACT_SYSTEM_TRUSTSTORE` (see the [command reference](./commands/index.md)). ### Precedence Within one operation, resolution is: 1. **Explicit config** for the options it sets: the `Config` object or dict passed to `DataContract`, the loaded `--config-file`, or the request's `datacontract-*` headers. 2. **Environment variables** for everything the explicit config leaves unset. 3. **`.env`** fills in environment variables that are not already set (it never overrides). So a committed team config file can hold defaults, CI can override single values via environment variables, and code can override everything. ### Notes for specific sources - **Snowflake**: only the documented `DATACONTRACT_SNOWFLAKE_*` options are passed to the connector. Unknown names are ignored with a warning. `datacontract test` does not read a `connections.toml`; only `datacontract import snowflake` does (see the [Snowflake Reference](./reference/snowflake.md)). - **AWS sources (S3, Athena, Redshift IAM, Glue)**: when no `DATACONTRACT_S3_*` options are set, boto3's own credential chain applies (`aws sso login`, `AWS_PROFILE`, instance roles, GitHub OIDC). The same credential set is used to read data contracts from `s3://` locations. --- ## API (2) The Data Contract CLI can run as a web server that exposes a REST API for data contract testing, linting, exporting, and changelogs. This is useful for integrating data contract checks into other services. You can try a public demo at [api.datacontract.com](https://api.datacontract.com). Note that the demo endpoint cannot connect to your secured data sources. ### Starting the API The API requires the `api` extra: ```bash pip install 'datacontract-cli[api]' ``` Start the server: ```bash datacontract api ``` | Option | Default | Description | |---|---|---| | `--port` | `4242` | Bind the socket to this port. | | `--host` | `127.0.0.1` | Bind to this host. In Docker, use `0.0.0.0`. | | `--debug` / `--no-debug` | `--no-debug` | Enable debug logging. | You can pass extra keyword arguments through to `uvicorn.run()`, e.g.: ```bash datacontract api --port 1234 --root_path /datacontract ``` ### OpenAPI / Swagger UI Once running, open the interactive OpenAPI documentation (Swagger UI) at [http://localhost:4242](http://localhost:4242). You can execute the commands directly from the UI. The OpenAPI 3.1 document itself is served at `http://localhost:4242/openapi.json` and can be fed to a client generator: ```bash curl -s http://localhost:4242/openapi.json > openapi.json ``` ### Test a data contract POST a data contract as the request body to `/test` and receive the test results as JSON: ```bash curl -X POST "http://localhost:4242/test?server=production" \ --data-binary @datacontract.yaml ``` You can also send the YAML inline with `-H 'Content-Type: application/yaml'`. ### Export a data contract ```bash curl -X POST "http://localhost:4242/export?format=sql" \ --data-binary @datacontract.yaml ``` ### Comparing two contracts Both comparison endpoints take the same JSON body: `v1` (before) and `v2` (after) as YAML strings. `POST /changelog` lists what changed. The response is a JSON object with `summary` (one entry per changed field) and `entries` (one per atomic change): ```bash curl -X POST "http://localhost:4242/changelog" \ -H "Content-Type: application/json" \ -d '{ "v1": "'"$(cat v1.odcs.yaml)"'", "v2": "'"$(cat v2.odcs.yaml)"'" }' ``` `POST /breaking` grades those same changes for compatibility impact. It adds a `level` (`info`, `warning` or `error`) and a `rule_id` to every entry, plus a top-level `is_breaking` flag that is `true` when any entry is an `error`: ```bash curl -X POST "http://localhost:4242/breaking" \ -H "Content-Type: application/json" \ -d '{ "v1": "'"$(cat v1.odcs.yaml)"'", "v2": "'"$(cat v2.odcs.yaml)"'" }' ``` ```json { "summary": [...], "entries": [ { "path": "schema.orders.properties.order_id.logicalTypeOptions.pattern", "change_type": "updated", "level": "warning", "direction": "undetermined", "message": "Changed validation constraint at schema.orders.properties.order_id.logicalTypeOptions.pattern from '^ORD-[0-9]+$' to '^ORD-[0-9]{4}$'", "rule_id": "validation-constraint-changed", "old_value": "^ORD-[0-9]+$", "new_value": "^ORD-[0-9]{4}$" } ], "perspective": "consumer", "is_breaking": false } ``` See [Compare contract versions](./compare-contract-versions.md) for the severity levels and the rules behind them. ### Configure server credentials To connect to a data source, set the required credentials as environment variables **before starting the API** (see [Configuration](./configuration.md)). For example, for Snowflake: ```bash export DATACONTRACT_SNOWFLAKE_USERNAME=123 export DATACONTRACT_SNOWFLAKE_PASSWORD= export DATACONTRACT_SNOWFLAKE_WAREHOUSE= export DATACONTRACT_SNOWFLAKE_ROLE= ``` Alternatively, `POST /test` accepts credentials per request via `datacontract-*` headers (e.g. `datacontract-snowflake-password`), matched case-insensitively and applied to that request only. This allows one server to test contracts for different tenants without sharing credentials through the process environment. Serve the API over HTTPS when sending credential headers. ### Secure the API Set `DATACONTRACT_CLI_API_KEY` to a secret value (such as a random UUID) to require authentication. Every endpoint then requires the header `x-api-key` with the correct key, and answers `401` when it is missing and `403` when it is wrong. ```bash export DATACONTRACT_CLI_API_KEY= ``` :::warning Securing the API is highly recommended. Data contract tests may otherwise be subject to SQL injection or leak sensitive information. ::: ### Posted contracts are untrusted A data contract carries SQL and names the hosts to connect to, so a contract that arrives over HTTP is treated as untrusted input, whether or not the API key is set: - a `quality.type: sql` rule must be a **read-only query** — DDL, DML, `COPY`, `ATTACH` and the like are reported as a failed check instead of being executed; - a credential held in the server's environment is **never sent to a host the contract names** (see [Configuration](./configuration.md)); - a `publish_url` may only point at the **Entropy Data platform or the host set via `ENTROPY_DATA_HOST`** on the server — per-request `entropy-data-host` headers do not widen this, and other hosts are refused; - `authoritativeDefinitions` are **only resolved against the Entropy Data platform or the host set via `ENTROPY_DATA_HOST`** on the server — per-request `entropy-data-host` headers do not widen this; a URL on any other host, or a reference to a local file, is refused, and a failed lookup reports only the URL, never what the host answered. An API key goes only to the host it belongs to — the server's `ENTROPY_DATA_API_KEY` to exactly `ENTROPY_DATA_HOST`, an `entropy-data-api-key` sent to `/test` to that request's `entropy-data-host`, with all Entropy Data domains counting as one host; - `servers[].type: local` is **refused**, so a caller cannot read the files of the machine running the API; - an `api` server's requests **may not reach a private network address**, so a caller cannot read or write services in the server's own network; proxies and CA bundles from the environment (`HTTPS_PROXY`, `REQUESTS_CA_BUNDLE`) are not used for them; - a `${VAR}` reference in the contract resolves **only from the variables the operator allow-listed**, so a posted contract cannot read the server's environment; - for a file-based server type (`s3`, `gcs`, `azure`), the DuckDB connection is **confined to the data locations the contract declares**. If the deployment serves its own files on purpose — the data mounted next to the API in the same container, say — allow it explicitly: ```bash export DATACONTRACT_CLI_API_ALLOW_LOCAL_FILES=true ``` The contract is then still confined to the paths it declares, but a caller chooses those paths, so only turn this on where callers are trusted. Likewise, a posted contract resolves no `${VAR}` references at all until you name the variables it may read — the environment holds your credentials, not the contract author's: ```bash export DATACONTRACT_CLI_API_CONTRACT_VARIABLES='TABLE_*,CUTOFF_DATE' ``` If the APIs to test live in the server's own network, allow private network addresses for `api` servers: ```bash export DATACONTRACT_CLI_API_ALLOW_PRIVATE_NETWORK=true ``` These settings are also options on the command: `datacontract api --contract-variables 'TABLE_*' --allow-local-files --allow-private-network`. The patterns are comma-separated fnmatch globs matched case-sensitively; a variable outside the list is indistinguishable from an unset one. `*` allows every variable, **including every credential in the server's environment**, so use it only where callers are trusted. ### Run as a Docker container The pre-built image can run the API in any container environment (Docker Compose, Kubernetes, Azure Container Apps, Google Cloud Run, …): ```yaml services: datacontract-api: image: datacontract/cli:latest ports: - "4242:4242" environment: - DATACONTRACT_CLI_API_KEY=a079ce4c-af90-45ab-abe5-a8d7697f60d6 command: ["api", "--host", "0.0.0.0"] ``` See the [`api` command reference](./commands/api.md). --- ## Python Library Everything the CLI does is also available as a Python library through the `DataContract` class. This is useful for embedding data contract checks in pipelines, notebooks, orchestrators (Airflow, Dagster, Prefect), or your own tooling. ```bash pip install 'datacontract-cli[all]' ``` ### Test a data contract ```python from datacontract.data_contract import DataContract data_contract = DataContract(data_contract_file="datacontract.yaml") run = data_contract.test() if not run.has_passed(): print("Data quality validation failed.") # Abort the pipeline, alert, or take corrective action... ``` #### Inspecting the result `test()` (and `lint()`) return a `Run` object: ```python run = data_contract.test() print(run.result) # "passed", "failed", "warning", or "error" print(run.has_passed()) # True / False for check in run.checks: print(check.result, check.name, check.reason) ``` ### Constructor options The `DataContract` constructor accepts the contract from a file, a string, or an in-memory ODCS object, plus the same options as the CLI: ```python from datacontract.data_contract import DataContract DataContract( data_contract_file="datacontract.yaml", # or data_contract_str=... / data_contract= server="production", # which server to test (default: all) schema_name="orders", # which schema to test (default: "all") check_categories={"schema", "quality"}, # subset of: schema, quality, servicelevel, custom publish_url="https://api.entropy-data.com/api/test-results", inline_references=True, include_failed_samples=False, ) ``` | Argument | Description | |---|---| | `data_contract_file` | Path, URL, or S3 URL (`s3://bucket/key`) to the contract. | | `data_contract_str` | The contract as a YAML string. | | `data_contract` | An in-memory `OpenDataContractStandard` object. | | `server` | Server to test against (the key in `servers`). | | `schema_name` | Which schema/model to test (default `"all"`). | | `check_categories` | Set of categories to run: `schema`, `quality`, `servicelevel`, `custom`. | | `spark` | A `SparkSession`, for the `dataframe` / Databricks engines. | | `duckdb_connection` | An existing DuckDB connection. | | `publish_url` | URL to publish test results to. | | `inline_references` | Resolve external references (default `True`). | | `include_failed_samples` | Collect a sample of failing rows (default `False`). | | `config` | Credentials and connection options, as a `Config` object or dict (see [Credentials](#credentials)). | ### Lint a data contract ```python from datacontract.data_contract import DataContract run = DataContract(data_contract_file="datacontract.yaml").lint() assert run.has_passed() ``` ### Export `export()` returns the converted artifact as a string (or bytes for binary formats such as Excel). Pass the target format and, optionally, a schema and format-specific keyword arguments. ```python from datacontract.data_contract import DataContract data_contract = DataContract(data_contract_file="datacontract.yaml", server="snowflake") sql = data_contract.export("sql") print(sql) # Format-specific options are passed as keyword arguments html = data_contract.export("html") with open("datacontract.html", "w") as f: f.write(html) ``` See [Exports](./exports/index.md) for the full list of formats. ### Import `DataContract.import_from_source()` is a class method that returns an ODCS (`OpenDataContractStandard`) object. Format-specific options are passed as keyword arguments. ```python from datacontract.data_contract import DataContract odcs = DataContract.import_from_source( format="sql", source="my_ddl.sql", dialect="postgres", ) # Wrap it to export or test data_contract = DataContract(data_contract=odcs) print(data_contract.export("odcs")) ``` See [Imports](./imports/index.md) for the full list of formats. ### Compare two contracts `changelog()` lists what changed between two versions: ```python from datacontract.data_contract import DataContract v1 = DataContract(data_contract_file="v1.odcs.yaml") v2 = DataContract(data_contract_file="v2.odcs.yaml") result = v1.changelog(v2) print(result) ``` `breaking()` grades those same changes for compatibility impact. Every entry carries a `direction` (`tightens`, `loosens`, `both`, `undetermined` or `untested`) and a `level` (`info`, `warning` or `error`), and `is_breaking` is `True` when any entry is an error. ```python result = v1.breaking(v2, perspective="consumer") for entry in result.entries: print(entry.level.value, entry.path, entry.message) ``` See [Compare contract versions](./compare-contract-versions.md) for the perspectives and severity levels. ### Spark DataFrames and Databricks Pass a `SparkSession` to test in-memory DataFrames (registered as temporary views) or to run inside a Databricks notebook: ```python from datacontract.data_contract import DataContract df.createOrReplaceTempView("my_table") data_contract = DataContract( data_contract_file="datacontract.yaml", spark=spark, ) run = data_contract.test() assert run.result == "passed" ``` See [Spark DataFrame](./testing/dataframe.md) and [Databricks](./testing/databricks.md) for details. ### Credentials Server credentials are read from environment variables (or a `.env` file), exactly as with the CLI — see [Configuration](./configuration.md) for all mechanisms and their precedence. They can also be passed programmatically via the `config` argument, without touching the process environment. The typed `Config` class declares every supported option; field names match the environment variable names (`snowflake_username` ↔ `DATACONTRACT_SNOWFLAKE_USERNAME`), unset fields fall back to the environment, and secrets are held as `SecretStr` so they stay out of logs and reprs: ```python from datacontract import Config from datacontract.data_contract import DataContract run = DataContract( data_contract_file="datacontract.yaml", server="production", config=Config( snowflake_username="svc_test", snowflake_password=get_secret("snowflake"), snowflake_role="TESTER", ), ).test() ``` A plain dict keyed by the environment variable names is accepted as well: `config={"DATACONTRACT_SNOWFLAKE_PASSWORD": "..."}`. `DataContract.import_from_source()` takes the same `config` argument. The config object is passed explicitly through the connection layer, so concurrent tests with different credentials in one process do not interfere. A YAML config file works for both the CLI (`--config-file`, defaulting to `./datacontract-config.yaml` or `~/.datacontract/config.yaml`) and the library (`Config.from_yaml(path)`). Sections map to option names, and `${VAR}` references resolve from the environment at load time, so the file can be committed without holding secrets: ```yaml # datacontract-config.yaml snowflake: username: svc_test password: ${SNOWFLAKE_PASSWORD} role: TESTER max_errors: 10 ``` --- ## Databricks Notebooks and Jobs ## ![](/img/icons/databricks.svg) Databricks Notebooks and Jobs Running the CLI **inside** Databricks is different from running it against Databricks from your laptop or CI. The cluster already provides Spark, so the contract is tested through the session you already have — there is no warehouse to point at and nothing to authenticate. If you are connecting to Databricks *from outside*, you want the [Databricks connection guide](./testing/databricks.md) instead. ### Install the `databricks` extra ```bash pip install 'datacontract-cli[databricks]' ``` The extra installs no PySpark of its own, so it cannot shadow the build your cluster ships. It contains the Databricks SQL connector and SDK as well, so the SQL-warehouse path keeps working if you use it from the same notebook. `databricks-runtime` still resolves, as an alias of `databricks`. It used to be the way to get the same set without PySpark; nothing needs the distinction now. :::tip[Install it as a cluster library, not with `%pip`] On the Databricks LTS ML runtimes (15.4, 16.4), `%pip install` inside a notebook can fail or leave the environment inconsistent. Add `datacontract-cli[databricks]` as a **PyPI library** on the cluster instead — Compute → your cluster → Libraries → Install new → PyPI — then restart the cluster. ::: ### Test a table in a notebook Pass the notebook's existing `spark` session. The contract's server needs no credentials — the session is already authenticated as you (or as the job's service principal). ```python from datacontract.data_contract import DataContract run = DataContract( data_contract_file="/Volumes/acme_catalog_prod/orders_latest/datacontract/datacontract.yaml", spark=spark, ).test() run.result ``` The contract's `servers` block selects the catalog and schema; both are required for a `databricks` server: ```yaml servers: - server: production type: databricks catalog: acme_catalog_prod schema: orders_latest ``` When a Spark session is passed, the CLI uses **that session** — it runs `USE .` on it and reads the tables named by the contract's schema objects. It never opens a SQL-warehouse connection, so `DATACONTRACT_DATABRICKS_HTTP_PATH` and `DATACONTRACT_DATABRICKS_TOKEN` are not needed. Leave `spark` out and the same contract connects through the SQL connector instead, which is what the [Databricks connection guide](./testing/databricks.md) describes. ### Test a DataFrame that is not a table yet To check data mid-pipeline, before it is written anywhere, register the DataFrame as a temporary view named after the contract's schema object. The same `type: databricks` server works — temporary views live in the session, so they resolve like any other table: ```python orders_df.createOrReplaceTempView("orders") run = DataContract(data_contract_file="orders.odcs.yaml", spark=spark).test() ``` Keep `type: databricks` when the contract also describes real Unity Catalog tables. Reach for [`type: dataframe`](./testing/dataframe.md) only when the contract exists purely to validate in-memory DataFrames and has no catalog or schema to name — it is not an ODCS server type, so the CLI writes it as `type: custom` with a `customType` property. ### Fail a job when the contract is violated `test()` returns the run rather than raising, so a job task decides for itself what a failure means: ```python run = DataContract(data_contract_file="orders.odcs.yaml", spark=spark).test() if not run.has_passed(): raise RuntimeError(f"Data contract violated: {run.result}") ``` Raising marks the task failed, which is what you want for a quality gate between two steps of a Databricks Workflow. To record results instead of stopping the pipeline, inspect `run.checks` and write them somewhere, or [publish them](./entropy-data.md). For scheduling this outside Databricks, see [Scheduling](./scheduling/index.md). ### Learn more - **[Databricks](./testing/databricks.md)** — connecting from outside, importing from Unity Catalog, troubleshooting - **[Databricks Reference](./reference/databricks.md)** — every authentication option and the data type mappings - **[Python Library](./python-library.md)** — the full `DataContract` API --- ## Adopting Data Contracts There are two proven ways to introduce data contracts with the Data Contract CLI. Pick the one that matches your situation. ### Data-first approach Create a data contract based on the **actual data**. This is the fastest way to get started and to get feedback from data consumers. 1. Use an existing physical schema (e.g. SQL DDL) as a starting point to define your logical data model in the contract. Right after the import, fill in the connection details in the generated `servers` block and double-check that the actual data meets the imported model: ```bash datacontract import sql --source ddl.sql --dialect postgres --output datacontract.yaml datacontract test ``` 2. Add [quality checks](./quality-rules/index.md) and additional type constraints one by one, making sure the data still adheres to the contract: ```bash datacontract test ``` 3. Validate that the `datacontract.yaml` is correctly formatted and adheres to the [Open Data Contract Standard](./open-data-contract-standard.md): ```bash datacontract lint ``` 4. Set up a CI pipeline that runs daily for continuous quality checks. Use the [`ci`](./commands/ci.md) command for CI-optimized output (GitHub Actions annotations and step summary, Azure DevOps annotations). You can also report results to tools like [Entropy Data](https://entropy-data.com): ```bash datacontract ci --publish https://api.entropy-data.com/api/test-results ``` See [Scheduling](./scheduling/index.md). ### Contract-first approach Create a data contract based on the **requirements** from use cases, before the data product exists. 1. Start with a `datacontract.yaml` template: ```bash datacontract init ``` 2. Create the model and quality guarantees based on your business requirements. Fill in the terms, descriptions, etc., then validate the format: ```bash datacontract lint ``` 3. Use the [export](./exports/index.md) functions to start building the providing data product as well as the integration into consuming data products: ```bash # data provider datacontract export dbt-models # data consumer datacontract export dbt-sources datacontract export dbt-staging-sql ``` 4. Test that your data product implementation adheres to the contract: ```bash datacontract test ``` --- ## Extending the CLI The CLI uses factory patterns for export and import, so you can register your own **custom exporter** (a new output format) or **custom importer** (a new input format) and use them through the same `DataContract` API. :::note For producing a custom text output from a Jinja template, you usually don't need code — use the built-in [`export custom`](./exports/custom.md) exporter. Register a custom exporter class (below) when you need full programmatic control. ::: ### Custom exporter Implement `Exporter.export(...)` and register the class with the exporter factory. The `data_contract` argument is an `OpenDataContractStandard` (ODCS) instance, so you read `data_contract.name`, `data_contract.version`, `data_contract.schema_`, etc. ```python from datacontract.data_contract import DataContract from datacontract.export.exporter import Exporter from datacontract.export.exporter_factory import exporter_factory class MyExporter(Exporter): def export(self, data_contract, schema_name, server, sql_server_type, export_args) -> str: lines = [f"# {data_contract.name} v{data_contract.version}"] for schema in data_contract.schema_ or []: columns = ", ".join(p.name for p in (schema.properties or [])) lines.append(f"{schema.name}: {columns}") return "\n".join(lines) # Register the exporter under a new format name exporter_factory.register_exporter("my_format", MyExporter) # Use it like any built-in format data_contract = DataContract(data_contract_file="datacontract.yaml") print(data_contract.export("my_format")) ``` The `export()` method receives: | Argument | Description | |---|---| | `data_contract` | The contract as an `OpenDataContractStandard` object. | | `schema_name` | The selected schema, or `"all"`. | | `server` | The selected server name (or `None`). | | `sql_server_type` | The SQL dialect (`"auto"` by default). | | `export_args` | A dict of any extra keyword arguments passed to `export(...)`. | ### Custom importer Implement `Importer.import_source(...)` to build and return an `OpenDataContractStandard`, then register it with the importer factory. ```python import json from open_data_contract_standard.model import ( OpenDataContractStandard, SchemaObject, SchemaProperty, ) from datacontract.data_contract import DataContract from datacontract.imports.importer import Importer from datacontract.imports.importer_factory import importer_factory class MyImporter(Importer): def import_source(self, source, import_args) -> OpenDataContractStandard: data = json.loads(source) return OpenDataContractStandard( apiVersion="v3.0.2", kind="DataContract", id=data["id"], name=data["title"], version=data["version"], description={"purpose": data.get("description")}, schema=[ SchemaObject( name=model["name"], properties=[ SchemaProperty(name=col["name"], logicalType=col.get("type")) for col in model["columns"] ], ) for model in data.get("models", []) ], ) # Register the importer under a new format name importer_factory.register_importer("my_format", MyImporter) source = '{"id": "urn:my:contract", "title": "My Contract", "version": "1.0.0", "models": []}' odcs = DataContract.import_from_source(format="my_format", source=source) # import_from_source returns an ODCS object; wrap it to export or test print(DataContract(data_contract=odcs).export("odcs")) ``` ### Tips - Register classes once at import time (e.g. in a small plugin module you import before using the CLI as a library). - Custom formats are only available through the [Python library](./python-library.md), not the standalone `datacontract` command. - See the built-in exporters/importers in the [source tree](https://github.com/datacontract/datacontract-cli/tree/main/datacontract) for complete, real-world implementations. --- ## Migrate from DCS to ODCS Contracts that start with `dataContractSpecification:` and describe their structure under `models:`/`fields:` use the **Data Contract Specification (DCS)**, the format the CLI was originally built around. The CLI now uses the **[Open Data Contract Standard (ODCS)](./open-data-contract-standard.md)** natively. :::note[Your DCS contracts keep working] `lint`, `test`, and `export` detect a DCS file and convert it in memory before doing their work. Migrating just makes the ODCS version the file you keep in Git. ::: ### Convert ```bash datacontract export odcs datacontract.yaml --output datacontract.odcs.yaml ``` For a whole repository of contracts: ```bash find . -name 'datacontract.yaml' -exec sh -c \ 'datacontract export odcs "$1" --output "$(dirname "$1")/datacontract.odcs.yaml"' _ {} \; ``` ### Verify ```bash # 1. The new file is valid ODCS. datacontract lint datacontract.odcs.yaml # 2. Nothing changed semantically — both files are compared as ODCS, # so an empty table means the conversion was lossless. datacontract changelog datacontract.yaml datacontract.odcs.yaml # 3. The new file still passes against real data. datacontract test datacontract.odcs.yaml ``` Once all three pass, delete the DCS file and point your [CI/CD pipeline](./scheduling/index.md) at the new one. ### Three things to check by hand - **`status` defaults to `draft`** when the DCS file has no `info.status`. Change it to `active` (or another value) once the contract is ready for production. - **The deprecated `primary: true` is not carried over** — only `primaryKey: true` is. Search your contracts for `primary:` and set `primaryKey: true` in the ODCS file. - **`physicalType` holds the DCS `type`**, so a column reads `physicalType: string` instead of its native type. Replace it with the real type (`VARCHAR`, `NUMBER(12,2)`, …) or drop it and keep only `logicalType`. Everything else is mapped automatically: `models` → `schema`, `fields` → `properties`, `servicelevels` → `slaProperties`, `terms` → `description`, `field.enum` → a [quality rule](./quality-rules/index.md), `$ref` definitions inlined. Anything without an ODCS equivalent (contact details, Kafka `topic`, `pii`, `precision`) is preserved under `customProperties`. ### Going back [`datacontract export dcs`](./exports/dcs.md) converts the other way, if a downstream tool still expects DCS: ```bash datacontract export dcs datacontract.odcs.yaml --output datacontract.yaml ``` --- ## Comparison with Other Tools Data quality and data contract tooling changed a lot in 2026: Great Expectations and Soda both changed hands or changed licenses, and dbt Labs merged into Fivetran. This page compares the Data Contract CLI with the three tools it is most often evaluated against — **[Great Expectations (GX Core)](https://greatexpectations.io/)**, **[Soda Core](https://www.soda.io/)**, and **[dbt contracts](https://docs.getdbt.com/docs/collaborate/govern/model-contracts)** — so you can pick the right one, or combine them. All facts below were verified in July 2026, with sources linked at the bottom of the page. ### At a glance | | Data Contract CLI | GX Core | Soda Core | dbt contracts | |---|---|---|---|---| | **License** | MIT | Apache 2.0 | Elastic License 2.0 (source-available) since v4 | Apache 2.0 (dbt Core) | | **Maintained by** | Entropy Data and the community | Fivetran (steward since May 2026) | Soda (vendor) | dbt Labs, part of Fivetran since June 2026 | | **Contract format** | [ODCS](./open-data-contract-standard.md), an open standard governed by [Bitol](https://bitol.io/) under the Linux Foundation AI & Data | Expectation Suites (tool-specific JSON/Python) | Soda contract YAML (tool-specific) | dbt model YAML inside a dbt project | | **Format is vendor-neutral** | Yes | No | No | No | | **What the contract covers** | Schema, semantics, quality rules, servers, service levels, ownership | Quality expectations | Schema and quality checks | Column names, data types, constraints | | **When checks run** | On demand, in CI/CD, or scheduled — against data that already exists | After the data is written | After the data is written | At build time (preflight check plus DDL constraints) | | **Where it runs** | Standalone CLI, Python library, Docker, GitHub Action, REST API | Python library | CLI and Python library | Only inside a dbt run | | **Data sources** | [18+](./testing/index.md), including Snowflake, Databricks, BigQuery, Kafka, S3, and local files | SQL databases, Spark, pandas | Postgres, Snowflake, BigQuery, Databricks, Redshift, SQL Server, Fabric, Synapse, Athena, DuckDB | Whatever your dbt adapter supports | | **Contract is shareable with consumers** | Yes — one portable YAML file | No | Within Soda | No — scoped to one dbt project | ### Data Contract CLI The CLI is MIT-licensed and built natively on the **Open Data Contract Standard**. The contract is a single YAML file that describes structure, semantics, quality rules, servers, and service levels — an artifact you can put in Git, review in a pull request, hand to a consumer in another team, and [export](./exports/index.md) to 25+ downstream formats. Because the format is an open standard governed by Bitol under the Linux Foundation AI & Data, it does not belong to any vendor — including us. That is the main structural difference to the other three: with GX, Soda, and dbt, the definition lives in a tool-specific format, so the definition and the tool move together. **Use it when** you want one portable contract that both documents the data for its consumers and is executable against 18+ platforms, and when license and governance risk matter to you. ### ![](/img/icons/great-expectations.svg) Great Expectations (GX Core) GX Core is an established Python data quality framework, Apache 2.0-licensed, with a large expectation library. Its ownership changed in 2026: **GX Cloud was acquired by FICO** and stopped being publicly available on June 1, 2026, while **Fivetran became the steward** of the GX Core project and the open-source community. GX describes expectations about data, not contracts. There is no portable, vendor-neutral artifact you can hand to a consuming team, and no notion of servers, service levels, or ownership. **Use it when** you need its breadth of expectations or already have suites in production. You can keep using it from a data contract: [`datacontract export --format great-expectations`](./exports/great-expectations.md) turns a contract into a GX suite. ### ![](/img/icons/soda.svg) Soda Core Soda Core changed its license with **v4 (January 2026), from Apache 2.0 to the Elastic License 2.0**. It stays source-available and free for internal use, including production, but you may no longer offer it to third parties as a hosted or managed service. Soda Core v4 also replaced the SodaCL checks language with **its own data contract format**, a breaking change with a migration tool. So Soda now has a data contract format too, but it is Soda's format for Soda's engine, under a source-available license — not an open standard. **Use it when** you are on the Soda platform and the ELv2 terms fit your use case. The CLI's [SodaCL export](./exports/sodacl.md) targets the checks language of Soda Core v3. ### ![](/img/icons/dbt.svg) dbt contracts dbt model contracts (`contract: enforced: true`) are the strongest option for **build-time** enforcement: dbt verifies before building that the model's SQL returns the declared columns, and it puts data types and constraints into the DDL, so a violating build fails instead of shipping bad data. dbt Core stays Apache 2.0-licensed under Fivetran, which completed its merger with dbt Labs on June 1, 2026. The limits are scope, not quality. A dbt contract lives in a dbt project and applies to models built by dbt: it does not describe a Kafka topic, an S3 bucket, or a table someone else's pipeline writes, it carries no servers, SLAs, or ownership, and a consumer outside your project cannot use it as an agreement. Contracts also only cover supported materializations, and they check structure — content is still the job of dbt tests. **Use it when** producers and consumers live in the same dbt project and build-time failure is what you need. ### They are not mutually exclusive A data contract is the definition; these tools are execution engines. The CLI can drive the others from one ODCS contract: - [`datacontract dbt sync`](./dbt.md) merges the contract's schema and tests into an existing dbt project, giving you build-time enforcement from the same contract. - [`datacontract export --format great-expectations`](./exports/great-expectations.md) generates a GX suite. - [`datacontract export --format sodacl`](./exports/sodacl.md) generates SodaCL checks. - [`datacontract export --format dbt`](./exports/dbt-models.md) generates a dbt model schema, [sources](./exports/dbt-sources.md), or [staging SQL](./exports/dbt-staging-sql.md). A common setup is: define the contract in ODCS, enforce structure at build time with dbt, and run [`datacontract test`](./testing/index.md) in CI/CD and on a schedule against the deployed data. ### Sources - [An Update for the Great Expectations Community](https://greatexpectations.io/blog/an-update-from-great-expectations/) (May 6, 2026) and [Fivetran to Become Steward of the Great Expectations Open Source Community and GX Core Project](https://www.fivetran.com/press/fivetran-to-become-steward-of-the-great-expectations-open-source-community-and-gx-core-project) - [Soda Core License Update: Moving to Elastic License 2.0](https://soda.io/blog/soda-core-license-update-moving-to-elastic-license) and [Introducing Soda 4.0](https://soda.io/blog/introducing-soda-4.0) - [dbt model contracts](https://docs.getdbt.com/docs/collaborate/govern/model-contracts) and [Fivetran + dbt Labs Complete Merger](https://www.fivetran.com/press/fivetran-dbt-labs-complete-merger-to-create-the-data-infrastructure-for-trusted-ai-agents) (June 1, 2026) --- ## Integrate with Entropy Data [Entropy Data](https://entropy-data.com/) is a commercial platform to manage data contracts. It provides a web UI, access management, and data governance for a data product marketplace based on data contracts. The Data Contract CLI integrates with Entropy Data through the `--publish` option: it runs the tests and pushes the **full results** to the [Entropy Data API](https://api.entropy-data.com/swagger/index.html), where they are displayed and tracked over time. ### Publish test results Reference the contract by URL, run the tests against a server, and append `--publish`. Provide your API key as an environment variable. ```bash export ENTROPY_DATA_API_KEY=xxx datacontract test https://demo.entropy-data.com/demo279750347121/datacontracts/4df9d6ee-e55d-4088-9598-b635b2fdcbbc/datacontract.yaml \ --server production \ --publish https://api.entropy-data.com/api/test-results ``` The same `--publish` option is available on [`ci`](./commands/ci.md) and [`dbt sync`](./commands/dbt/sync.md), so you can report results from CI/CD and scheduled runs — see [Scheduling](./scheduling/index.md). ### Self-hosted deployments The API key is only ever sent to Entropy Data: to `entropy-data.com` and its subdomains, or to the host set in `ENTROPY_DATA_HOST`. A contract URL or `--publish` URL on any other host is contacted without the key, so that a third party serving a data contract never receives it. If you run Entropy Data on your own domain, set the host so that contract URLs and `--publish` are authenticated: ```bash export ENTROPY_DATA_HOST=https://entropy.internal.example ``` ### Publish the contract Use the [`publish`](./commands/publish.md) command to push a data contract itself to Entropy Data: ```bash datacontract publish datacontract.yaml ``` ### TLS behind a corporate proxy or internal CA By default the CLI verifies TLS certificates against the bundled CA certificates (`certifi`). In a corporate network with a TLS-inspecting proxy or an internal certificate authority, this can fail with `CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate`, because the root CA is installed in the operating system's trust store but not in the bundled list. Use the global `--system-truststore` option to verify against the operating system's trust store (macOS Keychain, Windows certificate store, or the system CA certificates on Linux) instead: ```bash datacontract --system-truststore publish datacontract.yaml ``` You can also enable it for every invocation with an environment variable: ```bash export DATACONTRACT_SYSTEM_TRUSTSTORE=1 ``` This keeps certificate verification on while trusting the corporate root CA. It works for all commands that make HTTPS requests, not only the Entropy Data integration. --- ## Release Notes Every released version of the Data Contract CLI, newest first. The CLI follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html); breaking changes are marked as such in the entry. - Install or upgrade with `uv tool install --upgrade datacontract-cli` or `pip install --upgrade datacontract-cli` — see [Installation](./installation.md). - Packages: [PyPI](https://pypi.org/project/datacontract-cli/#history) · [Docker Hub](https://hub.docker.com/r/datacontract/cli/tags) · [GitHub releases](https://github.com/datacontract/datacontract-cli/releases) ### Unreleased {#unreleased} #### Added - `datacontract test`: test Excel workbooks (`format: xlsx`) (new `xlsx` extra) ([#1717](https://github.com/datacontract/datacontract-cli/issues/1717) [@nicolehazel](https://github.com/nicolehazel)) - `datacontract edit --language` shows the editor in English, German, Swedish or French - `datacontract import avro` and `export avro` keep the `aliases` of fields and of the top-level record ([#1744](https://github.com/datacontract/datacontract-cli/issues/1744) [@lairdman7](https://github.com/lairdman7)) #### Changed - The bundled Data Contract Editor (`datacontract edit`) is updated to 0.1.16 - The documentation moved from docs.datacontract.com to cli.datacontract.com - `datacontract export excel`: the ODCS v3.2 template shows a tooltip with the field description on every field label - `datacontract lint` and `datacontract test`: reject duplicate keys in a data contract instead of silently keeping the last value ([#1741](https://github.com/datacontract/datacontract-cli/issues/1741) [@kamathramu87](https://github.com/kamathramu87)) - `datacontract export odcs`, `import odcs` and `import excel` write a `team` given as a list of members as a team object with `members` (this syntax is preferred by ODCS 3.2) - `datacontract test`: custom quality checks accept the files Entropy Data writes, with a `default` query and the argument type `any` (`ansi` and `value` still work) - `datacontract test`: a quality rule that can't use its custom quality check reports `error` instead of `warning` or `failed` #### Fixed - `datacontract dbt sync`: library metrics (`nullValues`, `missingValues`, `invalidValues`, `duplicateValues`) generate tests that check the metric against its bound - `datacontract export sql`: apostrophes and backslashes in descriptions no longer break ClickHouse and Snowflake `COMMENT` clauses ([#1729](https://github.com/datacontract/datacontract-cli/issues/1729),[#1731](https://github.com/datacontract/datacontract-cli/issues/1731) [@banshee](https://github.com/banshee) [@ezno12](https://github.com/ezno12)) - `datacontract changelog` and `datacontract breaking` recognize a quality rule that was named, renamed or moved, instead of reporting it as removed and added - `datacontract breaking` grades more changes to quality rules and to `minimum`/`maximum` bounds instead of warning - `datacontract breaking` no longer flags changes that `datacontract test` does not check - Contracts whose `team` is the deprecated list of members no longer fail to export, `import odcs --owner` or `dbt sync` ### 1.2.7 — 2026-10-16 {#v1-2-7} #### Changed - `datacontract breaking` consistently grades changes from the consumer's perspective by default (previously an inconsistent mix of consumer and producer perspective) ([#1746](https://github.com/datacontract/datacontract-cli/issues/1746)) #### Added - `datacontract test`: API servers send POST requests with query parameters and a JSON body, set by the `method`, `params` and `body` custom properties, and an `X-API-Key` header from `DATACONTRACT_API_HEADER_X_API_KEY` (also used by `datacontract import odata`) ([#1747](https://github.com/datacontract/datacontract-cli/issues/1747)) - `datacontract breaking --perspective consumer|producer` (and `perspective` on `POST /breaking`) selects whose expectations changes are graded against, and the output shows whether each change tightens or loosens the contract ([#1746](https://github.com/datacontract/datacontract-cli/issues/1746)) - `datacontract import openapi` imports POST operations, and writes required query parameters to the `params` custom property ([#1747](https://github.com/datacontract/datacontract-cli/issues/1747)) #### Security - `datacontract api`: a posted contract's `api` server may not reach private network addresses unless the server runs with `--allow-private-network`, and is refused while `DATACONTRACT_API_HEADER_AUTHORIZATION` or `DATACONTRACT_API_HEADER_X_API_KEY` is set on the server ([#1747](https://github.com/datacontract/datacontract-cli/issues/1747)) ### 1.2.6 — 2026-10-13 {#v1-2-6} #### Changed - A `null` in the `enum` of a required property is a validation error ([#1742](https://github.com/datacontract/datacontract-cli/issues/1742)) - `datacontract test`: - every nested property, array item and map key and value gets its own type check, with a nested `physicalType` checked on Athena, Trino, Databricks, BigQuery, Snowflake, ClickHouse and Hive ([#1719](https://github.com/datacontract/datacontract-cli/issues/1719)) - a property whose data cannot hold the structure declared inside it fails its own type check, and the checks inside it are reported as warnings ([#1719](https://github.com/datacontract/datacontract-cli/issues/1719)) - a property with `properties`, `items` or a `map` block but no type is checked as an object, array or map ([#1719](https://github.com/datacontract/datacontract-cli/issues/1719)) - `datacontract import glue`, `athena`, `unity`, `hive` and `clickhouse`: arrays, structs and maps get their base type as `physicalType`, their items, fields, keys and values keep their own ([#1719](https://github.com/datacontract/datacontract-cli/issues/1719)) #### Fixed - `datacontract test`: - a `null` among an `enum` or `validValues` no longer lets every other value pass ([#1742](https://github.com/datacontract/datacontract-cli/issues/1742)) - a declared `physicalType: json`, `jsonb` or `variant` matches a json column on servers whose native types are not read ([#1719](https://github.com/datacontract/datacontract-cli/issues/1719)) - rules on the key or value of a map are reported as warnings instead of being ignored ([#1719](https://github.com/datacontract/datacontract-cli/issues/1719)) - `datacontract export jsonschema` and `datacontract test` on JSON files allow `null` for an optional property with an `enum` ([#1742](https://github.com/datacontract/datacontract-cli/issues/1742)) ### 1.2.5 — 2026-10-12 {#v1-2-5} #### Changed - `datacontract lint --all-errors`: unknown fields are warnings instead of errors, unless a custom `--schema` rejects them #### Added - `datacontract import postgres` imports primary and foreign keys from the Postgres catalog, also for SELECT-only roles ([#1709](https://github.com/datacontract/datacontract-cli/issues/1709) [@vtulus](https://github.com/vtulus)) - `datacontract edit`: enable the editor's AI assistant via `DATACONTRACT_EDITOR_AI_*` environment variables (endpoint, API key, model, provider, auth header) - `datacontract test`: check constraints and quality rules on nested properties on Athena and Trino servers - `DATACONTRACT_CONNECT_TIMEOUT` (default 30 seconds): `datacontract test` on Postgres, Redshift, MySQL and SQL Server fails with a connection error instead of waiting forever for a server that does not answer, e.g. on a wrong port #### Fixed - `datacontract lint` and `datacontract test`: warn about unknown fields, such as a misspelled `quality`, instead of silently ignoring them (will be an error in the next major version in January 2027) - `datacontract import sqlserver` and the physical type checks of `datacontract test` work on case-sensitive SQL Server databases such as Microsoft Fabric warehouses ([#1705](https://github.com/datacontract/datacontract-cli/issues/1705) [@Octacon100](https://github.com/Octacon100)) - `datacontract import`: Postgres and DuckDB `uuid` columns import as `string` with format `uuid` instead of without a logicalType - `datacontract dbt sync`: generated composite primary key, length, pattern and range tests work on Databricks, BigQuery and Snowflake ([#1713](https://github.com/datacontract/datacontract-cli/issues/1713)) - `datacontract api`: the endpoints run in a thread pool, so a slow or hanging `POST /test` no longer blocks every other request to the server ### 1.2.4 — 2026-10-06 {#v1-2-4} #### Added - `datacontract test`: custom quality checks, SQL checks defined once in a folder (`--custom-quality-checks`) and used by name from a contract's quality rules ([#1703](https://github.com/datacontract/datacontract-cli/issues/1703)) - `datacontract test`: check that foreign keys declared under `relationships` have no values missing from the referenced schema ([#1691](https://github.com/datacontract/datacontract-cli/issues/1691),[#1698](https://github.com/datacontract/datacontract-cli/issues/1698) [@jarichb](https://github.com/jarichb)) #### Security - Redact secrets in the MySQL connection string of error messages ([#1706](https://github.com/datacontract/datacontract-cli/issues/1706),[#1708](https://github.com/datacontract/datacontract-cli/issues/1708) [@SabineGl](https://github.com/SabineGl)) - `--debug` tracebacks no longer print local variables, which could hold credentials ([#1708](https://github.com/datacontract/datacontract-cli/issues/1708)) #### Fixed - `datacontract test`: `mustBe` and `mustNotBe` holding a `${VAR}` reference to a number compare with the number, not its text ([#1703](https://github.com/datacontract/datacontract-cli/issues/1703)) - `datacontract test`: SQL quality queries on Athena are read as Trino SQL, so a backslash in a string no longer gets the query refused ([#1703](https://github.com/datacontract/datacontract-cli/issues/1703)) ### 1.2.3 — 2026-10-05 {#v1-2-3} #### Added - ClickHouse and Hive: - `datacontract test`: test ClickHouse and Hive servers (new `clickhouse` and `hive` extras) ([#1687](https://github.com/datacontract/datacontract-cli/issues/1687)) - `datacontract import clickhouse` and `datacontract import hive`: import a data contract from a live ClickHouse or Hive database ([#1687](https://github.com/datacontract/datacontract-cli/issues/1687)) - `datacontract import sql`: support the `clickhouse` and `hive` dialects ([#1687](https://github.com/datacontract/datacontract-cli/issues/1687)) - XML: - `datacontract import xsd`: import a data contract from an XML Schema (XSD) file (new `xml` extra) ([#1684](https://github.com/datacontract/datacontract-cli/issues/1684)) - `datacontract export xsd`: export a data contract to an XML Schema (XSD) ([#1684](https://github.com/datacontract/datacontract-cli/issues/1684)) - `datacontract test`: test XML files (`format: xml`) ([#1684](https://github.com/datacontract/datacontract-cli/issues/1684)) - `datacontract import openapi`: import a data contract from the response of a GET operation in an OpenAPI 3.x document - `datacontract test`: test YAML responses of API servers - `datacontract export pydantic-model`: export `logicalTypeOptions` as `pydantic.Field` constraints ([#1678](https://github.com/datacontract/datacontract-cli/issues/1678),[#1679](https://github.com/datacontract/datacontract-cli/issues/1679) [@amassoudi](https://github.com/amassoudi)) - `datacontract export dbt-models` and `dbt-sources` write a property's `meta` custom property to the column's `config.meta` ([#1655](https://github.com/datacontract/datacontract-cli/issues/1655),[#1661](https://github.com/datacontract/datacontract-cli/issues/1661) [@lguyaux](https://github.com/lguyaux)) #### Changed - `datacontract export dbt-models`, `datacontract export dbt-sources` and `datacontract dbt sync` nest generic-test parameters under `arguments:` **(we now require dbt 1.10+)** ([#1680](https://github.com/datacontract/datacontract-cli/issues/1680),[#1682](https://github.com/datacontract/datacontract-cli/issues/1682) [@opencode13241-eng](https://github.com/opencode13241-eng) [@benvdh](https://github.com/benvdh)) - `datacontract export pydantic-model`: optional fields default to `None` ([#1678](https://github.com/datacontract/datacontract-cli/issues/1678),[#1679](https://github.com/datacontract/datacontract-cli/issues/1679) [@amassoudi](https://github.com/amassoudi)) - `datacontract test`: - an optional field that no record of a JSON, YAML or XML document has is no longer reported as missing - warns when the column types cannot be read from the catalog, as the physical type checks then compare only the logicalType ([#1688](https://github.com/datacontract/datacontract-cli/issues/1688)) - `glue`, `kinesis` and `sftp` servers report that they are valid ODCS but cannot be tested yet, like the other untestable server types ([#1688](https://github.com/datacontract/datacontract-cli/issues/1688)) #### Fixed - `datacontract export dbt-models` and `dbt-sources` write column `meta` and `tags` under `config`, so dbt Fusion accepts the output ([#1661](https://github.com/datacontract/datacontract-cli/issues/1661)) - `datacontract test`: - reports "no checks were executed" instead of failing when nothing was tested ([#1504](https://github.com/datacontract/datacontract-cli/issues/1504),[#1675](https://github.com/datacontract/datacontract-cli/issues/1675)) - a JSON record that breaks the JSON Schema no longer stops the other checks, and every such record is reported - JSON schema validation on S3 checks every file matched by the location, not only the last one ([#1511](https://github.com/datacontract/datacontract-cli/issues/1511),[#1673](https://github.com/datacontract/datacontract-cli/issues/1673) [@AdamoElProfesor](https://github.com/AdamoElProfesor)) - JSON schema validation on S3 uses the AWS credential chain ([#1683](https://github.com/datacontract/datacontract-cli/issues/1683)) - a JSON value of the wrong type fails the JSON Schema check, not every check on its column - the JSON Schema check reads `.jsonl` and `.ndjson` files, and files without a `delimiter` the way DuckDB does - constraints on the items of an array of plain values (`pattern`, `enum`, `minimum`, …) are checked - required fields of an absent optional object are no longer reported as missing ([#1684](https://github.com/datacontract/datacontract-cli/issues/1684)) - JSON files that are not found are reported instead of failing with a TypeError - a duplicate check on a composite primary key no longer breaks the JSON output and `run.pretty()` ([#1687](https://github.com/datacontract/datacontract-cli/issues/1687)) - a `DATACONTRACT_MAX_ERRORS` below 1 is reported as a configuration error ([#1673](https://github.com/datacontract/datacontract-cli/issues/1673) [@AdamoElProfesor](https://github.com/AdamoElProfesor)) - `datacontract import jsonschema`: - resolves local `$ref`s and merges `allOf`, which imported as strings or not at all - imports `const`, `additionalProperties` as a map, `minItems`, `maxItems`, `uniqueItems`, `multipleOf` and `examples`, and warns about the keywords it leaves out - boolean schemas (`true`, `false`) no longer fail the import - `datacontract import dbt` keeps column `meta` instead of silently dropping it: `classification` maps to ODCS `classification`, the other entries to a `meta` custom property ([#1655](https://github.com/datacontract/datacontract-cli/issues/1655),[#1661](https://github.com/datacontract/datacontract-cli/issues/1661) [@lguyaux](https://github.com/lguyaux)) - `datacontract import sql`: the fields of a `STRUCT` column are no longer imported as columns of the table ([#1688](https://github.com/datacontract/datacontract-cli/issues/1688)) - `datacontract export html`: add schema and property anchors (`#.`) and nested objects in array `items` back ([#1676](https://github.com/datacontract/datacontract-cli/issues/1676),[#1677](https://github.com/datacontract/datacontract-cli/issues/1677) [@dmaresma](https://github.com/dmaresma)) - `datacontract api`: authoritative definitions are looked up with the Entropy Data API key sent with the request again (since v1.2.1, the API was unable to resolve authoritative definitions if ENTROPY_DATA_API_KEY was unset) ([#1681](https://github.com/datacontract/datacontract-cli/issues/1681)) - `datacontract api` caches authoritative definitions for only 60s, previously until the next restart ([#1681](https://github.com/datacontract/datacontract-cli/issues/1681)) - `datacontract export sodacl`: date and timestamp `minimum`/`maximum` no longer fail in soda-core ([#1662](https://github.com/datacontract/datacontract-cli/issues/1662),[#1665](https://github.com/datacontract/datacontract-cli/issues/1665) [@AdamoElProfesor](https://github.com/AdamoElProfesor) [@parvin-97](https://github.com/parvin-97)) - `datacontract test` and `datacontract import s3`: public S3 buckets are read at the server's `endpointUrl` ([#1683](https://github.com/datacontract/datacontract-cli/issues/1683)) ### 1.2.2 — 2026-09-25 {#v1-2-2} #### Added - `datacontract api`: `--contract-variables` and `--allow-local-files` options, as alternatives to their environment variables - `datacontract import odata` creates a datacontract from OData 4 metadata at a URL or from a local file ([#1649](https://github.com/datacontract/datacontract-cli/issues/1649) [@jahlen](https://github.com/jahlen)) - `datacontract export` writes SQL Server `VECTOR(n)` and `VECTOR(n, float16)` vector types - `datacontract test` checks constraints and quality rules of nested properties on servers read through DuckDB ([#1278](https://github.com/datacontract/datacontract-cli/issues/1278)) #### Changed - `datacontract lint` and `datacontract test`: a quality rule the CLI cannot run is reported as a warning instead of being silently dropped - The bundled Data Contract Editor (`datacontract edit`) is updated to 0.1.14 - `${VAR}` references are accepted in fields with a fixed set of values (`quality.type`, `quality.metric`, `quality.dimension`, `logicalType`, `servers[].type`); `datacontract test` fails when one resolves into anything else - `datacontract test` reports nested checks it cannot run and unsupported type checks on parquet files as warnings ([#1278](https://github.com/datacontract/datacontract-cli/issues/1278)) #### Security - `datacontract api`: - a posted data contract resolves `${VAR}` only from the variables allow-listed with `--contract-variables` - a `${X:-local}` server type no longer slips past the checks for local files and environment-held credentials - `datacontract test`: a SQL quality rule on a `mysql` server can no longer reach the MySQL server or its credentials; rules on `mysql` and `iceberg` servers are read as DuckDB SQL #### Fixed - Loading a DCS contract dropped quality rule `arguments`, so `invalidValues` and `missingValues` rules lost their configuration - `datacontract test --dry-run`: a check that could not be planned no longer reports the run as `skipped` - Non-ASCII characters in data contract and import source files are now decoded as UTF-8, fixing garbling on Windows ([#1650](https://github.com/datacontract/datacontract-cli/issues/1650) [@ymurong](https://github.com/ymurong)) - `datacontract test`: SQL quality rule placeholders are quoted when the name needs it (e.g. a column with a space), and a query that does not parse reports the parse error ([#1653](https://github.com/datacontract/datacontract-cli/issues/1653)) - `datacontract import spark` preserves `varchar(...)` and `char(...)` types nested inside `struct`, `array` and `map` columns instead of widening them to `string` ([#1634](https://github.com/datacontract/datacontract-cli/issues/1634) [@IchEssBlumen](https://github.com/IchEssBlumen)) - `datacontract import jsonschema` keeps the type of nullable `anyOf`/`oneOf` properties and warns about union types ([#1278](https://github.com/datacontract/datacontract-cli/issues/1278)) - `datacontract export sodacl` warns about the nested properties it leaves out ([#1278](https://github.com/datacontract/datacontract-cli/issues/1278)) ### 1.2.1 — 2026-09-22 {#v1-2-1} #### Highlights - Support for SAP HANA and Exasol #### Added - `datacontract test`: - supports SAP HANA Cloud and SAP Datasphere through the optional `hana` extra ([#1332](https://github.com/datacontract/datacontract-cli/issues/1332) [@ToniLippmann](https://github.com/ToniLippmann)) - supports Exasol through the `exasol` extra ([#1335](https://github.com/datacontract/datacontract-cli/issues/1335)) - on Databricks and Spark checks nested array and struct properties ([#1278](https://github.com/datacontract/datacontract-cli/issues/1278) [@rob-h-w](https://github.com/rob-h-w)) - `datacontract export excel` and `datacontract import excel` support all versions of the Excel template (ODCS v3.0.2, v3.1.0, v3.2.0) - `datacontract import sql` detects table-level PRIMARY KEY and FOREIGN KEY constraints, derives `unique: true` for single-column keys, and emits property-level `relationships` in the ODCS shorthand format ([#1618](https://github.com/datacontract/datacontract-cli/issues/1618) [@dmaresma](https://github.com/dmaresma)) - `datacontract export great-expectations --checks` restricts the suite to `quality` and/or `properties` expectations ([#1617](https://github.com/datacontract/datacontract-cli/issues/1617) [@julienguilhempartner-spec](https://github.com/julienguilhempartner-spec)) - `datacontract import` warns once per import about column types that have no mapping to a `logicalType` ([#1629](https://github.com/datacontract/datacontract-cli/issues/1629) [@regdat](https://github.com/regdat)) #### Changed - `datacontract lint` validates against the ODCS schema for the `apiVersion` the contract declares, instead of always the newest one - `datacontract-cli[csv]` installs DuckDB, which `datacontract import csv` needs, instead of pandas - Internal logging uses named module loggers instead of the root logger ([#1605](https://github.com/datacontract/datacontract-cli/issues/1605) [@sondrfos](https://github.com/sondrfos)) #### Security - `datacontract test` and `datacontract import sqlserver` escape the host, database and driver in the SQL Server connection string, so a `;` in the contract's `servers` block can no longer inject connection keywords - `datacontract api`: - resolves `authoritativeDefinitions` only against the Entropy Data host configured on the server, without following redirects, and answers a failed lookup on every endpoint with `422` and the URL, never with what the host answered - answers a non-ASCII `x-api-key` with `403` instead of `500` and warns on failed API key checks - `/test` no longer logs the submitted contract - `datacontract changelog` and `datacontract breaking` no longer interpret contract values as terminal markup #### Fixed - `datacontract test`: - `--filter` reports a duplicate key whose other occurrence lies outside the filtered rows ([#1593](https://github.com/datacontract/datacontract-cli/issues/1593),[#1619](https://github.com/datacontract/datacontract-cli/issues/1619) [@gkhnelbstn](https://github.com/gkhnelbstn)) - no longer counts `NULL` values as duplicates of each other in uniqueness checks - SQL Server / Microsoft Fabric `cli` auth works again on macOS/Linux, and `ActiveDirectoryInteractive` fails fast off Windows instead of timing out ([#1603](https://github.com/datacontract/datacontract-cli/issues/1603) [@johannzv](https://github.com/johannzv)) - reports a `freshness` service level it cannot interpret as a single failed check instead of aborting the whole run; freshness now also accepts an ISO-8601 duration as its value, like retention - `datacontract import`: - maps timestamp, time, decimal, JSON-like and small integer column types that previously came out as `string` or `date` to the right `logicalType`, including in `avro` and `spark` imports ([#1629](https://github.com/datacontract/datacontract-cli/issues/1629) [@regdat](https://github.com/regdat)) - `s3`, `gcs` and `adls` accept `--format` again, so Delta tables can be imported ([#1628](https://github.com/datacontract/datacontract-cli/issues/1628) [@regdat](https://github.com/regdat)) - `sql` warns about statements it cannot parse and therefore skips, such as a `CREATE TABLE` with unquoted hyphens in its name ([#686](https://github.com/datacontract/datacontract-cli/issues/686) [@dwelden](https://github.com/dwelden)) - `datacontract dbt sync` writes the generated-column marker under `config.meta` instead of a top-level `meta` so the model YAML parses under dbt Fusion; files written by an earlier version are migrated on the next sync ([#1633](https://github.com/datacontract/datacontract-cli/issues/1633) [@FredrikBakken](https://github.com/FredrikBakken)) - `datacontract dbt sync` removes a column it generated once the property leaves the contract, without `--prune`; a column the user added their own tests or settings to is kept - `datacontract changelog` and `datacontract breaking`: - report changes in list items that have no name, url or id, such as unnamed `quality` rules - report a renamed schema correctly - `breaking` recognizes changes to `quality` rules ### 1.2.0 — 2026-09-08 {#v1-2-0} This release adds support for the [Open Data Contract Standard v3.2.0](https://github.com/bitol-io/open-data-contract-standard/blob/main/CHANGELOG.md) ([#1557](https://github.com/datacontract/datacontract-cli/issues/1557)). #### Fixed - Iceberg testing uses AWS SSO credentials for S3 Tables data files and supports logical table aliases. - Iceberg catalog imports supply a catalog name, preserve qualified table identifiers, and correctly map temporal and binary fields. - `datacontract test` checks vector element types when the data source reports them ([#1585](https://github.com/datacontract/datacontract-cli/issues/1585)) #### Added - `datacontract test` resolves variables in property names, enum values, nested type options, library quality arguments, and service levels ([#1583](https://github.com/datacontract/datacontract-cli/issues/1583)) - Lint requires vector dimensions and rejects duplicate enum values regardless of their labels ([#1586](https://github.com/datacontract/datacontract-cli/issues/1586)) - Contracts declaring `apiVersion: v3.2.0` lint and round-trip, including `enum`, `map`, `vector`, `semanticType`, `synonyms`, `deprecated`, `context`, variables in string values, and the new server types ([#1558](https://github.com/datacontract/datacontract-cli/issues/1558)) - `datacontract export avro-idl` writes `map<...>` and `array` for `map` and `vector` properties; `datacontract export sqlalchemy` writes `JSON` and `ARRAY(Float)` ([#1558](https://github.com/datacontract/datacontract-cli/issues/1558)) - `datacontract test` resolves `${VAR}` and `${VAR:-default}` references in server fields and SQL quality queries from the environment; an unset variable without a default fails the run with its name, and `export` keeps the references ([#1559](https://github.com/datacontract/datacontract-cli/issues/1559)) - Server `port` may be a string such as `${DB_PORT}`, including in Excel imports; config files accept `${VAR:-default}` ([#1559](https://github.com/datacontract/datacontract-cli/issues/1559)) - `enum` on properties is read by `datacontract test`, the jsonschema, avro, avro-idl, protobuf, pydantic-model, dcs, great-expectations, sodacl and data-caterer exporters, and dbt test mapping, ahead of `logicalTypeOptions.enum`, the `enum` custom property and the `invalidValues` rule; HTML export lists the values with labels and descriptions ([#1560](https://github.com/datacontract/datacontract-cli/issues/1560)) - `datacontract import` from JSON Schema, Avro, Protobuf and DCS writes allowed values as `enum` entries instead of an `invalidValues` quality rule or custom properties ([#1560](https://github.com/datacontract/datacontract-cli/issues/1560)) - `datacontract export pydantic-model` types an enumerated string or integer property as `typing.Literal` ([#1560](https://github.com/datacontract/datacontract-cli/issues/1560)) - `logicalType: map` with a `map` block (`key` and `value` as full property definitions) is a first-class type: `datacontract test` checks the key and value types, including nested objects and maps, on DuckDB, Databricks, Snowflake, Trino, Kafka and the file sources ([#1562](https://github.com/datacontract/datacontract-cli/issues/1562)) - `datacontract export` writes native map types for snowflake, databricks, dataframe, duckdb (local, s3), clickhouse, trino, spark, iceberg, avro, avro-idl, protobuf, pydantic-model, go and dcs, JSON for postgres, mysql, sqlserver, oracle and bigquery, and `additionalProperties` for jsonschema; HTML shows the key and value types ([#1562](https://github.com/datacontract/datacontract-cli/issues/1562)) - `datacontract import` from sql, databricks/unity, spark, glue, iceberg, avro, parquet and dcs writes `logicalType: map` with the key and value instead of `physicalType: map` with custom properties; the `mapKeyType`, `mapValueType`, `mapKeys` and `mapValues` custom properties are still read but deprecated ([#1562](https://github.com/datacontract/datacontract-cli/issues/1562)) - `logicalType: vector` with `logicalTypeOptions.dimensions` is a first-class type: `datacontract test` accepts a native vector or an array of numbers and compares the dimensions when the column states them; `export` writes `vector(n)` for postgres, `VECTOR(FLOAT, n)` for snowflake, `FLOAT[n]` for duckdb, `vector(n)` for mysql, arrays of floats for databricks, dataframe, trino, clickhouse, bigquery, spark, iceberg, avro, avro-idl, protobuf, pydantic-model, go and dcs, and a fixed-length array of numbers for jsonschema ([#1563](https://github.com/datacontract/datacontract-cli/issues/1563)) - `datacontract import` from sql, postgres and snowflake reads `vector(n)`, `halfvec(n)` and `VECTOR(FLOAT, n)` columns as `logicalType: vector` with their dimensions, and parquet reads a fixed-size list of floats the same way ([#1563](https://github.com/datacontract/datacontract-cli/issues/1563)) - `datacontract export html` renders `semanticType`, `synonyms`, `deprecated` and `context` on schema objects and properties, the contract-level `context`, `vendor` on custom properties, and `customProperties` and `authoritativeDefinitions` on SLA entries; `export rdf` writes `enum` entries, `synonyms`, `map` definitions, `customProperties` and other nested definitions as nodes of their own instead of dropping them or printing a model repr ([#1561](https://github.com/datacontract/datacontract-cli/issues/1561)) - `datacontract changelog` matches `synonyms`, `enum` entries, `context` statements and constraints, and the nested lists of SLA entries by their natural key, and relationships by `id` when present, instead of by position ([#1561](https://github.com/datacontract/datacontract-cli/issues/1561)) - `datacontract test` reads CSV files from local, s3, gcs and azure servers and Kafka JSON messages in the server's declared `encoding`, and uses the Athena `workgroup` (also `DATACONTRACT_ATHENA_WORKGROUP`), which makes `stagingDir` optional ([#1564](https://github.com/datacontract/datacontract-cli/issues/1564)) - The ODCS v3.2.0 server types `hana`, `iceberg`, `exasol`, `teradata`, `ingres`, `vectorwise`, `versant` and `poet` lint and export; `test` explains that it cannot connect to them yet, and the synonyms `fastobjects` and `btrieve` resolve to `poet` and `zen` ([#1564](https://github.com/datacontract/datacontract-cli/issues/1564)) - `datacontract export sql --sql-server-type auto` warns before falling back to the snowflake dialect for a server type without one ([#1564](https://github.com/datacontract/datacontract-cli/issues/1564)) - `datacontract test` supports the `iceberg` server type: tables are read from the REST catalog named by `catalogUrl`, `namespace` and `warehouse` with pyiceberg (credentials via `DATACONTRACT_ICEBERG_CREDENTIAL` or `DATACONTRACT_ICEBERG_TOKEN`, data files via the S3 options; `DATACONTRACT_ICEBERG_CATALOG_TYPE` selects a `sql`, `glue` or `hive` catalog instead of `rest`, `DATACONTRACT_ICEBERG_S3_ENDPOINT` points at an S3-compatible store, Amazon S3 Tables and the Glue REST endpoint are signed with SigV4 from the AWS credentials, `DATACONTRACT_ICEBERG_PROPERTIES` passes further catalog properties through), and `datacontract import iceberg --catalog-url` creates a contract from a catalog table with a ready-to-test server ([#1565](https://github.com/datacontract/datacontract-cli/issues/1565)) #### Changed - `datacontract breaking` detects enum restrictions and vector shape changes ([#1584](https://github.com/datacontract/datacontract-cli/issues/1584)) - `datacontract init` and all importers write `apiVersion: v3.2.0` ([#1558](https://github.com/datacontract/datacontract-cli/issues/1558)) - The bundled Data Contract Editor (`datacontract edit`) is updated to 0.1.13 ([#1566](https://github.com/datacontract/datacontract-cli/issues/1566)) - `open-data-contract-standard` dependency bumped to 3.2.x ([#1558](https://github.com/datacontract/datacontract-cli/issues/1558)) - Lint accepts stable ids with any character except whitespace and `.#/\@!%&^`, as in the released ODCS v3.2.0 schema ### 1.1.3 — 2026-09-03 {#v1-1-3} #### Added - An `authoritativeDefinitions` link can reference a file next to the contract, either a property in another contract (`url: business.odcs.yaml#schema/orders/properties/order_id`) or a file that is the definition itself (`url: definitions/order_id.odcs.yaml`), resolved relative to the referencing contract ([#1453](https://github.com/datacontract/datacontract-cli/issues/1453)) - `datacontract breaking` command and `POST /breaking` endpoint for breaking change detection ([#1016](https://github.com/datacontract/datacontract-cli/issues/1016) [@pierre-monnet](https://github.com/pierre-monnet)) - `DATACONTRACT_KAFKA_GROUP_PREFIX` environment variable to customise the consumer group ID prefix used during Kafka testing ([#1553](https://github.com/datacontract/datacontract-cli/issues/1553) [@philipp-lutz](https://github.com/philipp-lutz)) - `datacontract test` checks the ODCS array options `minItems`, `maxItems` and `uniqueItems` ([#1514](https://github.com/datacontract/datacontract-cli/issues/1514) [@OGsiji](https://github.com/OGsiji)) - `datacontract export odcs` defaults `status` to `draft` when the source DCS contract has no `info.status` ([#1542](https://github.com/datacontract/datacontract-cli/issues/1542) [@michal-swiatowy](https://github.com/michal-swiatowy)) - `datacontract export great-expectations` covers the `logicalTypeOptions` constraints, attaches contract metadata to every expectation, checks the column set instead of the column order and takes a `--suite-name` ([#1544](https://github.com/datacontract/datacontract-cli/issues/1544) [@julienguilhempartner-spec](https://github.com/julienguilhempartner-spec)) - `datacontract test --dry-run` reports the checks a run would execute without connecting to the server or reading any data ([#1510](https://github.com/datacontract/datacontract-cli/issues/1510) [@OGsiji](https://github.com/OGsiji)) #### Fixed - `datacontract export jsonschema`, `datacontract export avro` and `datacontract test` on local files use a property's `physicalName` as the field name when set, instead of the logical `name` ([#1494](https://github.com/datacontract/datacontract-cli/issues/1494) [@philipp-lutz](https://github.com/philipp-lutz)) - `datacontract import sql` takes the server's `database` and `schema` from a qualified `CREATE TABLE`, instead of always writing placeholders ([#651](https://github.com/datacontract/datacontract-cli/issues/651) [@ReguiguiMohamed](https://github.com/ReguiguiMohamed)) - `datacontract import sql` no longer fails on a DDL file that contains `CREATE SCHEMA` ([#1529](https://github.com/datacontract/datacontract-cli/issues/1529) [@ReguiguiMohamed](https://github.com/ReguiguiMohamed)) - `datacontract test` sums every component of an ISO 8601 retention period, instead of reading only the first one ([#1538](https://github.com/datacontract/datacontract-cli/issues/1538)) - `datacontract test` reports each freshness and retention check result on its own check, instead of writing every result to the first one ([#1515](https://github.com/datacontract/datacontract-cli/issues/1515) [@erikgrip2](https://github.com/erikgrip2)) - `datacontract test --publish` with an empty value runs without publishing, instead of failing ([#1491](https://github.com/datacontract/datacontract-cli/issues/1491)) ### 1.1.2 — 2026-08-26 {#v1-1-2} #### Added - New server type `duckdb` to test the tables inside a DuckDB database file (the file is opened read-only) - `--dry-run` flag for `datacontract dbt sync` that reports the same plan as a real sync, but writes nothing to disk ([#1513](https://github.com/datacontract/datacontract-cli/issues/1513) [@q-maze](https://github.com/q-maze)) - Test results report how many rows a check found bad out of the rows it read, plus the data quality dimension and the `quality` rule a check comes from ([#1526](https://github.com/datacontract/datacontract-cli/issues/1526)) - `postgresql` is accepted as the ODCS synonym of the `postgres` server type - `datacontract import pydantic-model` reads the contract description from the module docstring ([#1507](https://github.com/datacontract/datacontract-cli/issues/1507) [@OGsiji](https://github.com/OGsiji)) #### Changed - The `engine` field of test results is now always one of `datacontract-cli`, `dbt` or `jsonschema`; the values `datacontract`, `ibis` and `dbt-sync` no longer occur ([#1505](https://github.com/datacontract/datacontract-cli/issues/1505)) - `datacontract test` and `datacontract ci` report a failed publish of the test results on the console and exit with code 1, instead of silently succeeding - `datacontract api`: - only publishes test results to the Entropy Data platform or the host configured via `ENTROPY_DATA_HOST`; other `publish_url` targets are refused ([#1541](https://github.com/datacontract/datacontract-cli/issues/1541)) - no longer sends permissive `Access-Control-Allow-Origin: *` headers; it serves no CORS headers at all, since the only browser client is the same-origin Swagger UI - no longer reloads on file changes by default; pass `--reload` to enable it (development only) - A JSON Schema check that could not be run is reported as `skipped`, not `info` #### Security - `datacontract export html` and `datacontract catalog` now HTML-escape data contract field values, closing a stored cross-site scripting hole - The Entropy Data API key is only sent to the Entropy Data host, no longer to any host a data contract URL or `--publish` URL points at; set `ENTROPY_DATA_HOST` for a self-hosted deployment - A `quality.type: sql` rule must be a read-only query; DDL, DML, `COPY`, `ATTACH` and the like are reported as a failed check instead of being executed, for every data source - `datacontract api`: - refuses `servers[].type: local`, so a posted data contract cannot read the files of the server running it; set `DATACONTRACT_CLI_API_ALLOW_LOCAL_FILES=true` to allow it - confines the DuckDB connection of a file-based server to the data locations the posted data contract declares - refuses to send an environment-held data source credential to a host named by the posted contract, preventing credential exfiltration through a crafted `servers` section - compares the `x-api-key` header in constant time to avoid a timing side channel - reports the URL of a failed `authoritativeDefinitions` lookup instead of echoing the server-side error reason, which is now logged instead #### Fixed - A `quality.type: sql` rule is read in the SQL dialect of its server type, so dialect-specific syntax (BigQuery backticks, Snowflake `SAMPLE`, SQL Server `TOP`) is no longer mistaken for an invalid query - Configuration options that override a server's location (`DATACONTRACT_BIGQUERY_PROJECT`, `DATACONTRACT_POSTGRES_SCHEMA`, and the like) now apply to the whole test run, not just to the connection - Test results published to Entropy Data no longer drop the quality rule id, category, rule definition and failed samples - `datacontract test` and `datacontract export sodacl` freshness and retention checks now honor the schema object's and property's `physicalName` ([#1488](https://github.com/datacontract/datacontract-cli/issues/1488) [@erikgrip2](https://github.com/erikgrip2)) - `datacontract test --publish` no longer fails for runs with skipped checks (skipped checks are omitted from the published test results) - BigQuery: `DATACONTRACT_BIGQUERY_BILLING_PROJECT` no longer overrides the server's `project`, so tables are still read from the data project ([#1358](https://github.com/datacontract/datacontract-cli/issues/1358)) - `datacontract export pydantic-model` exports the ODCS `timestamp` and `time` logical types as `datetime.datetime` and `datetime.time` instead of guessing from the physical type, which turned a `TIMESTAMPTZ` column into a `str` ([#1507](https://github.com/datacontract/datacontract-cli/issues/1507) [@OGsiji](https://github.com/OGsiji)) - `datacontract export pydantic-model` exports the ODCS `date` logical type as `datetime.date` instead of `datetime.datetime` ([#1507](https://github.com/datacontract/datacontract-cli/issues/1507) [@OGsiji](https://github.com/OGsiji)) - `datacontract import pydantic-model` maps `datetime.datetime` to the `timestamp` logical type and `datetime.time` to `time` ([#1507](https://github.com/datacontract/datacontract-cli/issues/1507) [@OGsiji](https://github.com/OGsiji)) - `datacontract export dcs` exports a `pii` custom property as a boolean instead of the string `'True'` ### 1.1.1 — 2026-08-14 {#v1-1-1} #### Added - SQL quality rules support the `${dataset}`, `${project}`, `${catalog}`, and `${database}` placeholders for the server's values - `datacontract test --metadata-only` runs only checks that read the schema (field presence and types) and reports checks that read row values as skipped - `datacontract test --checks` accepts the ODCS terms `properties` and `slaProperties`, keeping `schema` and `servicelevel` as legacy aliases - Released Docker images are signed with cosign keyless signing, on Docker Hub and the Amazon ECR Public mirror; see [Installation](https://docs.datacontract.com/installation#verifying-the-image) for how to verify them - `datacontract import pydantic-model` creates a data contract from Pydantic models #### Changed - `datacontract export excel` uses the ODCS Excel template bundled with the CLI instead of downloading it, so the export works offline; use `--template` for a custom template - The OpenAPI document of `datacontract api` reports the CLI version, describes every endpoint, parameter, and response model, and names its operations `testDataContract`, `lintDataContract`, `exportDataContract`, and `changelogBetweenDataContracts` - `DATACONTRACT_CLI_API_KEY` now also protects `POST /lint` and `POST /export`, which previously answered without an API key - Test results name the check fields `qualityId` and `failedSamples` instead of `quality_id` and `failed_samples`; the old names are deprecated, but still accepted as input and still written next to the new ones - `datacontract import unity` no longer writes the `databricksType` custom property, which duplicated `physicalType` - `datacontract export sql --server databricks` keeps the declared length of `varchar(n)` and `char(n)` instead of exporting `STRING` #### Fixed - `datacontract test` for API servers with `delimiter: array` no longer fails JSON schema validation with `data must be object` ([#1495](https://github.com/datacontract/datacontract-cli/issues/1495)) - `POST /export` answers `422` instead of `500` when the posted data contract cannot be parsed - `datacontract test` for Databricks no longer fails all checks of a model with a `GEOGRAPHY` or `GEOMETRY` column ([#1483](https://github.com/datacontract/datacontract-cli/issues/1483)) - `datacontract export pydantic-model` no longer emits an unparseable empty class for an object property without properties - An empty `--publish` value now means "don't publish" instead of failing with a URL validation error ### 1.1.0 — 2026-08-04 {#v1-1-0} This release drops the pyspark compile-time dependency. The server types `dataframe` and `databricks` still work with a provided Spark session. This removes the JVM dependency, makes the images much, much smaller (Docker image from 777 MB to 277 MB), and many CVEs are resolved. #### Added - `DATACONTRACT_KAFKA_MAX_MESSAGES` limits how many messages `datacontract test` reads from a topic, and `DATACONTRACT_KAFKA_TIMEOUT` how long it waits for one #### Changed - `datacontract test` for Kafka no longer needs PySpark or a Java runtime: `datacontract-cli[kafka]` now installs confluent-kafka, fastavro, and DuckDB instead - `datacontract-cli[databricks]` and `datacontract-cli[dataframe]` no longer install PySpark, so they can no longer shadow the build a Databricks Runtime or EMR cluster provides; supply your own PySpark for the Spark session these server types take, and `databricks-runtime` is now an alias of `databricks` - `datacontract-cli[all]` installs no PySpark at all, and so needs no Java runtime - The Docker image ships no JRE and uses a shell-less base image; a derived image can no longer `RUN pip install`, so `datacontract dbt test`, which needs a dbt adapter, is not available in the container - `datacontract import unity` resolves struct and array columns into nested properties without PySpark installed - `spark_exporter.to_spark_schema()`, `to_struct_type()`, `to_struct_field()`, and `to_spark_data_type()` return the exporter's own `SparkDataType` instead of `pyspark.sql.types` objects; use `to_spark_dict()` or the new `to_pyspark_schema()` for real PySpark schemas - Kafka topics with an Avro union of more than one non-null type are now reported as an error instead of being decoded into a struct of the union's members - Kafka messages without a value (compaction tombstones) are skipped instead of being checked as a row of nulls #### Fixed - `datacontract export spark` and `datacontract export great-expectations --engine spark` no longer require PySpark to be installed - `spark_exporter.to_spark_dict()` reports which PySpark version lacks a type instead of raising a bare `AttributeError`, e.g. `VariantType` before PySpark 4.0 - `datacontract test` for Databricks no longer creates ibis's memtable staging volume, so read-only service principals without `CREATE VOLUME` permission can run tests ### 1.0.17 — 2026-08-01 {#v1-0-17} #### Deprecated - `DATAMESH_MANAGER_API_KEY`, `DATAMESH_MANAGER_HOST`, `DATACONTRACT_MANAGER_API_KEY`, and `DATACONTRACT_MANAGER_HOST` (and the matching `Config` fields): use `ENTROPY_DATA_API_KEY` and `ENTROPY_DATA_HOST` instead #### Added - `datacontract export html` fits to datacontract-editor visualization - `datacontract test --filter` and `--filters` test only the rows matching a SQL predicate, e.g., the latest partition; also available as `filter`/`filters` query parameters on the API server's `POST /test` ([#1463](https://github.com/datacontract/datacontract-cli/issues/1463)) - Credentials and connection options can be passed programmatically via `DataContract(config=...)` and `DataContract.import_from_source(..., config=...)`, using the typed `datacontract.Config` class or a dict keyed by the environment variable names - The API server accepts per-request credentials on `POST /test` via `datacontract-*` headers (e.g. `datacontract-snowflake-password`) - Credentials and connection options can be provided in a YAML config file, via `--config-file` (defaults to `./datacontract-config.yaml` or `~/.datacontract/config.yaml`) or `Config.from_yaml()`, with `${VAR}` references resolved from the environment - New Snowflake connection options: `DATACONTRACT_SNOWFLAKE_TOKEN`, `DATACONTRACT_SNOWFLAKE_PASSCODE`, `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY`, `DATACONTRACT_SNOWFLAKE_NETWORK_TIMEOUT`, `DATACONTRACT_SNOWFLAKE_SOCKET_TIMEOUT`, `DATACONTRACT_SNOWFLAKE_HOST`, `DATACONTRACT_SNOWFLAKE_PORT` - Config options to override the server details from the data contract (host, port, database, schema, catalog, project, dataset, account, service name, staging directory) for Postgres, MySQL, SQL Server, Oracle, Redshift, Snowflake, BigQuery, Databricks, Trino, Athena, and Impala, e.g. `DATACONTRACT_POSTGRES_HOST` ([#1076](https://github.com/datacontract/datacontract-cli/issues/1076)) - Data contract locations now support `s3://` URLs for commands that read contracts, including `lint`, `test`, `export`, `publish`, `changelog`, and `ci` - The documentation now supports raw Markdown access by adding a `.md` extension in url ([#1464](https://github.com/datacontract/datacontract-cli/issues/1464)) #### Changed - `datacontract test` against Snowflake only forwards the documented `DATACONTRACT_SNOWFLAKE_*` options to the connector; unknown variables are ignored with a warning - `DATACONTRACT_SNOWFLAKE_ACCOUNT` overrides the contract's `account` instead of being ignored with a warning - `DATACONTRACT_DATABRICKS_SERVER_HOSTNAME` overrides the contract's `host`; previously the contract value won when both were set #### Fixed - `datacontract dbt sync` writes a description on all dbt tests, not only newly-generated ones ### 1.0.16 — 2026-07-31 {#v1-0-16} #### Added - `datacontract test` reads the Avro schema of a Kafka topic from the Confluent Schema Registry via `DATACONTRACT_KAFKA_SCHEMA_REGISTRY_URL`, `DATACONTRACT_KAFKA_SCHEMA_REGISTRY_USERNAME`, and `DATACONTRACT_KAFKA_SCHEMA_REGISTRY_PASSWORD` ([#1347](https://github.com/datacontract/datacontract-cli/issues/1347)) #### Deprecated - `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_PATH`, `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_PASSPHRASE`, and `DATACONTRACT_SNOWFLAKE_CONNECTION_TIMEOUT`: use `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_FILE`, `DATACONTRACT_SNOWFLAKE_PRIVATE_KEY_FILE_PWD`, and `DATACONTRACT_SNOWFLAKE_LOGIN_TIMEOUT` instead - `DATACONTRACT_SQLSERVER_TRUSTED_CONNECTION`: use `DATACONTRACT_SQLSERVER_AUTHENTICATION=windows` instead #### Fixed - `datacontract test` against a Kafka topic reports Avro messages it cannot decode as such, instead of reading every field as null ([#1347](https://github.com/datacontract/datacontract-cli/issues/1347)) - `datacontract test` connects to Databricks with an OAuth service principal again, instead of failing with `Error during request to server` on databricks-sql-connector 4.3.0 and later ([#1389](https://github.com/datacontract/datacontract-cli/issues/1389)) - `DATACONTRACT_SQLSERVER_TRUSTED_CONNECTION` no longer overrides an explicitly set `DATACONTRACT_SQLSERVER_AUTHENTICATION`, so a leftover flag cannot silently downgrade an Entra ID login to Windows authentication - `datacontract test` passes `DATACONTRACT_IMPALA_AUTH_MECHANISM`, `DATACONTRACT_IMPALA_USE_HTTP_TRANSPORT`, and `DATACONTRACT_IMPALA_HTTP_PATH` to Impala again, so a Cloudera Virtual Warehouse can be reached instead of failing with `TSocket read 0 bytes` - `datacontract test` passes the `servers` block `catalog` to Athena again, instead of always querying `awsdatacatalog` - `datacontract test` supports `DATACONTRACT_BIGQUERY_IMPERSONATION_ACCOUNT` again to impersonate a service account - `datacontract test` applies the documented Snowflake key-pair and timeout variables instead of silently ignoring them: the names they were documented under are not accepted by the Snowflake driver, and now map to the ones that are - `datacontract dbt sync` does not assume `severity: warn` as default anymore: tests now fail with dbt's default severity unless the contract declares a non-blocking `quality.severity` - `datacontract dbt sync` no longer drops or misplaces YAML comments that introduce the next column, test, or key ### 1.0.15 — 2026-07-30 {#v1-0-15} #### Added - `datacontract test` treats a server typed `mssql` as SQL Server, so contracts carrying the ODBC/dbt spelling are testable (ODCS itself only defines `sqlserver`) - `datacontract test --quality-id` runs a single quality rule by its ODCS `quality.id`, and `--tag` runs every quality rule declaring one of the given `quality.tags` ([#1080](https://github.com/datacontract/datacontract-cli/issues/1080)) - Test results report the `quality_id` and `tags` of the quality rule a check comes from - New `databricks-runtime` extra for installing inside a Databricks Runtime, where the cluster already provides PySpark: `pip install datacontract-cli[databricks-runtime]` ([#1211](https://github.com/datacontract/datacontract-cli/issues/1211) [@chifu1234](https://github.com/chifu1234)) - The docs Commands reference documents the global options `--version` and `--system-truststore`, and every import and export guide links to its command page and back - `datacontract test --dimension` runs only the checks measuring one data quality dimension, e.g. `--dimension uniqueness`; it matches the ODCS `quality.dimension` of a rule and the schema and service level checks that measure the same aspect - New `dataframe` extra installs just what testing Spark DataFrames needs: `pip install datacontract-cli[dataframe]` - `datacontract import trino` creates a data contract from a Trino catalog, including a ready-to-test `servers` block - `datacontract import oracle` creates a data contract from a live Oracle database, including a ready-to-test `servers` block - `datacontract import gcs` and `datacontract import adls` create a data contract from files in Google Cloud Storage or Azure Blob Storage, including a ready-to-test `servers` block - `datacontract import sqlserver` creates a data contract from a live SQL Server database, including a ready-to-test `servers` block - `datacontract import mysql` creates a data contract from a live MySQL database, including a ready-to-test `servers` block - The documentation has a [Release Notes](https://docs.datacontract.com/release-notes) page, generated from this changelog - The documentation has a guide to [migrate contracts from DCS to ODCS](https://docs.datacontract.com/migrate-dcs-to-odcs) - `datacontract import s3` creates a data contract from files in an S3 bucket, including a ready-to-test `servers` block - `datacontract import athena` creates a data contract from an Amazon Athena database, including a ready-to-test `servers` block - `datacontract import unity` is now `datacontract import databricks`; the `unity` format name keeps working - Redshift infers the authentication method: a password means a database login, otherwise your AWS session is used for IAM. `DATACONTRACT_REDSHIFT_AUTHENTICATION` is no longer required and remains as an override - `datacontract import postgres` creates a data contract from a live Postgres schema, including a ready-to-test `servers` block - `datacontract import redshift` creates a data contract from an Amazon Redshift schema, including a ready-to-test `servers` block - Redshift supports IAM authentication, using temporary credentials from your AWS session instead of a database password - `datacontract import bigquery` now generates a `servers` block, so `datacontract test` works right after the import - `datacontract test` verifies declared primary keys: each key column must have no missing values, and the key must have no duplicates (a composite key is checked as a tuple) ([#1220](https://github.com/datacontract/datacontract-cli/issues/1220) [@DMZ22](https://github.com/DMZ22)) #### Fixed - `datacontract test` checked `physicalType` against a same-named table in another schema when one existed, because the native column types were read from the catalog without the contract's schema - `datacontract test` against SQL Server no longer fails every check with "Could not read model" when `server.schema` differs from the login's default schema - `datacontract-cli[s3]` could not run `datacontract test`, and `datacontract-cli[gcs]` was missing the AWS duckdb extension the GCS connection loads; each data source extra now installs everything its guide needs - The API testing guide stated that no extra is required, but the response is tested with duckdb; it installs `datacontract-cli[duckdb]` now - `datacontract test` told users to install `datacontract-cli[local]`, an extra that does not exist, and `datacontract-cli[api]`, which installs the web server rather than a test backend; both now point at `duckdb` - `datacontract import gcs` wrote `type: gcs`, which is not an ODCS server type, so the imported contract failed `datacontract lint` and `datacontract test`; GCS is now written as an `s3` server on the Google interoperability endpoint - A data contract could inject SQL into the duckdb session through `endpointUrl`, which is interpolated into the statement that stores the S3, GCS and Azure credentials; every value is escaped now - The `datacontract api` server accepted a local file path as the `schema` query parameter, so a caller could have it read files from the server's filesystem; only `http(s)` URLs are accepted now - Trino physical type checks were silently skipped: its `information_schema` has no length or precision columns, so the catalog query failed and a wrong `physicalType` still passed - `datacontract import athena` and `datacontract import glue` now honour `DATACONTRACT_S3_ACCESS_KEY_ID` and `DATACONTRACT_S3_SECRET_ACCESS_KEY`; the Glue catalog was read with ambient AWS credentials only - S3 now uses an existing AWS session (`aws sso login`, `AWS_PROFILE`, instance roles) when no access key is configured; previously such a setup failed with `403 Forbidden` - Documented that Athena authenticates with an existing AWS session (`aws sso login`, `AWS_PROFILE`, instance roles); static access keys were presented as the only option - `regionName` in an Athena `servers` block was ignored, so the region could only be set via `DATACONTRACT_S3_REGION` - `datacontract import glue` mapped `timestamp` columns to `logicalType: date` instead of `timestamp` - Testing and importing Redshift failed with `codec not available in Python: 'UNICODE'` - Error messages no longer drop bracketed text such as `pip install "botocore[crt]"` - BigQuery export failed on fields with `logicalType: time` - Testing Parquet files failed for `number` fields without a declared precision and scale - CSV and JSON imports now write detected formats (`email`, `uuid`, `date-time`) to `logicalTypeOptions.format` instead of a custom property, so they are validated by `datacontract test` - SQL imports now map `TIME` types with precision or time zone (e.g. `TIME(9)`) to `logicalType: time`; previously the logical type was left unset - `datacontract test --checks quality` now runs `rowCount` quality rules, which were wrongly categorized as schema checks - `datacontract import dbt` derives the contract `id` from the dbt manifest's `project_name` instead of always emitting the placeholder `my-data-contract` ([#1221](https://github.com/datacontract/datacontract-cli/issues/1221) [@DMZ22](https://github.com/DMZ22)) - A `physicalType` declaring a zero scale (`NUMBER(38,0)`, `decimal(18,0)`) failed against its own column on Snowflake, Oracle, SQL Server and Databricks ([#1377](https://github.com/datacontract/datacontract-cli/issues/1377) [@DMZ22](https://github.com/DMZ22)) - Snowflake `physicalType` checks failed for structured `OBJECT`, `ARRAY` and `MAP` columns, whose fields are now compared field by field instead of as rendered strings ([#1377](https://github.com/datacontract/datacontract-cli/issues/1377) [@DMZ22](https://github.com/DMZ22)) - `datacontract test` on Athena failed a `physicalType` written in the Hive spelling `datacontract import athena` produces (`array` against the reported `array(varchar)`) ([#1377](https://github.com/datacontract/datacontract-cli/issues/1377) [@DMZ22](https://github.com/DMZ22)) - A `physicalType` declaring fractional seconds (`TIMESTAMP_NTZ(9)`, `datetime2(7)`, `timestamp(3)`) failed against its own column, so every timestamp column imported from Snowflake failed the first `datacontract test` - `datacontract test` and `datacontract import oracle` read Oracle character lengths in bytes, so an `NVARCHAR2(50)` column was reported and checked as `NVARCHAR2(100)` - `datacontract test` on Databricks could not check the element types of `ARRAY`, `MAP` and `STRUCT` columns, which the catalog reports as a bare type name ### 1.0.14 — 2026-07-23 {#v1-0-14} #### Added - `datacontract export protobuf` supports a customizable package name via the `protoPackageName` custom property ([#1381](https://github.com/datacontract/datacontract-cli/issues/1381) [@Schokuroff](https://github.com/Schokuroff)) - `datacontract export protobuf` emits `optional` for non-required message/object fields ([#1390](https://github.com/datacontract/datacontract-cli/issues/1390) [@Schokuroff](https://github.com/Schokuroff)) #### Fixed - `datacontract dbt sync` resolves `{object}`/`{property}` placeholders in custom `sql` quality checks to the dbt `ref()` and column name ([#1397](https://github.com/datacontract/datacontract-cli/issues/1397)) - SyntaxWarning during installation: `datacontract/lint/resolve.py:72: SyntaxWarning: 'return' in a 'finally' block return except_message` is handled properly ([#1384](https://github.com/datacontract/datacontract-cli/issues/1384) [@Cupprum](https://github.com/Cupprum)) - `datacontract test` no longer reports "backend is not installed" for Athena and other ibis SQL backends when `packaging` is missing from the environment ### 1.0.13 — 2026-07-14 {#v1-0-13} #### Added - `datacontract test` reports the nested types of a property that declares `properties:` or `items:` as a separate check - `datacontract test` on Snowflake verifies the `physicalType` of nested properties against the real column type #### Fixed - `datacontract test` on Snowflake matches a `physicalType` against the alias the catalog reports, such as `BIGINT` on a `NUMBER(38,0)` column - `datacontract test` no longer reports a mismatch for a `physicalType` without precision, such as `NUMBER` on a `NUMBER(12,2)` column ### 1.0.12 — 2026-07-10 {#v1-0-12} #### Fixed - `datacontract test` now recursively verifies nested `logicalType` for Snowflake structured `OBJECT`/`ARRAY` columns ([#1373](https://github.com/datacontract/datacontract-cli/issues/1373)) - `datacontract test` now fails for complex types when the nested type definition cannot be verified (e.g. if array is required, array will no longer be accepted) ([#1373](https://github.com/datacontract/datacontract-cli/issues/1373)) ### 1.0.11 — 2026-07-09 {#v1-0-11} #### Added - `datacontract import` can now import BigQuery type `INTERVAL` ([#1367](https://github.com/datacontract/datacontract-cli/issues/1367),[#1372](https://github.com/datacontract/datacontract-cli/issues/1372) [@fantastisch](https://github.com/fantastisch)) #### Fixed - Failed business definition IRI lookups now suggest the `ENTROPY_DATA_HOST` value to set when the IRI host does not match the configured entropy-data host. - `datacontract test` no longer reports a physical type mismatch for BigQuery type aliases, such as a `physicalType` of `INTEGER` on an `INT64` column ([#1371](https://github.com/datacontract/datacontract-cli/issues/1371) [@fantastisch](https://github.com/fantastisch)) - `datacontract test` no longer fails with `CANNOT_CONVERT_COLUMN_INTO_BOOL` on Databricks when a Spark session is used ### 1.0.10 — 2026-07-08 {#v1-0-10} #### Added - extended `datacontract dbt sync`: - now edits existing properties files (schema.yaml) in-place instead of creating new ones - preserve manual edits to a properties file - edit schema incl. descriptions, column types and tags, not only tests - add `--prune` flag to remove everything that's not specified in the contract (models, tags, checks) - per default, only generated content gets removed - support for multiple contract versions (`versions:` block) - possibility to sync multiple contracts at once - store the contract id and version in the `meta:` block - added `datacontract dbt test`: Use local `dbt` to run all tests that have been generated using `datacontract dbt sync` earlier (scoped to a single data contract, or all data contracts in the opened dbt project) - optionally publish to Entropy Data #### Changed - `datacontract dbt sync`: - no longer executes tests per default (use `--run-tests` or run `datacontract dbt test` afterwards) - `datacontract test` now verifies a field's `physicalType` against the column's real native type from the platform catalog (length and precision included), taking precedence over `logicalType` ([#1354](https://github.com/datacontract/datacontract-cli/issues/1354)) - `datacontract test` JSON output now includes `datacontractCliVersion` ([#1353](https://github.com/datacontract/datacontract-cli/issues/1353) [@hk8suva](https://github.com/hk8suva)) - `datacontract test` type-check errors now report the first failing field and a count of the remaining errors ([#1334](https://github.com/datacontract/datacontract-cli/issues/1334) [@jorgengranseth](https://github.com/jorgengranseth)) #### Fixed - `datacontract test` no longer fails the type check for SQL Server `uniqueidentifier` (UUID) columns with "the column type could not be determined" ([#1354](https://github.com/datacontract/datacontract-cli/issues/1354)) - `datacontract test` against BigQuery no longer fails SQL quality checks with `'RowIterator' object has no attribute 'fetchone'` - `datacontract import` against BigQuery applies correct `logicalType` for BigQuery types `TIMESTAMP`, `DATETIME`, and `TIME` ([#1366](https://github.com/datacontract/datacontract-cli/issues/1366) [@fantastisch](https://github.com/fantastisch)) ### 1.0.9 — 2026-06-26 {#v1-0-9} #### Fixed - `datacontract test` against Kafka no longer reports every field as null for plain (non-Confluent Schema Registry) Avro messages ([#1344](https://github.com/datacontract/datacontract-cli/issues/1344)) - Honor the `pattern` argument in `invalidValues` quality checks ([#1346](https://github.com/datacontract/datacontract-cli/issues/1346)) ### 1.0.8 — 2026-06-25 {#v1-0-8} #### Fixed - `datacontract test` against Redshift no longer fails with `relation "pg_catalog.pg_enum" does not exist`. Redshift rides the Postgres ibis backend, whose schema introspection joins `pg_catalog.pg_enum` to detect enum columns — a relation Redshift does not expose. Introspection now omits that join (Redshift has no enum types). ### 1.0.7 — 2026-06-25 {#v1-0-7} #### Added - New global option `--system-truststore` (env `DATACONTRACT_SYSTEM_TRUSTSTORE`) to verify TLS using the operating system's certificate trust store, for use behind corporate proxies or with internal CAs. ### 1.0.6 — 2026-06-24 {#v1-0-6} #### Fixed - `datacontract test` against Redshift no longer fails with `column "current_schema" does not exist`. Redshift rides the Postgres ibis backend, whose introspection resolved the active schema with `SELECT current_schema` (no parentheses) — valid on PostgreSQL but rejected by Redshift, which only supports `current_schema()`. The configured server `schema` is now passed explicitly during introspection, skipping that query. ### 1.0.5 — 2026-06-24 {#v1-0-5} #### Fixed - `datacontract test` now only supports logicalTypes. Previously physicalType was preferrerd and used even if logicalType did not exist. - `datacontract test` field type check now compares the full structured type tree for `object` and `array` logical types. - Unknown and unsupported types are silently ignored rather than failing the check. Specifically the `map` type is not supported until ODCS version v3.2.0 and is also ignored. - `datacontract --help` no longer fails with `ModuleNotFoundError: No module named 'ibis'` when the optional `ibis` extra is not installed. - `datacontract test` against Oracle now qualifies tables with the configured server `schema` (owner), fixing `Could not read model '
':
` when the login user differs from the table owner. ### 1.0.4 — 2026-06-22 {#v1-0-4} #### Added - `datacontract test` validates Azure Blob Storage / ADLS Gen2 file metadata against a data contract used as a storage policy ([#1227](https://github.com/datacontract/datacontract-cli/issues/1227)) - `datacontract test` against Trino now supports `DATACONTRACT_TRINO_AUTHENTICATION=jwt` with `DATACONTRACT_TRINO_JWT_TOKEN`, and `DATACONTRACT_TRINO_AUTHENTICATION=oauth2` for the interactive browser flow. - `datacontract export sql --dialect clickhouse`: export data contracts to ClickHouse SQL DDL. ([#1293](https://github.com/datacontract/datacontract-cli/issues/1293)) - Example data contracts and import sources under `examples/`, used as the worked examples on the docs export and import pages. #### Fixed - `datacontract import unity` now imports struct and array columns as structured ODCS types instead of only a flat type string: structs get nested `properties`, arrays get `items` (parsed recursively from Unity's `type_json`, including descriptions on nested fields). Arrays also get the correct `logicalType: array` (previously `object`); this logical type fix applies to the `sql` and `snowflake` importers as well. Map columns and other unmappable SQL types now leave `logicalType` unset (instead of the invalid `object`) until ODCS v3.2 adds `logicalType: map` (RFC 0030). ([#1280](https://github.com/datacontract/datacontract-cli/issues/1280)) - `datacontract export dcs` no longer crashes on data contracts with a structured description or a standard server. - `datacontract test` against Oracle releases before 23ai no longer fails every check with `ORA-00923: FROM keyword not found where expected` during schema introspection. - `datacontract test` now recognizes Oracle `VARCHAR2`/`NVARCHAR2` (with or without a length, e.g. `VARCHAR2(4000)`) as string types in the field type check. ### 1.0.3 — 2026-06-15 {#v1-0-3} #### Added - `datacontract import powerbi` imports a data contract from a Power BI semantic model (.pbit, .bim, or .json) ([#1232](https://github.com/datacontract/datacontract-cli/issues/1232),[#1233](https://github.com/datacontract/datacontract-cli/issues/1233) [@dmaresma](https://github.com/dmaresma)) #### Changed - `datacontract edit` shows the edited filename in the editor header, no longer offers New/Load Example/Open, and Cancel reverts to the file on disk #### Fixed - `datacontract test` no longer aborts remaining quality checks after a SQL quality check falls back to native execution on DuckDB-backed sources ([#1302](https://github.com/datacontract/datacontract-cli/issues/1302)) - Athena: pass `DATACONTRACT_S3_SESSION_TOKEN` to the connection again, fixing `UnrecognizedClientException` with temporary AWS credentials ([#1309](https://github.com/datacontract/datacontract-cli/issues/1309)) ### 1.0.2 — 2026-06-11 {#v1-0-2} #### Added - `datacontract edit` now accepts a URL: it asks to download a local copy and opens that in the editor, using the configured API key (e.g., `ENTROPY_DATA_API_KEY`) to fetch the file. - BigQuery: added support for a separate billing/compute project via the `DATACONTRACT_BIGQUERY_BILLING_PROJECT` environment variable. When set, query jobs are submitted to (and billed against) the billing project while data is read from the `project` specified in the server config. This enables cross-project and cross-organisation validation without requiring `bigquery.jobUser` on the data project. #### Fixed - `datacontract test` no longer fails on Spark/Databricks tables containing columns of types ibis cannot represent, such as VariantType ([#1296](https://github.com/datacontract/datacontract-cli/issues/1296)) - The `postgres` and `redshift` extras now install `psycopg[binary]`, whose wheels bundle the libpq client library. Previously, `datacontract test` against PostgreSQL/Redshift failed on machines without PostgreSQL client libraries installed (`couldn't import psycopg: no pq wrapper available`). ### 1.0.1 — 2026-06-10 {#v1-0-1} #### Added - Environment variables (e.g., credentials for `datacontract test`) are now also loaded from a `.env` file in the current working directory, walking up parent directories until one is found. Already-set environment variables take precedence over `.env` values, so exported variables and CI secrets keep working unchanged. ([#1295](https://github.com/datacontract/datacontract-cli/issues/1295)) - `datacontract edit ` opens a local data contract file in the [Data Contract Editor](https://github.com/datacontract/datacontract-editor) (web UI). Starts a local web server (default port 4243, requires the `api` extra) that serves the editor, writes saves directly back to the file, and doubles as the editor's test runner: "Run test" in the editor executes the data contract tests locally via the server's own `/test` endpoint. The editor is bundled with the CLI and works offline; `--editor-version` loads a specific editor version from the CDN instead, `--editor-assets-url` a self-hosted build. If the file does not exist, the command offers to initialize a new data contract (same template as `datacontract init`). Saving gives feedback in the editor and on the console. #### Fixed - `datacontract test` no longer fails with `Compilation rule for RegexSearch operation is not defined` when a contract uses `logicalTypeOptions.pattern` (or `pattern`) against SQL Server. SQL Server has no native regex operator, so the ibis mssql backend cannot compile `re_search`; pattern checks now fall back to a `PATINDEX(...) > 0` LIKE match, restoring the behaviour of the former soda-core engine. Patterns that use real regex syntax (anchors, quantifiers, groups, `.`) cannot be expressed as a T-SQL LIKE pattern and now raise a clear error instead of failing cryptically. ([#1284](https://github.com/datacontract/datacontract-cli/issues/1284)) ### 1.0.0 — 2026-06-04 {#v1-0-0} #### Breaking Changes - Replaced the Soda Core quality/test engine with [ibis](https://ibis-project.org/). `datacontract test` now compiles schema and quality checks into ibis expressions (dialect-correct SQL per backend via sqlglot, local/remote files via DuckDB) instead of generating SodaCL. Install extras now pull `ibis-framework[]` instead of `soda-core-*`. Check semantics and pass/fail results are preserved for the supported sources (postgres, redshift, mysql, snowflake, bigquery, databricks, sqlserver, oracle, trino, athena, impala, kafka/dataframe via the ibis Spark backend, and local/S3/GCS/Azure files). - Raw SodaCL custom quality checks (`quality.type: custom` with `engine: soda`) are no longer executed and now report a warning. Migrate them to `quality.type: sql` or a library metric (e.g. `metric: rowCount`). #### Added - Python 3.13 and 3.14 support (`requires-python` now allows 3.10–3.14). On 3.13/3.14 the Spark-backed extras resolve to PySpark 4.0 (Spark 3.5 has no 3.13+ build); the Kafka/Avro connector jars already adapt to the runtime Spark/Scala version. `create_spark_session` now pins `PYSPARK_PYTHON`/`PYSPARK_DRIVER_PYTHON` to the running interpreter so Spark's Python workers match the driver. - `datacontract test` against Databricks now supports more authentication methods beyond the personal access token (`DATACONTRACT_DATABRICKS_TOKEN`): an OAuth service principal for machine-to-machine auth (`DATACONTRACT_DATABRICKS_CLIENT_ID` + `DATACONTRACT_DATABRICKS_CLIENT_SECRET`), a local config profile via the Databricks SDK unified auth (`DATACONTRACT_DATABRICKS_PROFILE`, also covers Azure CLI/MSI), and an explicit connector auth type (`DATACONTRACT_DATABRICKS_AUTH_TYPE`, e.g. `databricks-oauth` for the interactive browser flow). - `datacontract test` now records structured `diagnostics` on each check explaining why it passed or failed: the metric, measured value, threshold, and (for "bad row" metrics) the total row count and failed fraction. `invalid_count` checks also report the validity rule they enforced (e.g. `{"max_length": 20}`, `{"pattern": "^.+@.+$"}`). The diagnostics surface in the JSON output and the JUnit failure text. This replaces the Soda-specific `diagnostics` payload that the ibis migration had left unpopulated. - `datacontract test` now honors ODCS `quality.unit: percent` on the count-of-bad-rows library metrics (`nullValues`, `missingValues`, `invalidValues`). The threshold is then compared against the failed fraction (0–100) of the model row count instead of an absolute count, so e.g. `metric: nullValues`, `unit: percent`, `mustBeLessThan: 5` passes when fewer than 5% of rows are null. Percent on metrics where a row fraction has no meaning (`rowCount`, `duplicateValues`) logs a warning and falls back to the absolute count. The measured `percent` is added to the check diagnostics. - `datacontract test` now honors ODCS `quality.severity`: a non-blocking severity (`info`, `warning`, `low`, `minor`, `trivial`) downgrades a failing quality check to a `warning` instead of a `failed`, so it no longer fails the run. Any other severity (or none) still fails. The severity is recorded in the check diagnostics. - `datacontract test --include-failed-samples` collects a small sample of the rows that failed each `missing`/`invalid`/`duplicate` check (off by default). Each sample is restricted to the contract's identifier columns (`unique` / `primaryKey` fields) plus the offending column; duplicate checks report the duplicated key values and their counts. Columns whose ODCS `classification` marks them sensitive (`pii`, `personal`, `confidential`, `restricted`, `sensitive`, `secret`) are omitted. Samples are capped at 5 rows per check and surface on `Check.failed_samples` in the JSON output and in the JUnit failure text. This is local-only and needs no Soda Cloud (soda-core itself collects failed-row samples only via Soda Cloud). #### Changed - MySQL is tested through DuckDB's `mysql` extension instead of ibis's native MySQL backend, so the `mysql` extra stays pure-pip (no `mysqlclient` C build / system MySQL client libraries required). - Bumped DuckDB to the 1.5.x line (from 1.0.x) with the bundled `duckdb-extension-*` wheels (httpfs/aws/azure) pinned in lockstep. The 1.5.x extension wheels publish arm64 Linux builds, so air-gapped installs on arm64 Linux are now supported (the previous platform skip markers were removed). - The Kafka/Avro Spark connector jars are now derived from the installed PySpark at runtime (Spark version + Scala binary version), so both PySpark 3.5.x (Scala 2.12) and 4.x (Scala 2.13) work. The `kafka` and `databricks` extras allow `pyspark<5.0`. - `import protobuf` now uses the pure-Python `proto-schema-parser` instead of the `protoc` system binary. The `protobuf` extra no longer requires `protoc` (or the `protobuf` runtime), so `.proto` import works out of the box, including transitive imports across subdirectories. - Container image is now based on [Docker Hardened Images](https://hub.docker.com/hardened-images/catalog/dhi/python): signed, ships SBOM/VEX, and has tighter CVE patch SLAs than upstream Debian. Runs as nonroot (uid 65532) instead of root. `pip` / `uv` installs at build time are routed through Socket Firewall Free, which blocks malicious dependencies. ([#1275](https://github.com/datacontract/datacontract-cli/issues/1275), [#1277](https://github.com/datacontract/datacontract-cli/issues/1277)) - Container image now ships Eclipse Temurin JRE 17, so PySpark-backed engines (Kafka, Spark) actually run inside the image — previously they failed at `SparkSession` startup because the base image had no JVM. End users pulling `datacontract/cli` are unaffected by the build-side changes. ([#1277](https://github.com/datacontract/datacontract-cli/issues/1277)) #### Fixed - DuckDB S3 secret creation no longer fails (`Secret Validation Failure`) on DuckDB ≥1.5: explicit `KEY_ID`/`SECRET` now use the default `config` provider instead of `CREDENTIAL_CHAIN`. - `import csv` no longer fails with a DuckDB binder error on DuckDB ≥1.5 (the uniqueness probe now uses `count(DISTINCT ...)` via SQL). #### Removed - The `soda-core` runtime dependency and all `soda-core-*` install extras, plus the `setuptools` runtime shim they required. The `sodacl` export format (`datacontract export sodacl`) is unchanged and is now generated independently of any Soda runtime. - The unused `details` field on test-result checks (`Run.checks[].details`). It was a Soda-era placeholder that was never populated; the new structured `diagnostics` field replaces it. The JUnit failure text no longer prints a `Details:` line. ### 0.12.5 — 2026-05-30 {#v0-12-5} #### Added - Resolve `authoritativeDefinitions[type=definition]` on schema properties, filling fields the contract author left unset ([#1261](https://github.com/datacontract/datacontract-cli/issues/1261)) - **Breaking:** Per default, any resolution failure of `authoritativeDefinitions[type in {definition, semantics}]` now rejects the contract on `lint`, `test`, `ci`, `export`, and `changelog`. ([#1261](https://github.com/datacontract/datacontract-cli/issues/1261)) - Resolve `authoritativeDefinitions[type=semantics]` (and the legacy `type=semantic`) the same way. A `url:` that points at the configured entropy-data host is fetched directly; a `url:` that's an IRI (host doesn't match) is routed through `GET /api/semantics?iri=...` on the configured host, which uses the API key's organization to resolve. ([#1262](https://github.com/datacontract/datacontract-cli/issues/1262)) - `--no-inline-references` flag to skip the HTTP fetch ([#1261](https://github.com/datacontract/datacontract-cli/issues/1261)) - When `--json-schema` points at a custom JSON Schema, the ODCS Pydantic step now accepts extra top-level fields the schema allows ([#1266](https://github.com/datacontract/datacontract-cli/issues/1266)) #### Changed - JSON Schema validation errors now identify the offending schema and property by name instead of array indices ([#1255](https://github.com/datacontract/datacontract-cli/issues/1255) [@dmaresma](https://github.com/dmaresma)) - `test`, `lint`, and `ci` now infer `--output-format` from the `--output` file extension when not given (`.json` → json, `.xml` → junit) ([#1156](https://github.com/datacontract/datacontract-cli/issues/1156) [@dallylee](https://github.com/dallylee)) #### Fixed - Schema type check no longer fails for `varchar(n)` columns on Databricks with PySpark 4.0+, and for `map` and `varchar` types nested inside `struct` columns; affected columns emit a warning and skip the type check instead ([#1219](https://github.com/datacontract/datacontract-cli/issues/1219),[#1245](https://github.com/datacontract/datacontract-cli/issues/1245) [@IchEssBlumen](https://github.com/IchEssBlumen)) - `WARNING`/`ERROR` log messages are no longer hidden by default for `import`, `export`, `changelog`, `catalog`, `dbt`, and `publish` ([#1264](https://github.com/datacontract/datacontract-cli/issues/1264)) - `datacontract test` against S3, GCS, and Azure no longer fails with `Failed to download extension` in air-gapped containers. The required DuckDB extensions are now bundled via the `s3`/`gcs`/`azure` install extras ([#1191](https://github.com/datacontract/datacontract-cli/issues/1191) [@ParenParikh](https://github.com/ParenParikh)) - JSON/CSV/Parquet with DuckDB: `field_is_present` check now correctly detects missing columns ([#1065](https://github.com/datacontract/datacontract-cli/issues/1065),[#1163](https://github.com/datacontract/datacontract-cli/issues/1163) [@hieusats](https://github.com/hieusats)) - `import dbt --model .vN` now correctly imports the specified version of a versioned dbt model. Previously the filter compared the full `name.vN` string against `node["name"]` (which is always the bare base name), producing a silent empty contract ([#1249](https://github.com/datacontract/datacontract-cli/issues/1249) [@willbowditch](https://github.com/willbowditch)) ### 0.12.4 — 2026-05-21 {#v0-12-4} #### Added - `datacontract test` now logs the Data Contract CLI version and whether it ran as a local CLI or through the FastAPI server (including the request URL) as part of the test result logs #### Fixed - Schema checks now resolve each property by its `physicalName` when set (falling back to `name`), matching the existing table-level resolution and the SQL/BigQuery exporters. Previously a property whose logical `name` differed from its physical column (e.g. `name: brand` with `physicalName: BRAND`) failed the presence and type checks even though the physical column existed ([#1246](https://github.com/datacontract/datacontract-cli/issues/1246)) ### 0.12.3 — 2026-05-18 {#v0-12-3} #### Added - new `datacontract dbt sync` command: generate dbt tests from an ODCS contract, then run `dbt test` for them, and optionally publish the results to Entropy Data ([#1222](https://github.com/datacontract/datacontract-cli/issues/1222), [#1235](https://github.com/datacontract/datacontract-cli/issues/1235)) - `redshift` server type for `datacontract test` (requires `pip install datacontract-cli[redshift]`). ([#1236](https://github.com/datacontract/datacontract-cli/issues/1236)) #### Fixed - SQL type converter: emit canonical `decimal`/`numeric` per dialect (Postgres → `numeric`, MySQL → `decimal`) so `test`'s column-type check matches `information_schema` ([#1237](https://github.com/datacontract/datacontract-cli/issues/1237)) ### 0.12.2 — 2026-05-05 {#v0-12-2} #### Added - `impala` extra (`pip install datacontract-cli[impala]`) — pulls in `soda-core-impala`. Impala engine support landed in [#965](https://github.com/datacontract/datacontract-cli/issues/965) but the install extra was never added; users had to install `soda-core-impala` manually. Also included in `[all]`. #### Changed - **breaking:** drop the `dbt` extra and the `dbt-core` dependency. `import dbt` now reads `manifest.json` directly with no third-party dependency, and works without installing any extra. Minimum supported manifest schema version is v9 (dbt 1.5+). Users who installed `datacontract-cli[dbt]` should switch to plain `datacontract-cli`. - **breaking:** the `protobuf` extra now requires the `protoc` compiler installed on the system. Replaces the bundled `grpcio-tools` (~50 MB of platform-specific protoc binaries) with the lighter `protobuf` runtime (`>=3.20,<7.0`). `import protobuf` raises a clear error with platform-specific install hints if `protoc` is not on `PATH`. Install with `brew install protobuf` (macOS), `sudo apt install protobuf-compiler` (Debian/Ubuntu), etc. — see README. #### Fixed - README install table: add missing `csv`, `excel`, and `oracle` extras. The matching `[project.optional-dependencies]` entries already existed but were undocumented. - quality: support `{object}` and `${object}` placeholder in SQL quality queries as the ODCS-spec name for the current schema object (alias for `{model}`/`{table}`) ([#676](https://github.com/datacontract/datacontract-cli/issues/676)) - `changelog` command help text now advertises `(url or path)` for V1/V2 arguments, clarifying that HTTP/HTTPS URLs are accepted ([#1162](https://github.com/datacontract/datacontract-cli/issues/1162)) - **breaking:** `test` command now exits non-zero when a server is specified, but soda-core fails to connect or authenticate ([#1181](https://github.com/datacontract/datacontract-cli/issues/1181)) - correct swapped `check_type` labels `model_qualty_sql` and `field_quality_sql` ([#1187](https://github.com/datacontract/datacontract-cli/issues/1187)) - `import spark` now emits a native Spark SQL physicalType (e.g. `string`) instead of Python repr (e.g. `StringType()`). Contracts imported using Spark in v0.11.0–v0.12.1 did not perform type checks and must be re-imported. ([#1048](https://github.com/datacontract/datacontract-cli/issues/1048)) - Re-add `setuptools` as a base dependency. soda-core's `env_helper.py` imports `from distutils.util import strtobool`; `distutils` was removed from stdlib in Python 3.12 and stripped from python-build-standalone 3.11 builds. `setuptools` provides the `distutils` shim. Previously pulled in transitively via `grpcio-tools`; now required explicitly. Reverts [#1199](https://github.com/datacontract/datacontract-cli/issues/1199) — see soda-core#2091. - SLA freshness checks now quote column identifiers with special characters ([#1202](https://github.com/datacontract/datacontract-cli/issues/1202)) - update field / model quotation for Impala, dataframe, and Kafka ([#1202](https://github.com/datacontract/datacontract-cli/issues/1202)) ### 0.12.1 — 2026-04-21 {#v0-12-1} #### Fixed - make `--schema` a deprecated alias for `--json-schema` to (will be removed in v0.13.0) ### 0.12.0 — 2026-04-20 {#v0-12-0} This release introduces several changes to improve the usability of `datacontract-cli` for AI Agents. - **Breaking**: Several changes in the CLI syntax ([#1157](https://github.com/datacontract/datacontract-cli/issues/1157)): > Fix in v0.12.1: re-added `--schema` as alias for the new `--json-schema` (will be removed in v0.13.0) | Command | Old option | New option | |--------------------------------------------|-----------------------------------------------|----------------------------------------| | `lint`, `test`, `ci`, `publish`, `catalog` | `--schema ` (will work until v0.13.0) | `--json-schema ` | | `export`, `import` | `--format ` | ` ` (drop `--format`) | | **Export options:** | | | | `export --format dbt` | `--format dbt` | `dbt-models` (format renamed) | | `export --format great-expectations` | `--sql-server-type ` | `--dialect ` | | `export --format rdf` | `--rdf-base ` | `--base ` | | `export --format sql` | `--sql-server-type ` | `--dialect ` | | `export --format sql-query` | `--sql-server-type ` | `--dialect ` | | **Import options:** | | | | `import --format bigquery` | `--bigquery-[project\|dataset\|table] ` | `--[project\|dataset\|table] ` | | `import --format dbt` | `--dbt-model ` | `--model ` | | `import --format glue` | `--source `, `--glue-table ` | `--database `, `--table ` | | `import --format iceberg` | `--iceberg-table ` | `--table ` | | `import --format unity` | `--unity-table-full-name ` | `--table ` | | `import --format spark` | `--source ` | `--tables ` | | `import` | `--template` | dropped (was a no-op) | The `--schema` option (referring to the ODCS JSON schema) was renamed to `--json-schema` to avoid confusion with `--schema-name`, which refers to the schema within the data contract to test for. - Error messages for uncaught exceptions are shortened now. Pass `--debug` (or set `DATACONTRACT_CLI_DEBUG=1`) to see the full traceback. ([#1175](https://github.com/datacontract/datacontract-cli/issues/1175)) - Add example calls to `--help` outputs ([#1176](https://github.com/datacontract/datacontract-cli/issues/1176)) - Add explicit errors when required env vars for soda connections are missing ([#1177](https://github.com/datacontract/datacontract-cli/issues/1177)) - Validate some of the CLI options against their allowed values instead of accepting any string ([#1178](https://github.com/datacontract/datacontract-cli/issues/1178)) ### 0.11.9 — 2026-04-20 {#v0-11-9} #### Added - Added `--checks` option to `test` command to selectively run check categories: `schema`, `quality`, `servicelevel` ([#678](https://github.com/datacontract/datacontract-cli/issues/678)) - Added `--schema-name` option to `test` command to test a specific schema instead of all schemas ([#1079](https://github.com/datacontract/datacontract-cli/issues/1079),[#1085](https://github.com/datacontract/datacontract-cli/issues/1085) [@kelsoufi-sanofi](https://github.com/kelsoufi-sanofi)) #### Fixed - Move `precision`/`scale` for `number` types from `logicalTypeOptions` to `customProperties` ([#1145](https://github.com/datacontract/datacontract-cli/issues/1145),[#1160](https://github.com/datacontract/datacontract-cli/issues/1160) [@davidb-tada](https://github.com/davidb-tada)) - Emit placeholder server values in SQL importer so generated contracts pass lint ([#1146](https://github.com/datacontract/datacontract-cli/issues/1146),[#1152](https://github.com/datacontract/datacontract-cli/issues/1152) [@Ai-chan-0411](https://github.com/Ai-chan-0411)) - Fix Protobuf export for arrays of objects and improve message/enum naming to UpperCamelCase ([#1012](https://github.com/datacontract/datacontract-cli/issues/1012) [@Schokuroff](https://github.com/Schokuroff)) - Exit with code 1 when `--server` name is not found ([#1153](https://github.com/datacontract/datacontract-cli/issues/1153),[#1161](https://github.com/datacontract/datacontract-cli/issues/1161) [@Ai-chan-0411](https://github.com/Ai-chan-0411)) Thanks to [@kelsoufi-sanofi](https://github.com/kelsoufi-sanofi) for the new `--schema-name` option on `test`, and to [@Schokuroff](https://github.com/Schokuroff), [@Ai-chan-0411](https://github.com/Ai-chan-0411), and [@davidb-tada](https://github.com/davidb-tada) for their contributions. ### 0.11.8 — 2026-04-10 {#v0-11-8} #### Added - Added `ci` command for CI/CD-optimized test runs: multi-file support, GitHub Actions annotations and step summary, Azure DevOps annotations, `--fail-on` flag, `--json` output ([#1114](https://github.com/datacontract/datacontract-cli/issues/1114)) - Added `changelog` command and API endpoint ([#1118](https://github.com/datacontract/datacontract-cli/issues/1118) [@davidb-tada](https://github.com/davidb-tada)) - Added opt-in `--all-errors` mode for `datacontract lint` to report all JSON Schema validation errors, with matching `all_errors` support in the Python library and API ([#1125](https://github.com/datacontract/datacontract-cli/issues/1125) [@jmbenedetto](https://github.com/jmbenedetto)) - Added `--schema-name` option to custom model export ([#978](https://github.com/datacontract/datacontract-cli/issues/978) [@AntoineGiraud](https://github.com/AntoineGiraud)) #### Fixed - Avro importer now raises an error for union fields with multiple non-null types, which are not supported by ODCS ([#1124](https://github.com/datacontract/datacontract-cli/issues/1124)) - Fix SQL export generating multiple PRIMARY KEY constraints for composite keys ([#1026](https://github.com/datacontract/datacontract-cli/issues/1026),[#1092](https://github.com/datacontract/datacontract-cli/issues/1092) [@barry0451](https://github.com/barry0451) [@dwestheide](https://github.com/dwestheide)) - Preserve parametrized physicalTypes for SQL export ([#1086](https://github.com/datacontract/datacontract-cli/issues/1086),[#1093](https://github.com/datacontract/datacontract-cli/issues/1093) [@barry0451](https://github.com/barry0451) [@alexander-griesbeck](https://github.com/alexander-griesbeck)) - Fix incorrect SQL type mappings: SQL Server `double`/`jsonb`, MySQL bare `varchar`, missing Trino types ([#1110](https://github.com/datacontract/datacontract-cli/issues/1110)) - Fix markdown export breaking table structure when extra field values contain pipe characters ([#832](https://github.com/datacontract/datacontract-cli/issues/832),[#1117](https://github.com/datacontract/datacontract-cli/issues/1117) [@barry0451](https://github.com/barry0451) [@grepwood](https://github.com/grepwood)) - Fix dbt import using incorrect physicalType instead of actual materialization type ([#1136](https://github.com/datacontract/datacontract-cli/issues/1136)) - Remove unnecessary numpy dependency from databricks and kafka extras ([#1135](https://github.com/datacontract/datacontract-cli/issues/1135) [@kayhendriksen](https://github.com/kayhendriksen)) Special thanks to [@davidb-tada](https://github.com/davidb-tada) for the outstanding contribution of the new `changelog` command and API endpoint! Also thanks to [@barry0451](https://github.com/barry0451) for multiple quality fixes across the SQL exporter and markdown export, and to [@AntoineGiraud](https://github.com/AntoineGiraud) and [@jmbenedetto](https://github.com/jmbenedetto) for their feature contributions. ### 0.11.7 — 2026-03-24 {#v0-11-7} #### Fixed - Escape single quotes in string values for SodaCL checks ([#1090](https://github.com/datacontract/datacontract-cli/issues/1090)) - Escape BigQuery field and model names with backticks for SodaCL checks ([#736](https://github.com/datacontract/datacontract-cli/issues/736)) - Escape Databricks model names with backticks for SodaCL checks - Fixed catalog export SpecView not having a tags property for the index.html template ([#1059](https://github.com/datacontract/datacontract-cli/issues/1059)) - Fix SQL importer type mappings: binary types, datetime/time, uuid now map to correct ODCS logicalType and format ([#790](https://github.com/datacontract/datacontract-cli/issues/790)) #### Added - Added support for MySQL for data contract tests ([#1101](https://github.com/datacontract/datacontract-cli/issues/1101)) - Support additional PyArrow types in Parquet importer ([#1091](https://github.com/datacontract/datacontract-cli/issues/1091)) - Populate `logicalTypeOptions.format` for SQL import from binary and uuid types ([#790](https://github.com/datacontract/datacontract-cli/issues/790)) - Snowflake DDL import with tags, descriptions, and template variable handling ([#790](https://github.com/datacontract/datacontract-cli/issues/790)) ### 0.11.6 — 2026-03-17 {#v0-11-6} #### Fixed - Fix parser error for CSV / Parquet table names containing special characters ([#1066](https://github.com/datacontract/datacontract-cli/issues/1066)) - Fix BigQuery export failing with "Unsupported type" for parameterized physicalType like `NUMERIC(18, 4)` ([#1083](https://github.com/datacontract/datacontract-cli/issues/1083)) #### Added - Added JSON output format for test results (`--output-format json`) - Added Azure AD / Entra ID authentication support for SQL Server and Microsoft Fabric ### 0.11.5 — 2026-02-19 {#v0-11-5} #### Fixed - Fix BigQuery import for repeated fields ([#1017](https://github.com/datacontract/datacontract-cli/issues/1017)) - Make Markdown export compatible with XHTML by replacing `
` with `
` ([#1030](https://github.com/datacontract/datacontract-cli/issues/1030)) - Add ADC/WIF and impersonation support for BigQuery ([#1064](https://github.com/datacontract/datacontract-cli/issues/1064)) - Fix Snowflake quoted identifiers by enabling double-quote quoting ([#1053](https://github.com/datacontract/datacontract-cli/issues/1053)) - Fix retention duration crash for numeric ODCS values ([#1051](https://github.com/datacontract/datacontract-cli/issues/1051)) - Fix physicalType bypass for precision and scale conversion ([#1043](https://github.com/datacontract/datacontract-cli/issues/1043)) - Fix mkdir TOCTOU race causing silent JUnit write failure ([#1050](https://github.com/datacontract/datacontract-cli/issues/1050)) - Fix validation failure for field names with special chars on Databricks ([#1049](https://github.com/datacontract/datacontract-cli/issues/1049)) - Add Azure support for field name quoting in schema checks ([#1025](https://github.com/datacontract/datacontract-cli/issues/1025)) ### 0.11.4 — 2026-01-19 {#v0-11-4} #### Changed - Made `duckdb` an optional dependency. Install with `pip install datacontract-cli[duckdb]` for local/S3/GCS/Azure file testing. - Removed unused `fastparquet` and `numpy` core dependencies. #### Added - Include searchable tags in catalog index.html #### Fixed - Fixed example(s) field mapping for Data Contract Specification importer ([#992](https://github.com/datacontract/datacontract-cli/issues/992)). - Spark exporter now supports decimal precision/scale via `customProperties` or parsing from `physicalType` (e.g., `decimal(10,2)`) ([#996](https://github.com/datacontract/datacontract-cli/issues/996)) - Fix catalog/HTML export failing on ODCS contracts with no schema or no properties ([#971](https://github.com/datacontract/datacontract-cli/issues/971)) ### 0.11.3 — 2026-01-10 {#v0-11-3} #### Fixed - Fix `datacontract init` to generate ODCS format instead of deprecated Data Contract Specification ([#984](https://github.com/datacontract/datacontract-cli/issues/984)) - Fix ODCS lint failing on optional relationship `type` field by updating open-data-contract-standard to v3.1.2 ([#971](https://github.com/datacontract/datacontract-cli/issues/971)) - Restrict DuckDB dependency to < 1.4.0 ([#972](https://github.com/datacontract/datacontract-cli/issues/972)) - Fixed schema evolution support for optional fields in CSV and Parquet formats. Optional fields marked with `required: false` are no longer incorrectly treated as required during validation, enabling proper schema evolution where optional fields can be added to contracts without breaking validation of historical data files ([#977](https://github.com/datacontract/datacontract-cli/issues/977)) - Fixed decimals in pydantic model export. Fields marked with `type: decimal` will be mapped to `decimal.Decimal` instead of `float`. - Fix BigQuery test failure for fields with FLOAT or BOOLEAN types by mapping them to equivalent types (BOOL and FLOAT64) ### 0.11.2 — 2025-12-15 {#v0-11-2} #### Added - Add Impala engine support for Soda scans via ODCS `impala` server type. #### Fixed - Restrict DuckDB dependency to < 1.4.0 ([#972](https://github.com/datacontract/datacontract-cli/issues/972)) ### 0.11.1 — 2025-12-14 {#v0-11-1} This is a major release with breaking changes: We switched the internal data model from [Data Contract Specification](https://datacontract-specification.com) to [Open Data Contract Standard](https://datacontract.com/#odcs) (ODCS). Not all features that were available are supported in this version, as some features are not supported by the Open Data Contract Standard, such as: - Internal definitions using `$ref` (you can refer to external definitions via `authoritativeDefinition`) - Lineage (no real workaround, use customProperties or transformation object if needed) - Support for different physical types (no real workaround, use customProperties if needed) - Support for enums (use quality metric `invalidValues`) - Support for properties with type map and defining `keys` and `values` (use logical type map) - Support for `scale` and `precision` (define them in `physicalType`) The reason for this change is that the Data Contract Specification is deprecated, we focus on best possible support for the Open Data Contract Standard. We try to make this transition as seamless as possible. If you face issues, please open an issue on GitHub. We continue support reading [Data Contract Specification](https://datacontract-specification.com) data contracts during v0.11.x releases until end of 2026. To migrate existing data contracts to Open Data Contract Standard use this instruction: https://datacontract-specification.com/#migration #### Changed - ODCS v3.1.0 is now the default format for all imports. - Renamed `--model` option to `--schema-name` in the `export` command to align with ODCS terminology. - Renamed exporter files from `*_converter.py` to `*_exporter.py` for consistency (internal change). #### Added - If an ODCS slaProperty "freshness" is defined with a reference to the element (column), the CLI will now test freshness of the data. - If an ODCS slaProperty "retention" is defined with a reference to the element (column), the CLI will now test retention of the data. - Support for custom Soda quality checks in ODCS using `type: custom` and `engine: soda` with raw SodaCL implementation. #### Fixed - Oracle: Fix `service_name` attribute access to use ODCS field name `serviceName` #### Removed - The `breaking`, `changelog`, and `diff` commands are now deleted ([#925](https://github.com/datacontract/datacontract-cli/issues/925)). - The `terraform` export format has been removed. ### 0.10.41 — 2025-12-02 {#v0-10-41} #### Changed - Great Expectations export: Update to Great Expectations 1.x format ([#919](https://github.com/datacontract/datacontract-cli/issues/919)) - Changed `expectation_suite_name` to `name` in suite output - Changed `expectation_type` to `type` in expectations - Removed `data_asset_type` field from suite output - **Breaking**: Users with custom quality definitions using `expectation_type` must update to use `type` #### Added - test: Log server name and type in output ([#963](https://github.com/datacontract/datacontract-cli/issues/963)) - api: CORS is now enabled for all origins - quality: Support `{schema}` and `${schema}` placeholder in SQL quality checks to reference the server's database schema ([#957](https://github.com/datacontract/datacontract-cli/issues/957)) - SQL Server: Support `DATACONTRACT_SQLSERVER_DRIVER` environment variable to specify the ODBC driver ([#959](https://github.com/datacontract/datacontract-cli/issues/959)) - Excel: Add Oracle server type support for Excel export/import ([#960](https://github.com/datacontract/datacontract-cli/issues/960)) - Excel: Add local/CSV server type support for Excel export/import ([#961](https://github.com/datacontract/datacontract-cli/issues/961)) - Excel Export: Complete server types (glue, kafka, postgres, s3, snowflake, sqlserver, custom) #### Fixed - Protobuf import: Fix transitive imports across subdirectories ([#943](https://github.com/datacontract/datacontract-cli/issues/943)) - Protobuf export now works without error ([#951](https://github.com/datacontract/datacontract-cli/issues/951)) - lint: YAML date values (e.g., `2022-01-15`) are now kept as strings instead of being converted to datetime objects, fixing ODCS schema validation - export: field annotation now matches to number/numeric/decimal types - Excel: Server port is now correctly parsed as integer instead of string for all server types - Excel: Remove invalid `table` and `view` fields from custom server import - Fixed DuckDB DDL generation to use `JSON` type instead of invalid empty `STRUCT()` for objects without defined properties ([#940](https://github.com/datacontract/datacontract-cli/issues/940)) #### Deprecated - The `breaking`, `changelog`, and `diff` commands are now deprecated and will be removed in a future version ([#925](https://github.com/datacontract/datacontract-cli/issues/925)) ### 0.10.40 — 2025-11-25 {#v0-10-40} #### Added - Support for ODCS v3.1.0 ### 0.10.39 — 2025-11-20 {#v0-10-39} #### Added - Oracle DB: Client Directory for Connection Mode 'Thick' can now be specified in the `DATACONTRACT_ORACLE_CLIENT_DIR` environment variable ([#949](https://github.com/datacontract/datacontract-cli/issues/949)) #### Fixed - Import composite primary keys from open data contract spec ### 0.10.38 — 2025-11-11 {#v0-10-38} #### Added - Support for Oracle Database (>= 19C) #### Fixed - Athena: Now correctly uses the (optional) AWS session token specified in the `DATACONTRACT_S3_SESSION_TOKEN' environment variable when testing contracts ([#934](https://github.com/datacontract/datacontract-cli/issues/934)) ### 0.10.37 — 2025-11-03 {#v0-10-37} #### Added - import: Support for nested arrays in odcs v3 importer - lint: ODCS schema is now checked before converting - --debug flag for all commands #### Fixed - export: Excel exporter now exports critical data element ### 0.10.36 — 2025-10-17 {#v0-10-36} #### Added - Support for Data Contract Specification v1.2.1 (Data Quality Metrics) - Support for decimal testing in spark and databricks ([#902](https://github.com/datacontract/datacontract-cli/issues/902)) - Support for BigQuery Flexible Schema in Data Contract Checks ([#909](https://github.com/datacontract/datacontract-cli/issues/909)) #### Changed - `DataContract().import_from_source()` as an instance method is now deprecated. Use `DataContract.import_from_source()` as a class method instead. #### Fixed - Export to DQX: Correct DQX format for global-level quality check of data contract export. ([#877](https://github.com/datacontract/datacontract-cli/issues/877)) - Import the table tags from a open data contract spec v3 ([#895](https://github.com/datacontract/datacontract-cli/issues/895)) - dbt export: Enhanced model-level primaryKey support with automatic test generation for single and multiple column primary keys ([#898](https://github.com/datacontract/datacontract-cli/issues/898)) - ODCS: field discarded when no logicalType defined ([#891](https://github.com/datacontract/datacontract-cli/issues/891)) #### Removed - Removed specific linters, as the linters did not support ODCS ([#913](https://github.com/datacontract/datacontract-cli/issues/913)) ### 0.10.35 — 2025-08-25 {#v0-10-35} #### Added - Export to DQX : datacontract export --format dqx ([#846](https://github.com/datacontract/datacontract-cli/issues/846)) - API `/test` endpoint now supports `publish_url` parameter to publish test results to a URL. ([#853](https://github.com/datacontract/datacontract-cli/issues/853)) - The Spark importer and exporter now also exports the description of columns via the additional metadata of StructFields ([#868](https://github.com/datacontract/datacontract-cli/issues/868)) #### Fixed - Improved regex for extracting Azure storage account names from URLs with containerName@storageAccountName format ([#848](https://github.com/datacontract/datacontract-cli/issues/848)) - JSON Schema Check: Add globbing support for local JSON files - Fixed server section rendering for markdown exporter ### 0.10.34 — 2025-08-06 {#v0-10-34} #### Added - `datacontract test` now supports HTTP APIs. - `datacontract test` now supports Athena. #### Fixed - Avro Importer: Optional and required enum types are now supported ([#804](https://github.com/datacontract/datacontract-cli/issues/804)) ### 0.10.33 — 2025-07-29 {#v0-10-33} #### Added - Export to Excel: Convert ODCS YAML to Excel https://github.com/datacontract/open-data-contract-standard-excel-template ([#742](https://github.com/datacontract/datacontract-cli/issues/742)) - Extra properties in Markdown export. ([#842](https://github.com/datacontract/datacontract-cli/issues/842)) ### 0.10.32 — 2025-07-28 {#v0-10-32} #### Added - Import from Excel: Support the new quality sheet #### Fixed - JUnit Test Report: Fixed incorrect syntax on handling warning test report. ([#833](https://github.com/datacontract/datacontract-cli/issues/833)) ### 0.10.31 — 2025-07-18 {#v0-10-31} #### Added - Added support for Variant with Spark exporter, data_contract.test(), and import as source unity catalog ([#792](https://github.com/datacontract/datacontract-cli/issues/792)) ### 0.10.30 — 2025-07-15 {#v0-10-30} #### Fixed - Excel Import should return ODCS YAML ([#829](https://github.com/datacontract/datacontract-cli/issues/829)) - Excel Import: Missing server section when the server included a schema property ([#823](https://github.com/datacontract/datacontract-cli/issues/823)) #### Changed - Use ` ` instead of ` ` for tab in Markdown export. ### 0.10.29 — 2025-07-06 {#v0-10-29} #### Added - Support for Data Contract Specification v1.2.0 - `datacontract import --format json`: Import from JSON files #### Changed - `datacontract api [OPTIONS]`: Added option to pass extra arguments for `uvicorn.run()` #### Fixed - `pytest tests\test_api.py`: Fixed an issue where special characters were not read correctly from file. - `datacontract export --format mermaid`: Fixed an issue where the `mermaid` export did not handle references correctly ### 0.10.28 — 2025-06-05 {#v0-10-28} #### Added - Much better ODCS support - Import anything to ODCS via the `import --spec odcs` flag - Export to HTML with an ODCS native template via `export --format html` - Export to Mermaid with an ODCS native mapping via `export --format mermaid` - The databricks `unity` importer now supports more than a single table. You can use `--unity-table-full-name` multiple times to import multiple tables. And it will automatically add a server with the catalog and schema name. #### Changed - `datacontract catalog [OPTIONS]`: Added version to contract cards in `index.html` of the catalog (enabled search by version) - The type mapping of the `unity` importer no uses the native databricks types instead of relying on spark types. This allows for better type mapping and more accurate data contracts. #### Fixed ### 0.10.27 — 2025-05-22 {#v0-10-27} #### Added - `datacontract export --format mermaid` Export to [Mermaid](https://mermaid-js.github.io/mermaid/#/) ([#767](https://github.com/datacontract/datacontract-cli/issues/767), [#725](https://github.com/datacontract/datacontract-cli/issues/725)) #### Changed - `datacontract export --format html`: Adding the mermaid figure to the html export - `datacontract export --format odcs`: Export physical type to ODCS if the physical type is configured in config object - `datacontract import --format spark`: Added support for spark importer table level comments ([#761](https://github.com/datacontract/datacontract-cli/issues/761)) - `datacontract import` respects `--owner` and `--id` flags ([#753](https://github.com/datacontract/datacontract-cli/issues/753)) #### Fixed - `datacontract export --format sodacl`: Fix resolving server when using `--server` flag ([#768](https://github.com/datacontract/datacontract-cli/issues/768)) - `datacontract export --format dbt`: Fixed DBT export behaviour of constraints to default to data tests when no model type is specified in the datacontract model ### 0.10.26 — 2025-05-16 {#v0-10-26} #### Changed - Databricks: Add support for Variant type ([#758](https://github.com/datacontract/datacontract-cli/issues/758)) - `datacontract export --format odcs`: Export physical type if the physical type is configured in config object ([#757](https://github.com/datacontract/datacontract-cli/issues/757)) - `datacontract export --format sql` Include datacontract descriptions in the Snowflake sql export ( [#756](https://github.com/datacontract/datacontract-cli/issues/756)) ### 0.10.25 — 2025-05-07 {#v0-10-25} #### Added - Extracted the DataContractSpecification and the OpenDataContractSpecification in separate pip modules and use them in the CLI. - `datacontract import --format excel`: Import from Excel template https://github.com/datacontract/open-data-contract-standard-excel-template ([#742](https://github.com/datacontract/datacontract-cli/issues/742)) ### 0.10.24 — 2025-04-19 {#v0-10-24} #### Added - `datacontract test` with DuckDB: Deep nesting of json objects in duckdb ([#681](https://github.com/datacontract/datacontract-cli/issues/681)) #### Changed - `datacontract import --format csv` produces more descriptive output. Replaced using clevercsv with duckdb for loading and sniffing csv file. - Updated dependencies #### Fixed - Fix to handle logicalType format wrt avro mentioned in issue ([#687](https://github.com/datacontract/datacontract-cli/issues/687)) - Fix field type from TIME to DATETIME in BigQuery converter and schema ([#728](https://github.com/datacontract/datacontract-cli/issues/728)) - Fix encoding issues. ([#712](https://github.com/datacontract/datacontract-cli/issues/712)) - ODCS: Fix required in export and added item and fields format ([#724](https://github.com/datacontract/datacontract-cli/issues/724)) #### Removed - Deprecated QualityLinter is now removed ### 0.10.23 — 2025-03-03 {#v0-10-23} #### Added - `datacontract test --output-format junit --output TEST-datacontract.xml` Export CLI test results to a file, in a standard format (e.g. JUnit) to improve CI/CD experience ([#650](https://github.com/datacontract/datacontract-cli/issues/650)) - Added import for `ProtoBuf` Code for proto to datacontract ([#696](https://github.com/datacontract/datacontract-cli/issues/696)) - `dbt` & `dbt-sources` export formats now support the optional `--server` flag to adapt the DBT column `data_type` to specific SQL dialects - Duckdb Connections are now configurable, when used as Python library ([#666](https://github.com/datacontract/datacontract-cli/issues/666)) - export to avro format add map type #### Changed - Changed Docker base image to python:3.11-bullseye - Relax fastparquet dependency #### Fixed - Unicode Encode Error when exporting data contract YAML to HTML ([#652](https://github.com/datacontract/datacontract-cli/issues/652)) - Fix multiline descriptions in the DBT export functionality - Incorrectly parsing $ref values in definitions ([#664](https://github.com/datacontract/datacontract-cli/issues/664)) - Better error message when the server configuration is missing in a data contract ([#670](https://github.com/datacontract/datacontract-cli/issues/670)) - Improved default values in ODCS generator to avoid breaking schema validation ([#671](https://github.com/datacontract/datacontract-cli/issues/671)) - Updated ODCS v3 generator to drop the "is" prefix from fields like `isNullable` and `isUnique` ([#669](https://github.com/datacontract/datacontract-cli/issues/669)) - Fix issue when testing databricks server with ODCS format - avro export fix float format ### 0.10.22 — 2025-02-20 {#v0-10-22} #### Added - `datacontract test` now also executes tests for service levels freshness and retention ([#407](https://github.com/datacontract/datacontract-cli/issues/407)) #### Changed - `datacontract import --format sql` is now using SqlGlot as importer. - `datacontract import --format sql --dialect ` Dialect can now to defined when importing SQL. #### Fixed - Schema type checks fail on nested fields for Databricks spark ([#618](https://github.com/datacontract/datacontract-cli/issues/618)) - Export to Avro add namespace on field as optional configuration ([#631](https://github.com/datacontract/datacontract-cli/issues/631)) #### Removed - `datacontract test --examples`: This option was removed as it was not very popular and top-level examples section is deprecated in the Data Contract Specification v1.1.0 ([#628](https://github.com/datacontract/datacontract-cli/issues/628)) - Support for `odcs_v2` ([#645](https://github.com/datacontract/datacontract-cli/issues/645)) ### 0.10.21 — 2025-02-06 {#v0-10-21} #### Added - `datacontract export --format custom`: Export to custom format with Jinja - `datacontract api` now can be protected with an API key #### Changed - `datacontract serve` renamed to `datacontract api` #### Fixed - Fix Error: 'dict object' has no attribute 'model_extra' when trying to use type: string with enum values inside an array ([#619](https://github.com/datacontract/datacontract-cli/issues/619)) ### 0.10.20 — 2025-01-30 {#v0-10-20} #### Added - datacontract serve: now has a route for testing data contracts - datacontract serve: now has a OpenAPI documentation on root #### Changed - FastAPI endpoint is now moved to extra "web" #### Fixed - API Keys for Data Mesh Manager are now also applied for on-premise installations ### 0.10.19 — 2025-01-29 {#v0-10-19} #### Added - datacontract import --format csv - publish command also supports publishing ODCS format - Option to separate physical table name for a model via config option ([#270](https://github.com/datacontract/datacontract-cli/issues/270)) #### Changed - JSON Schemas are now bundled with the application ([#598](https://github.com/datacontract/datacontract-cli/issues/598)) - datacontract export --format html: The model title is now shown if it is different to the model name ([#585](https://github.com/datacontract/datacontract-cli/issues/585)) - datacontract export --format html: Custom model attributes are now shown ([#585](https://github.com/datacontract/datacontract-cli/issues/585)) - datacontract export --format html: The composite primary key is now shown. ([#591](https://github.com/datacontract/datacontract-cli/issues/591)) - datacontract export --format html: now examples are rendered in the model and definition ([#497](https://github.com/datacontract/datacontract-cli/issues/497)) - datacontract export --format sql: Create arrays and struct for Databricks ([#467](https://github.com/datacontract/datacontract-cli/issues/467)) #### Fixed - datacontract lint: Linter 'Field references existing field' too many values to unpack (expected 2) ([#586](https://github.com/datacontract/datacontract-cli/issues/586)) - datacontract test (Azure): Error querying delta tables from azure storage. ([#458](https://github.com/datacontract/datacontract-cli/issues/458)) - datacontract export --format data-caterer: Use `fields` instead of `schema` - datacontract export --format data-caterer: Use `options` instead of `generator.options` - datacontract export --format data-caterer: Capture array type length option and inner data type - Fixed schemas/datacontract-1.1.0.init.yaml not included in build and `--template` not resolving file ### 0.10.18 — 2025-01-18 {#v0-10-18} #### Fixed - Fixed an issue when resolving project's dependencies when all extras are installed. - Definitions referenced by nested fields are not validated correctly ([#595](https://github.com/datacontract/datacontract-cli/issues/595)) - Replaced deprecated `primary` field with `primaryKey` in exporters, importers, examples, and Jinja templates for backward compatibility. Fixes [#518](https://github.com/your-repo/your-project/issues/518). - Cannot execute test on column of type record(bigquery) [#597](https://github.com/datacontract/datacontract-cli/issues/597) ### 0.10.17 — 2025-01-16 {#v0-10-17} #### Added - added export format **markdown**: `datacontract export --format markdown` ([#545](https://github.com/datacontract/datacontract-cli/issues/545)) - When importing in dbt format, add the dbt unique information as a datacontract unique field ([#558](https://github.com/datacontract/datacontract-cli/issues/558)) - When importing in dbt format, add the dbt primary key information as a datacontract primaryKey field ([#562](https://github.com/datacontract/datacontract-cli/issues/562)) - When exporting in dbt format, add the datacontract references field as a dbt relationships test ([#569](https://github.com/datacontract/datacontract-cli/issues/569)) - When importing in dbt format, add the dbt relationships test field as a reference in the data contract ([#570](https://github.com/datacontract/datacontract-cli/issues/570)) - Add serve command on README ([#592](https://github.com/datacontract/datacontract-cli/issues/592)) #### Changed - Primary and example fields have been deprecated in Data Contract Specification v1.1.0 ([#561](https://github.com/datacontract/datacontract-cli/issues/561)) - Define primaryKey and examples for model to follow the changes in datacontract-specification v1.1.0 ([#559](https://github.com/datacontract/datacontract-cli/issues/559)) #### Fixed - SQL Server: cannot escape reserved word on model ([#557](https://github.com/datacontract/datacontract-cli/issues/557)) - Export dbt-staging-sql error on multi models contracts ([#587](https://github.com/datacontract/datacontract-cli/issues/587)) #### Removed - OpenTelemetry publisher, as it was hardly used ### 0.10.16 — 2024-12-19 {#v0-10-16} #### Added - Support for exporting a Data Contract to an Iceberg schema definition. - When importing in dbt format, add the dbt `not_null` information as a datacontract `required` field ([#547](https://github.com/datacontract/datacontract-cli/issues/547)) #### Changed - Type conversion when importing contracts into dbt and exporting contracts from dbt ([#534](https://github.com/datacontract/datacontract-cli/issues/534)) - Ensure 'name' is the first column when exporting in dbt format, considering column attributes ([#541](https://github.com/datacontract/datacontract-cli/issues/541)) - Rename dbt's `tests` to `data_tests` ([#548](https://github.com/datacontract/datacontract-cli/issues/548)) #### Fixed - Modify the arguments to narrow down the import target with `--dbt-model` ([#532](https://github.com/datacontract/datacontract-cli/issues/532)) - SodaCL: Prevent `KeyError: 'fail'` from happening when testing with SodaCL - fix: populate database and schema values for bigquery in exported dbt sources ([#543](https://github.com/datacontract/datacontract-cli/issues/543)) - Fixing the options for importing and exporting to standard output ([#544](https://github.com/datacontract/datacontract-cli/issues/544)) - Fixing the data quality name for model-level and field-level quality tests ### 0.10.15 — 2024-12-02 {#v0-10-15} #### Added - Support for model import from parquet file metadata. - Great Expectation export: add optional args ([#496](https://github.com/datacontract/datacontract-cli/issues/496)) - `suite_name` the name of the expectation suite to export - `engine` used to run checks - `sql_server_type` to define the type of SQL Server to use when engine is `sql` - Changelog support for `Info` and `Terms` blocks. - `datacontract import` now has `--output` option for saving Data Contract to file - Enhance JSON file validation (local and S3) to return the first error for each JSON object, the max number of total errors can be configured via the environment variable: `DATACONTRACT_MAX_ERRORS`. Furthermore, the primaryKey will be additionally added to the error message. - fixes issue where records with no fields create an invalid bq schema. #### Changed - Changelog support for custom extension keys in `Models` and `Fields` blocks. - `datacontract catalog --files '*.yaml'` now checks also any subfolders for such files. - Optimize test output table on console if tests fail #### Fixed - raise valid exception in DataContractSpecification.from_file if file does not exist - Fix importing JSON Schemas containing deeply nested objects without `required` array - SodaCL: Only add data quality tests for executable queries ### 0.10.14 — 2024-10-26 {#v0-10-14} Data Contract CLI now supports the Open Data Contract Standard (ODCS) v3.0.0. #### Added - `datacontract test` now also supports ODCS v3 data contract format - `datacontract export --format odcs_v3`: Export to Open Data Contract Standard v3.0.0 ([#460](https://github.com/datacontract/datacontract-cli/issues/460)) - `datacontract test` now also supports ODCS v3 anda Data Contract SQL quality checks on field and model level - Support for import from Iceberg table definitions. - Support for decimal logical type on avro export. - Support for custom Trino types #### Changed - `datacontract import --format odcs`: Now supports ODSC v3.0.0 files ([#474](https://github.com/datacontract/datacontract-cli/issues/474)) - `datacontract export --format odcs`: Now creates v3.0.0 Open Data Contract Standard files (alias to odcs_v3). Old versions are still available as format `odcs_v2`. ([#460](https://github.com/datacontract/datacontract-cli/issues/460)) #### Fixed - fix timestamp serialization from parquet -> duckdb ([#472](https://github.com/datacontract/datacontract-cli/issues/472)) ### 0.10.13 — 2024-09-20 {#v0-10-13} #### Added - `datacontract export --format data-caterer`: Export to [Data Caterer YAML](https://data.catering/setup/guide/scenario/data-generation/) #### Changed - `datacontract export --format jsonschema` handle optional and nullable fields ([#409](https://github.com/datacontract/datacontract-cli/issues/409)) - `datacontract import --format unity` handle nested and complex fields ([#420](https://github.com/datacontract/datacontract-cli/issues/420)) - `datacontract import --format spark` handle field descriptions ([#420](https://github.com/datacontract/datacontract-cli/issues/420)) - `datacontract export --format bigquery` handle bigqueryType ([#422](https://github.com/datacontract/datacontract-cli/issues/422)) #### Fixed - use correct float type with bigquery ([#417](https://github.com/datacontract/datacontract-cli/issues/417)) - Support DATACONTRACT_MANAGER_API_KEY - Some minor bug fixes ### 0.10.12 — 2024-09-08 {#v0-10-12} #### Added - Support for import of DBML Models ([#379](https://github.com/datacontract/datacontract-cli/issues/379)) - `datacontract export --format sqlalchemy`: Export to [SQLAlchemy ORM models](https://docs.sqlalchemy.org/en/20/orm/quickstart.html) ([#399](https://github.com/datacontract/datacontract-cli/issues/399)) - Support of varchar max length in Glue import ([#351](https://github.com/datacontract/datacontract-cli/issues/351)) - `datacontract publish` now also accepts the `DATACONTRACT_MANAGER_API_KEY` as an environment variable - Support required fields for Avro schema export ([#390](https://github.com/datacontract/datacontract-cli/issues/390)) - Support data type map in Spark import and export ([#408](https://github.com/datacontract/datacontract-cli/issues/408)) - Support of enum on export to avro - Support of enum title on avro import #### Changed - Deltalake is now using DuckDB's native deltalake support ([#258](https://github.com/datacontract/datacontract-cli/issues/258)). Extra deltalake removed. - When dumping to YAML (import) the alias name is used instead of the pythonic name. ([#373](https://github.com/datacontract/datacontract-cli/issues/373)) #### Fixed - Fix an issue where the datacontract cli fails if installed without any extras ([#400](https://github.com/datacontract/datacontract-cli/issues/400)) - Fix an issue where Glue database without a location creates invalid data contract ([#351](https://github.com/datacontract/datacontract-cli/issues/351)) - Fix bigint -> long data type mapping ([#351](https://github.com/datacontract/datacontract-cli/issues/351)) - Fix an issue where column description for Glue partition key column is ignored ([#351](https://github.com/datacontract/datacontract-cli/issues/351)) - Corrected name of table parameter for bigquery import ([#377](https://github.com/datacontract/datacontract-cli/issues/377)) - Fix a failed to connect to S3 Server ([#384](https://github.com/datacontract/datacontract-cli/issues/384)) - Fix a model bug mismatching with the specification (`definitions.fields`) ([#375](https://github.com/datacontract/datacontract-cli/issues/375)) - Fix array type management in Spark import ([#408](https://github.com/datacontract/datacontract-cli/issues/408)) ### 0.10.11 — 2024-08-08 {#v0-10-11} #### Added - Support data type map in Glue import. ([#340](https://github.com/datacontract/datacontract-cli/issues/340)) - Basic html export for new `keys` and `values` fields - Support for recognition of 1 to 1 relationships when exporting to DBML - Added support for arrays in JSON schema import ([#305](https://github.com/datacontract/datacontract-cli/issues/305)) #### Changed - Aligned JSON schema import and export of required properties - Change dbt importer to be more robust and customizable #### Fixed - Fix required field handling in JSON schema import - Fix an issue where the quality and definition `$ref` are not always resolved - Fix an issue where the JSON schema validation fails for a field with type `string` and format `uuid` - Fix an issue where common DBML renderers may not be able to parse parts of an exported file ### 0.10.10 — 2024-07-18 {#v0-10-10} #### Added - Add support for dbt manifest file ([#104](https://github.com/datacontract/datacontract-cli/issues/104)) - Fix import of pyspark for type-checking when pyspark isn't required as a module ([#312](https://github.com/datacontract/datacontract-cli/issues/312)) - Adds support for referencing fields within a definition ([#322](https://github.com/datacontract/datacontract-cli/issues/322)) - Add `map` and `enum` type for Avro schema import ([#311](https://github.com/datacontract/datacontract-cli/issues/311)) #### Fixed - Fix import of pyspark for type-checking when pyspark isn't required as a module ([#312](https://github.com/datacontract/datacontract-cli/issues/312))- `datacontract import --format spark`: Import from Spark tables ([#326](https://github.com/datacontract/datacontract-cli/issues/326)) - Fix an issue where specifying `glue_table` as parameter did not filter the tables and instead returned all tables from `source` database ([#333](https://github.com/datacontract/datacontract-cli/issues/333)) ### 0.10.9 — 2024-07-03 {#v0-10-9} #### Added - Add support for Trino ([#278](https://github.com/datacontract/datacontract-cli/issues/278)) - Spark export: add Spark StructType exporter ([#277](https://github.com/datacontract/datacontract-cli/issues/277)) - add `--schema` option for the `catalog` and `export` command to provide the schema also locally - Integrate support into the pre-commit workflow. For further details, please refer to the information provided [here](https://github.com/datacontract/datacontract-cli/blob/main/README.md#use-with-pre-commit). - Improved HTML export, supporting links, tags, and more - Add support for AWS SESSION_TOKEN ([#309](https://github.com/datacontract/datacontract-cli/issues/309)) #### Changed - Added array management on HTML export ([#299](https://github.com/datacontract/datacontract-cli/issues/299)) #### Fixed - Fix `datacontract import --format jsonschema` when description is missing ([#300](https://github.com/datacontract/datacontract-cli/issues/300)) - Fix `datacontract test` with case-sensitive Postgres table names ([#310](https://github.com/datacontract/datacontract-cli/issues/310)) ### 0.10.8 — 2024-06-19 {#v0-10-8} #### Added - `datacontract serve` start a local web server to provide a REST-API for the commands - Provide server for sql export for the appropriate schema ([#153](https://github.com/datacontract/datacontract-cli/issues/153)) - Add struct and array management to Glue export ([#271](https://github.com/datacontract/datacontract-cli/issues/271)) #### Changed - Introduced optional dependencies/extras for significantly faster installation times. ([#213](https://github.com/datacontract/datacontract-cli/issues/213)) - Added delta-lake as an additional optional dependency - support `GOOGLE_APPLICATION_CREDENTIALS` as variable for connecting to bigquery in `datacontract test` - better support bigqueries `type` attribute, don't assume all imported models are tables - added initial implementation of an importer from unity catalog (not all data types supported, yet) - added the importer factory. This refactoring aims to make it easier to create new importers and consequently the growth and maintainability of the project. ([#273](https://github.com/datacontract/datacontract-cli/issues/273)) #### Fixed - `datacontract export --format avro` fixed array structure ([#243](https://github.com/datacontract/datacontract-cli/issues/243)) ### 0.10.7 — 2024-05-31 {#v0-10-7} #### Added - Test data contract against dataframes / temporary views ([#175](https://github.com/datacontract/datacontract-cli/issues/175)) #### Fixed - AVRO export: Logical Types should be nested ([#233](https://github.com/datacontract/datacontract-cli/issues/233)) ### 0.10.6 — 2024-05-29 {#v0-10-6} #### Fixed - Fixed Docker build by removing msodbcsql18 dependency (temporary workaround) ### 0.10.5 — 2024-05-29 {#v0-10-5} #### Added - Added support for `sqlserver` ([#196](https://github.com/datacontract/datacontract-cli/issues/196)) - `datacontract export --format dbml`: Export to [Database Markup Language (DBML)](https://dbml.dbdiagram.io/home/) ([#135](https://github.com/datacontract/datacontract-cli/issues/135)) - `datacontract export --format avro`: Now supports config map on field level for logicalTypes and default values [Custom Avro Properties](https://github.com/datacontract/datacontract-cli/blob/main/README.md#custom-avro-properties) - `datacontract import --format avro`: Now supports importing logicalType and default definition on avro files [Custom Avro Properties](https://github.com/datacontract/datacontract-cli/blob/main/README.md#custom-avro-properties) - Support `config.bigqueryType` for testing BigQuery types - Added support for selecting specific tables in an AWS Glue `import` through the `glue-table` parameter ([#122](https://github.com/datacontract/datacontract-cli/issues/122)) #### Fixed - Fixed jsonschema export for models with empty object-typed fields ([#218](https://github.com/datacontract/datacontract-cli/issues/218)) - Fixed testing BigQuery tables with BOOL fields - `datacontract catalog` Show search bar also on mobile ### 0.10.4 — 2024-05-17 {#v0-10-4} #### Added - `datacontract catalog` Search - `datacontract publish`: Publish the data contract to the Data Mesh Manager - `datacontract import --format bigquery`: Import from BigQuery format ([#110](https://github.com/datacontract/datacontract-cli/issues/110)) - `datacontract export --format bigquery`: Export to BigQuery format ([#111](https://github.com/datacontract/datacontract-cli/issues/111)) - `datacontract export --format avro`: Now supports [Avro logical types](https://avro.apache.org/docs/1.11.1/specification/#logical-types) to better model date types. `date`, `timestamp`/`timestamp-tz` and `timestamp-ntz` are now mapped to the appropriate logical types. ([#141](https://github.com/datacontract/datacontract-cli/issues/141)) - `datacontract import --format jsonschema`: Import from JSON schema ([#91](https://github.com/datacontract/datacontract-cli/issues/91)) - `datacontract export --format jsonschema`: Improved export by exporting more additional information - `datacontract export --format html`: Added support for Service Levels, Definitions, Examples and nested Fields - `datacontract export --format go`: Export to go types format ### 0.10.3 — 2024-05-05 {#v0-10-3} #### Fixed - datacontract catalog: Add index.html to manifest ### 0.10.2 — 2024-05-05 {#v0-10-2} #### Added - Added import glue ([#166](https://github.com/datacontract/datacontract-cli/issues/166)) - Added test support for `azure` ([#146](https://github.com/datacontract/datacontract-cli/issues/146)) - Added support for `delta` tables on S3 ([#24](https://github.com/datacontract/datacontract-cli/issues/24)) - Added new command `datacontract catalog` that generates a data contract catalog with an `index.html` file. - Added field format information to HTML export #### Fixed - RDF Export: Fix error if owner is not a URI/URN ### 0.10.1 — 2024-04-19 {#v0-10-1} #### Fixed - Fixed docker columns ### 0.10.0 — 2024-04-19 {#v0-10-0} #### Added - Added timestamp when ah HTML export was created #### Fixed - Fixed export format **html** ### 0.9.9 — 2024-04-18 {#v0-9-9} #### Added - Added export format **html** ([#15](https://github.com/datacontract/datacontract-cli/issues/15)) - Added descriptions as comments to `datacontract export --format sql` for Databricks dialects - Added import of arrays in Avro import ### 0.9.8 — 2024-04-01 {#v0-9-8} #### Added - Added export format **great-expectations**: `datacontract export --format great-expectations` - Added gRPC support to OpenTelemetry integration for publishing test results - Added AVRO import support for namespace ([#121](https://github.com/datacontract/datacontract-cli/issues/121)) - Added handling for optional fields in avro import ([#112](https://github.com/datacontract/datacontract-cli/issues/112)) - Added Databricks SQL dialect for `datacontract export --format sql` #### Fixed - Use `sql_type_converter` to build checks. - Fixed AVRO import when doc is missing ([#121](https://github.com/datacontract/datacontract-cli/issues/121)) ### 0.9.7 — 2024-03-15 {#v0-9-7} #### Added - Added option publish test results to **OpenTelemetry**: `datacontract test --publish-to-opentelemetry` - Added export format **protobuf**: `datacontract export --format protobuf` - Added export format **terraform**: `datacontract export --format terraform` (limitation: only works for AWS S3 right now) - Added export format **sql**: `datacontract export --format sql` - Added export format **sql-query**: `datacontract export --format sql-query` - Added export format **avro-idl**: `datacontract export --format avro-idl`: Generates an Avro IDL file containing records for each model. - Added new command **changelog**: `datacontract changelog datacontract1.yaml datacontract2.yaml` will now generate a changelog based on the changes in the data contract. This will be useful for keeping track of changes in the data contract over time. - Added extensive linting on data contracts. `datacontract lint` will now check for a variety of possible errors in the data contract, such as missing descriptions, incorrect references to models or fields, nonsensical constraints, and more. - Added importer for avro schemas. `datacontract import --format avro` will now import avro schemas into a data contract. #### Fixed - Fixed a bug where the export to YAML always escaped the unicode characters. ### 0.9.6-2 — 2024-03-04 {#v0-9-6-2} #### Added - test kafka for avro messages - added export format **avro**: `datacontract export --format avro` ### 0.9.6 — 2024-03-04 {#v0-9-6} This is a huge step forward, we now support testing Kafka messages. We start with JSON messages and avro, and Protobuf will follow. #### Added - test kafka for JSON messages - added import format **sql**: `datacontract import --format sql` ([#51](https://github.com/datacontract/datacontract-cli/issues/51)) - added export format **dbt-sources**: `datacontract export --format dbt-sources` - added export format **dbt-staging-sql**: `datacontract export --format dbt-staging-sql` - added export format **rdf**: `datacontract export --format rdf` ([#52](https://github.com/datacontract/datacontract-cli/issues/52)) - added command `datacontract breaking` to detect breaking changes in between two data contracts. ### 0.9.5 — 2024-02-22 {#v0-9-5} #### Added - export to dbt models ([#37](https://github.com/datacontract/datacontract-cli/issues/37)). - export to ODCS ([#49](https://github.com/datacontract/datacontract-cli/issues/49)). - test - show a test summary table. - lint - Support local schema ([#46](https://github.com/datacontract/datacontract-cli/issues/46)). ### 0.9.4 — 2024-02-18 {#v0-9-4} #### Added - Support for Postgres - Support for Databricks ### 0.9.3 — 2024-02-10 {#v0-9-3} #### Added - Support for BigQuery data connection - Support for multiple models with S3 #### Fixed - Fix Docker images. Disable builds for linux/amd64. ### 0.9.2 — 2024-01-31 {#v0-9-2} #### Added - Publish to Docker Hub ### [0.9.0] - 2024-01-26 - BREAKING This is a breaking change (we are still on a 0.x.x version). The project migrated from Golang to Python. The Golang version can be found at [cli-go](https://github.com/datacontract/cli-go) #### Added - `test` Support to directly run tests and connect to data sources defined in servers section. - `test` generated schema tests from the model definition. - `test --publish URL` Publish test results to a server URL. - `export` now exports the data contract so format jsonschema and sodacl. #### Changed - The `--file` option removed in favor of a direct argument.: Use `datacontract test datacontract.yaml` instead of `datacontract test --file datacontract.yaml`. #### Removed - `model` is now part of `export` - `quality` is now part of `export` - Temporary Removed: `diff` needs to be migrated to Python. - Temporary Removed: `breaking` needs to be migrated to Python. - Temporary Removed: `inline` needs to be migrated to Python. ### 0.6.0 {#v0-6-0} #### Added - Support local json schema in lint command. - Update to specification 0.9.2. ### 0.5.3 {#v0-5-3} #### Fixed - Fix format flag bug in model (print) command. ### 0.5.2 {#v0-5-2} #### Changed - Log to STDOUT. - Rename `model` command parameter, `type` -> `format`. ### 0.5.1 {#v0-5-1} #### Removed - Remove `schema` command. #### Fixed - Fix documentation. - Security update of x/sys. ### 0.5.0 {#v0-5-0} #### Added - Adapt Data Contract Specification in version 0.9.2. - Use `models` section for `diff`/`breaking`. - Add `model` command. - Let `inline` print to STDOUT instead of overwriting datacontract file. - Let `quality` write input from STDIN if present. ### 0.4.0 {#v0-4-0} #### Added - Basic implementation of `test` command for Soda Core. #### Changed - Change package structure to allow usage as library. ### 0.3.2 {#v0-3-2} #### Fixed - Fix field parsing for dbt models, affects stability of `diff`/`breaking`. ### 0.3.1 {#v0-3-1} #### Fixed - Fix comparing order of contracts in `diff`/`breaking`. ### 0.3.0 {#v0-3-0} #### Added - Handle non-existent schema specification when using `diff`/`breaking`. - Resolve local and remote resources such as schema specifications when using "$ref: ..." notation. - Implement `schema` command: prints your schema. - Implement `quality` command: prints your quality definitions. - Implement the `inline` command: resolves all references using the "$ref: ..." notation and writes them to your data contract. #### Changed - Allow remote and local location for all data contract inputs (`--file`, `--with`). ### 0.2.0 {#v0-2-0} #### Added - Add `diff` command for dbt schema specification. - Add `breaking` command for dbt schema specification. #### Changed - Suggest a fix during `init` when the file already exists. - Rename `validate` command to `lint`. #### Removed - Remove `check-compatibility` command. #### Fixed - Improve usage documentation. ### 0.1.1 {#v0-1-1} #### Added - Initial release. --- ## Contributing The Data Contract CLI is open source under the [MIT license](https://github.com/datacontract/datacontract-cli/blob/main/LICENSE) and contributions are welcome — a new importer or exporter, support for another data source, a bug fix, or a documentation improvement. - **Source code**: [github.com/datacontract/datacontract-cli](https://github.com/datacontract/datacontract-cli) - **Issues**: [github.com/datacontract/datacontract-cli/issues](https://github.com/datacontract/datacontract-cli/issues) - **Questions and discussion**: [Community Slack](https://datacontract.com/slack) For anything larger than a bug fix, open an issue first so the approach can be discussed before you write the code. ### Development setup The project targets Python 3.10–3.14 and is developed against **3.11**. [uv](https://docs.astral.sh/uv/) is the recommended toolchain. ```bash gh repo clone datacontract/datacontract-cli cd datacontract-cli uv python pin 3.11 uv sync --all-extras --dev ``` Or with a plain virtual environment: ```bash python3.11 -m venv venv source venv/bin/activate pip install --upgrade pip setuptools wheel pip install -e '.[dev]' ``` Install the pre-commit hooks so linting and formatting run on every commit: ```bash pre-commit install ``` ### Running the tests ```bash # The full suite (slow tests excluded by default) uv run pytest # In parallel uv run pytest -n 8 # A single file or test uv run pytest tests/test_export_sql.py uv run pytest tests/test_export_sql.py::test_export_sql ``` Tests that pull large containers are marked `slow` and are deselected by default. Run them explicitly: ```bash uv run pytest -m slow # only the slow tests uv run pytest -m "slow or not slow" # everything ``` Many tests use [testcontainers](https://testcontainers.com/), so a running Docker daemon is needed for the data source tests. :::tip Tests describe **expected** behavior, not current behavior. Write the test for what the code *should* do; if it fails, fix the code under test rather than the test. ::: ### Linting and formatting The project uses [ruff](https://docs.astral.sh/ruff/) with a 120-character line length. CI fails on unformatted code. ```bash uv run ruff check --fix uv run ruff format # or run every pre-commit hook at once pre-commit run --all-files ``` ### Documentation The documentation site is a [Docusaurus](https://docusaurus.io/) project in `docs/`: ```bash cd docs npm install npm start ``` Some pages contain `AUTOGENERATED` blocks that must be regenerated rather than edited by hand: ```bash python update_export_examples.py # command + output examples on the export pages python update_import_examples.py # command + output examples on the import pages python update_reference_types.py # logicalType -> native type tables on the reference pages python lint_examples.py # validates every contract under examples/ (also runs in CI) ``` The Commands section under `docs/docs/commands/` and the Release Notes page are generated rather than committed: `npm start` and `npm run build` rebuild them from the CLI's `--help` output and from `CHANGELOG.md` first. Nothing to regenerate by hand, and nothing to keep in sync in a pull request — `_category_.json` is the only file under `docs/docs/commands/` that is checked in. Because the Commands section is rendered from the CLI, the docs build needs it installed (`uv pip install -e '.[dev]'`). Prose guides belong in `docs/docs/imports`, `exports`, or `testing`, and each one links to its generated command page (and back), which `tests/test_docs_commands.py` enforces. Every importer and exporter is listed three times — in the sidebar, in the card grid on the index page, and in the subcommand table of the generated `commands/import/index.md`. All three lists are alphabetical by the label that is **rendered**, not by file name (`Import: AWS Glue` sorts under A, not G). This is enforced by `tests/test_docs_ordering.py`. ### Opening a pull request Branch off `main` and make sure the checklist from the pull request template passes: - [ ] Tests pass (`uv run pytest`) - [ ] Code formatted (`uv run ruff check --fix && uv run ruff format`) - [ ] Docs updated (if relevant) - [ ] `CHANGELOG.md` entry added `CHANGELOG.md` entries are one line each, stating what changed from a user's point of view — not how or why. Append the issue number if there is one: ```markdown - Support for the `exclusiveMinimum` logical type option (#1234) ``` CI runs linting, the test suite on Python 3.10–3.14, the slow tests, and an integration job that installs the built wheel and exercises the CLI end to end. --- ## FAQ ### Which contract format should I use? [ODCS](./open-data-contract-standard.md). The older Data Contract Specification (`models`/`fields`) is still read, but write new contracts in ODCS — see [Migrate from DCS to ODCS](./migrate-dcs-to-odcs.md). ### I imported a contract and `test` immediately fails on types. Why? On the backends with catalog introspection, the declared `physicalType` is compared against the column's real native type — a stricter check than the portable `logicalType` used elsewhere. See [Types](./schema.md#types) and the [Data Source Reference](./reference/index.md#how-data-types-work). ### I set `isNullable: false` but nothing is checked. The CLI reads `required`, not `isNullable`. Write `required: true`. See [What is not checked](./schema.md#what-is-not-checked). ### Where do credentials go? Never in the contract. Connection details live in `servers`; credentials come from environment variables or a `.env` file in the working directory. Each [reference page](./reference/index.md) lists the variables its source expects. ### How do I make CI fail on quality warnings? `datacontract ci --fail-on warning`. The default is `error`; `never` always exits 0. See [`ci`](./commands/ci.md). ### How do I run only some checks? `--checks properties`, `quality`, `slaProperties`, or `custom`, comma-separated. Omit it to run everything. See [Test your Data](./testing/index.md). ### Which optional dependency do I need? One per data source — `datacontract-cli[snowflake]`, `[databricks]`, `[postgres]`, … — or `[all]` for everything. See [Installation](./installation.md#optional-dependencies-extras). ### TLS fails behind a corporate proxy. Use `datacontract --system-truststore …` to verify against the operating system's trust store instead of the bundled CA certificates, or set `DATACONTRACT_SYSTEM_TRUSTSTORE=1`. ### Can I use it without the CLI? Yes — as a [Python library](./python-library.md), or as a REST service with [`datacontract api`](./api.md). ### Something is missing or broken. Open an [issue on GitHub](https://github.com/datacontract/datacontract-cli/issues) or ask in the [community Slack](https://datacontract.com/slack). To add it yourself, see [Contributing](./contributing.md).