Repository: dbt-labs/dbt-core

Issue:
Support partial parsing with env_vars in metrics definition
Because the metrics feature and the support for partially parsing yaml entries with changed env vars happened in parallel, there is no code in core/dbt/parser/partial.py to re-parse on env var changes for metrics entries.


Discussion:

Resolving patch:
diff --git a/core/dbt/parser/partial.py b/core/dbt/parser/partial.py
--- a/core/dbt/parser/partial.py
+++ b/core/dbt/parser/partial.py
@@ -693,21 +693,31 @@ def handle_schema_file_changes(self, schema_file, saved_yaml_dict, new_yaml_dict
                     continue
                 elem = self.get_schema_element(new_yaml_dict[dict_key], name)
                 if elem:
-                    self.delete_schema_exposure(schema_file, exposure)
-                    self.merge_patch(schema_file, dict_key, exposure)
+                    self.delete_schema_exposure(schema_file, elem)
+                    self.merge_patch(schema_file, dict_key, elem)
 
         # metrics
+        dict_key = 'metrics'
         metric_diff = self.get_diff_for('metrics', saved_yaml_dict, new_yaml_dict)
         if metric_diff['changed']:
             for metric in metric_diff['changed']:
                 self.delete_schema_metric(schema_file, metric)
-                self.merge_patch(schema_file, 'metrics', metric)
+                self.merge_patch(schema_file, dict_key, metric)
         if metric_diff['deleted']:
             for metric in metric_diff['deleted']:
                 self.delete_schema_metric(schema_file, metric)
         if metric_diff['added']:
             for metric in metric_diff['added']:
-                self.merge_patch(schema_file, 'metrics', metric)
+                self.merge_patch(schema_file, dict_key, metric)
+        # Handle schema file updates due to env_var changes
+        if dict_key in env_var_changes and dict_key in new_yaml_dict:
+            for name in env_var_changes[dict_key]:
+                if name in metric_diff['changed_or_deleted_names']:
+                    continue
+                elem = self.get_schema_element(new_yaml_dict[dict_key], name)
+                if elem:
+                    self.delete_schema_metric(schema_file, elem)
+                    self.merge_patch(schema_file, dict_key, elem)
 
     # Take a "section" of the schema file yaml dictionary from saved and new schema files
     # and determine which parts have changed


Test patch:
diff --git a/test/integration/068_partial_parsing_tests/test-files/env_var_metrics.yml b/test/integration/068_partial_parsing_tests/test-files/env_var_metrics.yml
new file mode 100644
--- /dev/null
+++ b/test/integration/068_partial_parsing_tests/test-files/env_var_metrics.yml
@@ -0,0 +1,30 @@
+version: 2
+
+metrics:
+
+  - model: "ref('people')"
+    name: number_of_people
+    description: Total count of people
+    label: "Number of people"
+    type: count
+    sql: "*"
+    timestamp: created_at
+    time_grains: [day, week, month]
+    dimensions:
+      - favorite_color
+      - loves_dbt
+    meta:
+        my_meta: '{{ env_var("ENV_VAR_METRICS") }}'
+
+  - model: "ref('people')"
+    name: collective_tenure
+    description: Total number of years of team experience
+    label: "Collective tenure"
+    type: sum
+    sql: tenure
+    timestamp: created_at
+    time_grains: [day]
+    filters:
+      - field: loves_dbt
+        operator: is
+        value: 'true'
diff --git a/test/integration/068_partial_parsing_tests/test_pp_vars.py b/test/integration/068_partial_parsing_tests/test_pp_vars.py
--- a/test/integration/068_partial_parsing_tests/test_pp_vars.py
+++ b/test/integration/068_partial_parsing_tests/test_pp_vars.py
@@ -221,6 +221,23 @@ def test_postgres_env_vars_models(self):
         expected_env_vars = {'models': {'model_one': ['TEST_SCHEMA_VAR', 'ENV_VAR_COLOR'], 'model_color': ['ENV_VAR_ENABLED']}, 'exposures': {'proxy_for_dashboard': ['ENV_VAR_OWNER']}}
         self.assertEqual(expected_env_vars, schema_file.env_vars)
 
+        # Add a metrics file with env_vars
+        os.environ['ENV_VAR_METRICS'] = 'TeStInG'
+        self.copy_file('test-files/people.sql', 'models/people.sql')
+        self.copy_file('test-files/env_var_metrics.yml', 'models/metrics.yml')
+        results = self.run_dbt(["run"])
+        manifest = get_manifest()
+        self.assertIn('ENV_VAR_METRICS', manifest.env_vars)
+        self.assertEqual(manifest.env_vars['ENV_VAR_METRICS'], 'TeStInG')
+        metric_node = manifest.metrics['metric.test.number_of_people']
+        self.assertEqual(metric_node.meta, {'my_meta': 'TeStInG'})
+
+        # Change metrics env var
+        os.environ['ENV_VAR_METRICS'] = 'Changed!'
+        results = self.run_dbt(["run"])
+        manifest = get_manifest()
+        metric_node = manifest.metrics['metric.test.number_of_people']
+        self.assertEqual(metric_node.meta, {'my_meta': 'Changed!'})
 
         # delete the env vars to cleanup
         del os.environ['ENV_VAR_TEST']
@@ -230,6 +247,7 @@ def test_postgres_env_vars_models(self):
         del os.environ['ENV_VAR_COLOR']
         del os.environ['ENV_VAR_SOME_KEY']
         del os.environ['ENV_VAR_OWNER']
+        del os.environ['ENV_VAR_METRICS']
 
 
 class ProjectEnvVarTest(BasePPTest):