Repository: datacontract/datacontract-cli

Issue:
export dbt-staging-sql error on multi models contracts
When there is more than one model in contract, `datacontract export --format dbt-staging-sql --model name` is not usable.

With correct model name:
```bash
$ datacontract export ./datacontract_50D_50D.yml --format dbt-staging-sql --model PNA
Export to dbt-staging-sql currently only works with exactly one model in the data contract.Please specify the model name.
```

With an incorrect model name:
```bash
$ datacontract export ./datacontract_50D_50D.yml --format dbt-staging-sql --model models.PNA
RuntimeError: Model model.PNA not found in the data contract. Available models: ['FAC', 'PNA']
```

Hints from issue comments:
@jochenchrist: Included in v0.10.18

Solution patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 731b771..c6ed473 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -27,6 +27,7 @@ jobs:
         uses: actions/setup-python@v5
         with:
           python-version: ${{matrix.python-version}}
+          cache: 'pip' # caching pip dependencies
       - name: Install msodbcsql18
         run: |
           sudo apt-get update
@@ -56,6 +57,7 @@ jobs:
         uses: actions/setup-python@v5
         with:
           python-version: ${{matrix.python-version}}
+          cache: 'pip' # caching pip dependencies
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 9b765e6..a53ead3 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -55,6 +55,7 @@ jobs:
         uses: actions/setup-python@v5
         with:
           python-version: ${{matrix.python-version}}
+          cache: 'pip' # caching pip dependencies
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip setuptools wheel build && \
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d43caeb..ba53f52 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Fixed
 - SQL Server: cannot escape reserved word on model (#557)
+- Export dbt-staging-sql error on multi models contracts (#587) 
 
 
 ## [0.10.16] - 2024-12-19
diff --git a/README.md b/README.md
index 0c36819..b32f0ca 100644
--- a/README.md
+++ b/README.md
@@ -512,7 +512,6 @@ datacontract.yaml
 servers:
   production:
     type: databricks
-    host: dbc-abcdefgh-1234.cloud.databricks.com
     catalog: acme_catalog_prod
     schema: orders_latest
 models:
@@ -523,10 +522,11 @@ models:
 
 #### Environment Variables
 
-| Environment Variable                         | Example                              | Description                                           |
-|----------------------------------------------|--------------------------------------|-------------------------------------------------------|
-| `DATACONTRACT_DATABRICKS_TOKEN`              | `dapia00000000000000000000000000000` | The personal access token to authenticate             |
-| `DATACONTRACT_DATABRICKS_HTTP_PATH`          | `/sql/1.0/warehouses/b053a3ffffffff` | The HTTP path to the SQL warehouse or compute cluster |
+| Environment Variable                      | Example                              | Description                                               |
+|-------------------------------------------|--------------------------------------|-----------------------------------------------------------|
+| `DATACONTRACT_DATABRICKS_TOKEN`           | `dapia00000000000000000000000000000` | The personal access token to authenticate                 |
+| `DATACONTRACT_DATABRICKS_HTTP_PATH`       | `/sql/1.0/warehouses/b053a3ffffffff` | The HTTP path to the SQL warehouse or compute cluster     |
+| `DATACONTRACT_DATABRICKS_SERVER_HOSTNAME` | `dbc-abcdefgh-1234.cloud.databricks.com` | The host name of the SQL warehouse or compute cluster |
 
 
 ### Databricks (programmatic)
diff --git a/datacontract/cli.py b/datacontract/cli.py
index 7a2df93..b1cd7e2 100644
--- a/datacontract/cli.py
+++ b/datacontract/cli.py
@@ -139,6 +139,10 @@ def test(
         ),
     ] = False,
     logs: Annotated[bool, typer.Option(help="Print logs")] = False,
+    ssl_verification: Annotated[
+        bool,
+        typer.Option(help="SSL verification when publishing the test results."),
+    ] = True,
 ):
     """
     Run schema and quality tests on configured servers.
@@ -315,6 +319,10 @@ def publish(
         str,
         typer.Option(help="The location (url or path) of the Data Contract Specification JSON Schema"),
     ] = DEFAULT_DATA_CONTRACT_SCHEMA_URL,
+    ssl_verification: Annotated[
+        bool,
+        typer.Option(help="SSL verification when publishing the data contract."),
+    ] = True,
 ):
     """
     Publish the data contract to the Data Mesh Manager.
@@ -323,6 +331,7 @@ def publish(
         data_contract_specification=DataContract(
             data_contract_file=location, schema_location=schema
         ).get_data_contract_specification(),
+        ssl_verification=ssl_verification,
     )
 
 
diff --git a/datacontract/engines/soda/check_soda_execute.py b/datacontract/engines/soda/check_soda_execute.py
index 62011f7..4447a22 100644
--- a/datacontract/engines/soda/check_soda_execute.py
+++ b/datacontract/engines/soda/check_soda_execute.py
@@ -25,6 +25,7 @@ def check_soda_execute(run: Run, data_contract: DataContractSpecification, serve
 
     if server.type in ["s3", "gcs", "azure", "local"]:
         if server.format in ["json", "parquet", "csv", "delta"]:
+            run.log_info(f"Configuring engine soda-core to connect to {server.type} {server.format} with duckdb")
             con = get_duckdb_connection(data_contract, server, run)
             scan.add_duckdb_connection(duckdb_connection=con, data_source_name=server.type)
             scan.set_data_source_name(server.type)
@@ -54,11 +55,12 @@ def check_soda_execute(run: Run, data_contract: DataContractSpecification, serve
         scan.set_data_source_name(server.type)
     elif server.type == "databricks":
         if spark is not None:
-            logging.info("Use Spark to connect to data source")
+            run.log_info("Connecting to databricks via spark")
             scan.add_spark_session(spark, data_source_name=server.type)
             scan.set_data_source_name(server.type)
             spark.sql(f"USE {server.catalog}.{server.schema_}")
         else:
+            run.log_info("Connecting to databricks directly")
             soda_configuration_str = to_databricks_soda_configuration(server)
             scan.add_configuration_yaml_str(soda_configuration_str)
             scan.set_data_source_name(server.type)
diff --git a/datacontract/engines/soda/connections/databricks.py b/datacontract/engines/soda/connections/databricks.py
index d15748f..e3e4bcb 100644
--- a/datacontract/engines/soda/connections/databricks.py
+++ b/datacontract/engines/soda/connections/databricks.py
@@ -4,15 +4,24 @@ import yaml
 
 
 def to_databricks_soda_configuration(server):
+    token = os.getenv("DATACONTRACT_DATABRICKS_TOKEN")
+    if token is None:
+        raise ValueError("DATACONTRACT_DATABRICKS_TOKEN environment variable is not set")
+    http_path = os.getenv("DATACONTRACT_DATABRICKS_HTTP_PATH")
+    host = server.host
+    if host is None:
+        host = os.getenv("DATACONTRACT_DATABRICKS_SERVER_HOSTNAME")
+    if host is None:
+        raise ValueError("DATACONTRACT_DATABRICKS_SERVER_HOSTNAME environment variable is not set")
     soda_configuration = {
         f"data_source {server.type}": {
             "type": "spark",
             "method": "databricks",
-            "host": server.host,
+            "host": host,
             "catalog": server.catalog,
             "schema": server.schema_,
-            "http_path": os.getenv("DATACONTRACT_DATABRICKS_HTTP_PATH"),
-            "token": os.getenv("DATACONTRACT_DATABRICKS_TOKEN"),
+            "http_path": http_path,
+            "token": token,
         }
     }
 
diff --git a/datacontract/export/dbt_converter.py b/datacontract/export/dbt_converter.py
index a1dfced..48c76c7 100644
--- a/datacontract/export/dbt_converter.py
+++ b/datacontract/export/dbt_converter.py
@@ -39,13 +39,6 @@ def to_dbt_models_yaml(data_contract_spec: DataContractSpecification):
 
 
 def to_dbt_staging_sql(data_contract_spec: DataContractSpecification, model_name: str, model_value: Model) -> str:
-    if data_contract_spec.models is None or len(data_contract_spec.models.items()) != 1:
-        print(
-            "Export to dbt-staging-sql currently only works with exactly one model in the data contract."
-            "Please specify the model name."
-        )
-        return ""
-
     id = data_contract_spec.id
     columns = []
     for field_name, field in model_value.fields.items():
diff --git a/datacontract/integration/datamesh_manager.py b/datacontract/integration/datamesh_manager.py
index ffda5f4..6b17e5b 100644
--- a/datacontract/integration/datamesh_manager.py
+++ b/datacontract/integration/datamesh_manager.py
@@ -28,7 +28,7 @@ def publish_test_results_to_datamesh_manager(run: Run, publish_url: str):
         headers = {"Content-Type": "application/json", "x-api-key": api_key}
         request_body = run.model_dump_json()
         # print("Request Body:", request_body)
-        response = requests.post(url, data=request_body, headers=headers)
+        response = requests.post(url, data=request_body, headers=headers, verify=False,)
         # print("Status Code:", response.status_code)
         # print("Response Body:", response.text)
         if response.status_code != 200:
@@ -39,9 +39,12 @@ def publish_test_results_to_datamesh_manager(run: Run, publish_url: str):
         run.log_error(f"Failed publishing test results. Error: {str(e)}")
 
 
-def publish_data_contract_to_datamesh_manager(data_contract_specification: DataContractSpecification):
+def publish_data_contract_to_datamesh_manager(data_contract_specification: DataContractSpecification, ssl_verification: bool):
     try:
         api_key = os.getenv("DATAMESH_MANAGER_API_KEY")
+        host = "https://api.datamesh-manager.com"
+        if os.getenv("DATAMESH_MANAGER_HOST") is not None:
+            host = os.getenv("DATAMESH_MANAGER_HOST")
         if api_key is None:
             api_key = os.getenv("DATACONTRACT_MANAGER_API_KEY")
         if api_key is None:
@@ -51,12 +54,13 @@ def publish_data_contract_to_datamesh_manager(data_contract_specification: DataC
         headers = {"Content-Type": "application/json", "x-api-key": api_key}
         spec = data_contract_specification
         id = spec.id
-        url = "https://api.datamesh-manager.com/api/datacontracts/{0}".format(id)
+        url = f"{host}/api/datacontracts/{id}"
         request_body = spec.model_dump_json().encode("utf-8")
         response = requests.put(
             url=url,
             data=request_body,
             headers=headers,
+            verify=ssl_verification,
         )
         if response.status_code != 200:
             print(f"Error publishing data contract to Data Mesh Manager: {response.text}")
diff --git a/datacontract/lint/resolve.py b/datacontract/lint/resolve.py
index 8947ff1..3ed8a84 100644
--- a/datacontract/lint/resolve.py
+++ b/datacontract/lint/resolve.py
@@ -202,9 +202,12 @@ def _resolve_data_contract_from_str(
     yaml_dict = _to_yaml(data_contract_str)
 
     if is_open_data_contract_standard(yaml_dict):
+        logging.info("Importing ODCS v3")
         # if ODCS, then validate the ODCS schema and import to DataContractSpecification directly
         data_contract_specification = DataContractSpecification(dataContractSpecification="1.1.0")
         return import_odcs_v3_from_str(data_contract_specification, source_str=data_contract_str)
+    else:
+        logging.info("Importing DCS")
 
     _validate_data_contract_specification_schema(yaml_dict, schema_location)
     data_contract_specification = yaml_dict


Test patch:
diff --git a/tests/test_export_dbt_staging_sql.py b/tests/test_export_dbt_staging_sql.py
index 06b6856..f4c1d07 100644
--- a/tests/test_export_dbt_staging_sql.py
+++ b/tests/test_export_dbt_staging_sql.py
@@ -14,9 +14,11 @@ def test_cli():
         app,
         [
             "export",
-            "./fixtures/export/datacontract.yaml",
+            "./fixtures/dbt/export/datacontract.yaml",
             "--format",
             "dbt-staging-sql",
+            "--model",
+            "orders",
         ],
     )
     print(result.stdout)
@@ -24,12 +26,13 @@ def test_cli():
 
 
 def test_to_dbt_staging():
-    data_contract = DataContractSpecification.from_file("fixtures/export/datacontract.yaml")
+    data_contract = DataContractSpecification.from_file("fixtures/dbt/export/datacontract.yaml")
     expected = """
 select 
     order_id,
     order_total,
-    order_status
+    order_status,
+    user_id
 from {{ source('orders-unit-test', 'orders') }}
 """