Repository: TobikoData/sqlmesh
Pull request #2564 (2024-05-04 18:12:57)

Feat: provide helpful error for invalid model defaults keys


Patch:
diff --git a/docs/reference/model_configuration.md b/docs/reference/model_configuration.md
index 7678ef11..7dab2a3b 100644
--- a/docs/reference/model_configuration.md
+++ b/docs/reference/model_configuration.md
@@ -43,7 +43,6 @@ The SQLMesh project-level `model_defaults` key supports the following options, d
 - cron
 - owner
 - start
-- end
 - storage_format
 
 ## Model kind properties
diff --git a/sqlmesh/core/config/loader.py b/sqlmesh/core/config/loader.py
index 95469c95..435dee7e 100644
--- a/sqlmesh/core/config/loader.py
+++ b/sqlmesh/core/config/loader.py
@@ -7,6 +7,7 @@ from pathlib import Path
 from sqlglot.helper import ensure_list
 
 from sqlmesh.core import constants as c
+from sqlmesh.core.config.model import ModelDefaultsConfig
 from sqlmesh.core.config.root import Config
 from sqlmesh.utils import env_vars, merge_dicts, sys_path
 from sqlmesh.utils.errors import ConfigError
@@ -113,9 +114,18 @@ def load_config_from_paths(
             "SQLMesh config could not be found. Point the cli to the right path with `sqlmesh -p`. If you haven't set up SQLMesh, run `sqlmesh init`."
         )
 
-    no_dialect_err_msg = "Default model SQL dialect is a required configuration parameter. Set it in the `model_defaults` `dialect` key in your config file."
+    non_python_config_dict = merge_dicts(*non_python_configs)
+
+    supported_model_defaults = ModelDefaultsConfig.all_fields()
+    for default in non_python_config_dict.get("model_defaults", {}):
+        if not default in supported_model_defaults:
+            raise ConfigError(
+                f"'{default}' is not a valid model default configuration key. Please remove it from the `model_defaults` specification in your config file."
+            )
+
+    non_python_config = config_type.parse_obj(non_python_config_dict)
 
-    non_python_config = config_type.parse_obj(merge_dicts(*non_python_configs))
+    no_dialect_err_msg = "Default model SQL dialect is a required configuration parameter. Set it in the `model_defaults` `dialect` key in your config file."
     if python_config:
         model_defaults = python_config.model_defaults
         if model_defaults.dialect is None:


Test patch:
diff --git a/tests/core/test_config.py b/tests/core/test_config.py
index ffd13162..9778ad01 100644
--- a/tests/core/test_config.py
+++ b/tests/core/test_config.py
@@ -214,6 +214,29 @@ config = Config(default_connection=DuckDBConnectionConfig())
         load_config_from_paths(Config, project_paths=[tmp_path / "config.py"])
 
 
+def test_load_config_bad_model_default_key(tmp_path):
+    create_temp_file(
+        tmp_path,
+        pathlib.Path("config.yaml"),
+        """
+gateways:
+    local:
+        connection:
+            type: duckdb
+            database: db.db
+
+model_defaults:
+    dialect: ''
+    test: 1
+""",
+    )
+
+    with pytest.raises(
+        ConfigError, match=r"^'test' is not a valid model default configuration key.*"
+    ):
+        load_config_from_paths(Config, project_paths=[tmp_path / "config.yaml"])
+
+
 def test_load_config_unsupported_extension(tmp_path):
     config_path = tmp_path / "config.txt"
     config_path.touch()