Repository: fal-ai/dbt-fal

Issue:
[DevEx] Better error messaging when a dbt run is missing 
When the developer doesn't run dbt run but have models that have fal scripts those models dont have any run results.. and we error but not very descriptive, I think if we dont find run results 99% of the time its a wrong `dbt run`.  


Discussion:

Resolving patch:
diff --git a/src/faldbt/parse.py b/src/faldbt/parse.py
--- a/src/faldbt/parse.py
+++ b/src/faldbt/parse.py
@@ -25,7 +25,7 @@ def _load_file_contents(path: str, strip: bool = True) -> str:
     return to_return
 
 
-def _load_yaml(path):
+def _read_yaml(path):
     contents = _load_file_contents(path)
     return load_yaml_text(contents)
 
@@ -45,7 +45,8 @@ def _get_all_model_config(project_root, project_dict):
             ## look at all of them find the ones that has model in them
             ## and keep remembering it
             lambda model_path: glob.glob(
-                os.path.join(project_root, model_path, "**.yml"), recursive=True
+                os.path.join(project_root, model_path, "**.yml"),
+                recursive=True,
             ),
             project_dict["source-paths"],
         )
@@ -57,7 +58,12 @@ def parse_project(project_dir: str, profiles_dir: str, keyword: str):
     scripts = glob.glob(os.path.join(project_dir, "**.py"), recursive=True)
     model_config_paths = _get_all_model_config(project_dir, project_dict)
     target_path = os.path.join(project_dir, project_dict["target-path"])
-    run_result_path = os.path.join(target_path, "run_results.json")
+
+    run_results_path = os.path.join(target_path, "run_results.json")
+    try:
+        run_results = _read_json(run_results_path)
+    except IOError as e:
+        raise FalParseError("Did you forget to run dbt run?") from e
 
     config = lib.get_dbt_config(project_dir)
     lib.register_adapters(config)
@@ -66,7 +72,7 @@ def parse_project(project_dir: str, profiles_dir: str, keyword: str):
     dbt.tracking.initialize_tracking(profiles_dir)
 
     manifest = lib.parse_to_manifest(config)
-    run_result_artifact = RunResultsArtifact(**_read_json(run_result_path))
+    run_result_artifact = RunResultsArtifact(**run_results)
     dbtmanifest = DbtManifest(nativeManifest=manifest)
 
     models = dbtmanifest.get_models()
@@ -74,7 +80,7 @@ def parse_project(project_dir: str, profiles_dir: str, keyword: str):
         map(lambda result: [result["unique_id"], result["status"]], run_result_artifact)
     )
     for model in models:
-        model.status = status_map[model.unique_id]
+        model.status = status_map.get(model.unique_id)
 
     return DbtProject(
         name=project_dict["name"],
@@ -90,14 +96,14 @@ def parse_project(project_dir: str, profiles_dir: str, keyword: str):
 def _get_project_dict(project_dir):
     project_yaml_filepath = os.path.join(project_dir, "dbt_project.yml")
 
-    if not os.path.lexists(project_yaml_filepath):
+    if not os.path.exists(project_yaml_filepath):
         raise FalParseError(
             "no dbt_project.yml found at expected path {}".format(project_yaml_filepath)
         )
 
-    project_dict = _load_yaml(project_yaml_filepath)
+    project_dict = _read_yaml(project_yaml_filepath)
 
     if not isinstance(project_dict, dict):
-        raise FalParseError("dbt_project.yml does not parse to a dictionary")
+        raise FalParseError("dbt_project.yml formatting is wrong")
 
     return project_dict
diff --git a/src/faldbt/project.py b/src/faldbt/project.py
--- a/src/faldbt/project.py
+++ b/src/faldbt/project.py
@@ -70,24 +70,20 @@ def find_model_location(self, model: DbtModel) -> List[str]:
         model_node = self.manifest.nodes[model.unique_id]
         return model_node.relation_name.replace("`", "")
 
-    def changed_model_names(self) -> List[str]:
-        return list(
-            map(
-                lambda result: result["unique_id"].split(".")[-1],
-                self.run_result.results,
-            )
-        )
+    def changed_models_ids(self) -> List[str]:
+        return list(map(lambda res: res["unique_id"], self.run_result.results))
 
     def get_models_with_keyword(self, keyword) -> List[DbtModel]:
         return list(filter(lambda model: keyword in model.meta, self.models))
 
     def get_filtered_models(self, all):
+        changed_models_ids = self.changed_models_ids()
         filtered_models: List[DbtModel] = []
+
         for node in self.get_models_with_keyword(self.keyword):
             if all:
                 filtered_models.append(node)
-            elif node.name in self.changed_model_names():
+            elif node.unique_id in changed_models_ids:
                 filtered_models.append(node)
-            else:
-                continue
+
         return filtered_models


Test patch: