Skip to content

Commit

Permalink
Make dbt support documentation for snapshots and seeds (and analyses)
Browse files Browse the repository at this point in the history
Add a deprecation warning for snapshots/seeds documented under "models"
Add a bunch of tests
  • Loading branch information
Jacob Beck committed Jan 15, 2020
1 parent 1a72660 commit 39ab8b0
Show file tree
Hide file tree
Showing 26 changed files with 436 additions and 200 deletions.
18 changes: 16 additions & 2 deletions core/dbt/contracts/graph/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from dbt.include.global_project import PACKAGES
from dbt.logger import GLOBAL_LOGGER as logger
from dbt.node_types import NodeType
from dbt import deprecations
from dbt import tracking
import dbt.utils

Expand Down Expand Up @@ -439,12 +440,25 @@ def patch_nodes(self, patches):
# nodes looking for matching names. We could use _find_by_name if we
# were ok with doing an O(n*m) search (one nodes scan per patch)
for node in self.nodes.values():
if node.resource_type != NodeType.Model:
if node.resource_type == NodeType.Source:
continue
patch = patches.pop(node.name, None)
if not patch:
continue
node.patch(patch)
if node.resource_type.pluralize() == patch.yaml_key:
node.patch(patch)
elif patch.yaml_key == 'models':
deprecations.warn(
'models-key-mismatch', patch=patch, node=node
)
else:
raise_compiler_error(
f'patch instruction in {patch.original_file_path} under '
f'key "{patch.yaml_key}" was for node "{node.name}", but '
f'the node with the same name (from '
f'{node.original_file_path}) had resource type '
f'"{node.resource_type}"'
)

# log debug-level warning about nodes we couldn't find
if patches:
Expand Down
10 changes: 5 additions & 5 deletions core/dbt/contracts/graph/parsed.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from dbt.contracts.graph.unparsed import (
UnparsedNode, UnparsedMacro, UnparsedDocumentationFile, Quoting,
UnparsedBaseNode, FreshnessThreshold, ExternalTable,
AdditionalPropertiesAllowed
AdditionalPropertiesAllowed, HasYamlMetadata
)
from dbt.contracts.util import Replaceable, list_str
from dbt.logger import GLOBAL_LOGGER as logger # noqa
Expand Down Expand Up @@ -173,16 +173,17 @@ def is_ephemeral_model(self):
def depends_on_nodes(self):
return self.depends_on.nodes

def patch(self, patch):
def patch(self, patch: 'ParsedNodePatch'):
"""Given a ParsedNodePatch, add the new information to the node."""
# explicitly pick out the parts to update so we don't inadvertently
# step on the model name or anything
self.patch_path = patch.original_file_path
self.patch_path: Optional[str] = patch.original_file_path
self.description = patch.description
self.columns = patch.columns
self.docrefs = patch.docrefs
self.meta = patch.meta
if dbt.flags.STRICT_MODE:
assert isinstance(self, JsonSchemaMixin)
self.to_dict(validate=True)

def get_materialization(self):
Expand Down Expand Up @@ -452,10 +453,9 @@ def json_schema(cls, embeddable=False):
# regular parsed node. Note that description and columns must be present, but
# may be empty.
@dataclass
class ParsedNodePatch(JsonSchemaMixin, Replaceable):
class ParsedNodePatch(HasYamlMetadata, Replaceable):
name: str
description: str
original_file_path: str
columns: Dict[str, ColumnInfo]
docrefs: List[Docref]
meta: Dict[str, Any]
Expand Down
13 changes: 12 additions & 1 deletion core/dbt/contracts/graph/unparsed.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,14 @@ class NodeDescription(NamedTested):


@dataclass
class UnparsedNodeUpdate(ColumnDescription, NodeDescription):
class HasYamlMetadata(JsonSchemaMixin):
original_file_path: str
yaml_key: str
package_name: str


@dataclass
class UnparsedNodeUpdate(ColumnDescription, NodeDescription, HasYamlMetadata):
def __post_init__(self):
NodeDescription.__post_init__(self)

Expand Down Expand Up @@ -219,6 +226,10 @@ class UnparsedSourceDefinition(JsonSchemaMixin, Replaceable):
loaded_at_field: Optional[str] = None
tables: List[UnparsedSourceTableDefinition] = field(default_factory=list)

@property
def yaml_key(self) -> 'str':
return 'sources'


@dataclass
class UnparsedDocumentationFile(JsonSchemaMixin, Replaceable):
Expand Down
13 changes: 13 additions & 0 deletions core/dbt/deprecations.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,18 @@ class ColumnQuotingDeprecation(DBTDeprecation):
'''


class ModelsKeyNonModelDeprecation(DBTDeprecation):
_name = 'models-key-mismatch'

_description = '''
patch instruction in {patch.original_file_path} under key
"models" was for node "{node.name}", but the node with the same
name (from {node.original_file_path}) had resource type
"{node.resource_type}". Nodes should be described under their resource
specific key. Support for this will be removed in a future release.
'''.strip()


_adapter_renamed_description = """\
The adapter function `adapter.{old_name}` is deprecated and will be removed in
a future release of dbt. Please use `adapter.{new_name}` instead.
Expand Down Expand Up @@ -136,6 +148,7 @@ def warn(name, *args, **kwargs):
MaterializationReturnDeprecation(),
NotADictionaryDeprecation(),
ColumnQuotingDeprecation(),
ModelsKeyNonModelDeprecation(),
]

deprecations: Dict[str, DBTDeprecation] = {
Expand Down
18 changes: 18 additions & 0 deletions core/dbt/node_types.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import List

from hologram.helpers import StrEnum


Expand Down Expand Up @@ -34,6 +36,22 @@ def refable(cls):
cls.Snapshot,
]]

@classmethod
def documentable(cls) -> List['NodeType']:
return [
cls.Model,
cls.Seed,
cls.Snapshot,
cls.Analysis,
cls.Source,
]

def pluralize(self) -> str:
if self == 'analysis':
return 'analyses'
else:
return f'{self}s'


class RunHookType(StrEnum):
Start = 'on-run-start'
Expand Down
8 changes: 4 additions & 4 deletions core/dbt/parser/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import os
import pickle
from datetime import datetime
from typing import Dict, Optional, Mapping, Callable, Any
from typing import Dict, Optional, Mapping, Callable, Any, List, Type

from dbt.include.global_project import PACKAGES
import dbt.exceptions
Expand All @@ -15,7 +15,7 @@
from dbt.contracts.graph.compiled import CompileResultNode
from dbt.contracts.graph.manifest import Manifest, FilePath, FileHash
from dbt.exceptions import raise_compiler_error
from dbt.parser.base import BaseParser
from dbt.parser.base import BaseParser, Parser
from dbt.parser.analysis import AnalysisParser
from dbt.parser.data_test import DataTestParser
from dbt.parser.docs import DocumentationParser
Expand All @@ -36,7 +36,7 @@
DEFAULT_PARTIAL_PARSE = False


_parser_types = [
_parser_types: List[Type[Parser]] = [
ModelParser,
SnapshotParser,
AnalysisParser,
Expand Down Expand Up @@ -161,7 +161,7 @@ def parse_project(
macro_manifest: Manifest,
old_results: Optional[ParseResult],
) -> None:
parsers = []
parsers: List[Parser] = []
for cls in _parser_types:
parser = cls(self.results, project, self.root_project,
macro_manifest)
Expand Down
5 changes: 5 additions & 0 deletions core/dbt/parser/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,16 @@ def sanitized_update(
.format(node_id, old_file)
)

patched = False
for name in old_file.patches:
patch = _expect_value(
name, old_result.patches, old_file, "patches"
)
self.add_patch(source_file, patch)
patched = True

if patched:
self.get_file(source_file).patches.sort()

return True

Expand Down
10 changes: 5 additions & 5 deletions core/dbt/parser/schema_test_builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ def tests(self) -> List[Union[Dict[str, Any], str]]:
return self.table.tests


ModelTarget = UnparsedNodeUpdate
NodeTarget = UnparsedNodeUpdate


Target = TypeVar('Target', ModelTarget, SourceTarget)
Target = TypeVar('Target', NodeTarget, SourceTarget)


@dataclass
Expand Down Expand Up @@ -257,7 +257,7 @@ def macro_name(self) -> str:
return macro_name

def describe_test_target(self) -> str:
if isinstance(self.target, ModelTarget):
if isinstance(self.target, NodeTarget):
fmt = "model('{0}')"
elif isinstance(self.target, SourceTarget):
fmt = "source('{0.source}', '{0.table}')"
Expand All @@ -268,7 +268,7 @@ def describe_test_target(self) -> str:
raise NotImplementedError('describe_test_target not implemented!')

def get_test_name(self) -> Tuple[str, str]:
if isinstance(self.target, ModelTarget):
if isinstance(self.target, NodeTarget):
name = self.name
elif isinstance(self.target, SourceTarget):
name = 'source_' + self.name
Expand All @@ -290,7 +290,7 @@ def build_raw_sql(self) -> str:
)

def build_model_str(self):
if isinstance(self.target, ModelTarget):
if isinstance(self.target, NodeTarget):
fmt = "ref('{0.name}')"
elif isinstance(self.target, SourceTarget):
fmt = "source('{0.source.name}', '{0.table.name}')"
Expand Down
Loading

0 comments on commit 39ab8b0

Please sign in to comment.