Repository: dbt-labs/dbt-core

Issue:
Exposures are missing in graph context variable
The graph context variable contains "nodes" (models and tests) and "sources". The exosures are missing. 

https://github.com/fishtown-analytics/dbt/blob/749f87397ec1e0a270b2e09bd8dbeb71862fdb81/core/dbt/contracts/graph/manifest.py#L579-L594


Discussion:

Resolving patch:
diff --git a/core/dbt/contracts/graph/manifest.py b/core/dbt/contracts/graph/manifest.py
--- a/core/dbt/contracts/graph/manifest.py
+++ b/core/dbt/contracts/graph/manifest.py
@@ -583,6 +583,10 @@ def build_flat_graph(self):
         manifest!
         """
         self.flat_graph = {
+            'exposures': {
+                k: v.to_dict(omit_none=False)
+                for k, v in self.exposures.items()
+            },
             'nodes': {
                 k: v.to_dict(omit_none=False)
                 for k, v in self.nodes.items()
@@ -649,7 +653,7 @@ def find_materialization_macro_by_name(
 
     def get_resource_fqns(self) -> Mapping[str, PathSet]:
         resource_fqns: Dict[str, Set[Tuple[str, ...]]] = {}
-        all_resources = chain(self.nodes.values(), self.sources.values())
+        all_resources = chain(self.exposures.values(), self.nodes.values(), self.sources.values())
         for resource in all_resources:
             resource_type_plural = resource.resource_type.pluralize()
             if resource_type_plural not in resource_fqns:


Test patch:
diff --git a/test/unit/test_manifest.py b/test/unit/test_manifest.py
--- a/test/unit/test_manifest.py
+++ b/test/unit/test_manifest.py
@@ -20,7 +20,15 @@
     NodeConfig,
     ParsedSeedNode,
     ParsedSourceDefinition,
+    ParsedExposure,
 )
+
+from dbt.contracts.graph.unparsed import (
+    ExposureType,
+    ExposureOwner,
+    MaturityType
+)
+
 from dbt.contracts.graph.compiled import CompiledModelNode
 from dbt.node_types import NodeType
 import freezegun
@@ -63,6 +71,27 @@ def setUp(self):
             'tags': [],
         })
 
+        self.exposures = {
+            'exposure.root.my_exposure': ParsedExposure(
+                name='my_exposure',
+                type=ExposureType.Dashboard,
+                owner=ExposureOwner(email='some@email.com'),
+                resource_type=NodeType.Exposure,
+                description='Test description',
+                maturity=MaturityType.High,
+                url='hhtp://mydashboard.com',
+                depends_on=DependsOn(nodes=['model.root.multi']),
+                refs=[['multi']],
+                sources=[],
+                fqn=['root', 'my_exposure'],
+                unique_id='exposure.root.my_exposure',
+                package_name='root',
+                root_path='',
+                path='my_exposure.sql',
+                original_file_path='my_exposure.sql'
+            )
+        }
+
         self.nested_nodes = {
             'model.snowplow.events': ParsedModelNode(
                 name='events',
@@ -211,6 +240,8 @@ def setUp(self):
                 original_file_path='schema.yml',
             ),
         }
+        for exposure in self.exposures.values():
+            exposure.validate(exposure.to_dict(omit_none=True))
         for node in self.nested_nodes.values():
             node.validate(node.to_dict(omit_none=True))
         for source in self.sources.values():
@@ -319,15 +350,18 @@ def test__nested_nodes(self):
         )
 
     def test__build_flat_graph(self):
+        exposures = copy.copy(self.exposures)
         nodes = copy.copy(self.nested_nodes)
         sources = copy.copy(self.sources)
         manifest = Manifest(nodes=nodes, sources=sources, macros={}, docs={},
-                            disabled=[], files={}, exposures={}, selectors={})
+                            disabled=[], files={}, exposures=exposures, selectors={})
         manifest.build_flat_graph()
         flat_graph = manifest.flat_graph
+        flat_exposures = flat_graph['exposures']
         flat_nodes = flat_graph['nodes']
         flat_sources = flat_graph['sources']
-        self.assertEqual(set(flat_graph), set(['nodes', 'sources']))
+        self.assertEqual(set(flat_graph), set(['exposures', 'nodes', 'sources']))
+        self.assertEqual(set(flat_exposures), set(self.exposures))
         self.assertEqual(set(flat_nodes), set(self.nested_nodes))
         self.assertEqual(set(flat_sources), set(self.sources))
         for node in flat_nodes.values():
@@ -424,8 +458,11 @@ def test_get_resource_fqns(self):
             checksum=FileHash.empty(),
         )
         manifest = Manifest(nodes=nodes, sources=self.sources, macros={}, docs={},
-                            disabled=[], files={}, exposures={}, selectors={})
+                            disabled=[], files={}, exposures=self.exposures, selectors={})
         expect = {
+            'exposures': frozenset([
+                ('root', 'my_exposure')
+            ]),
             'models': frozenset([
                 ('snowplow', 'events'),
                 ('root', 'events'),
@@ -707,7 +744,7 @@ def test__build_flat_graph(self):
         manifest.build_flat_graph()
         flat_graph = manifest.flat_graph
         flat_nodes = flat_graph['nodes']
-        self.assertEqual(set(flat_graph), set(['nodes', 'sources']))
+        self.assertEqual(set(flat_graph), set(['exposures', 'nodes', 'sources']))
         self.assertEqual(set(flat_nodes), set(self.nested_nodes))
         compiled_count = 0
         for node in flat_nodes.values():
@@ -723,7 +760,7 @@ def test__build_flat_graph(self):
 
 class TestManifestSearch(unittest.TestCase):
     _macros = []
-    _models = []
+    _nodes = []
     _docs = []
 
     @property