Skip to content

Commit

Permalink
Improve error handling on invalid archive configs
Browse files Browse the repository at this point in the history
Added a special archive-only node that has extra config restrictions
add tests for invalid archive config
  • Loading branch information
Jacob Beck committed Apr 9, 2019
1 parent 41f4bfa commit f1a5bce
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 1 deletion.
38 changes: 38 additions & 0 deletions core/dbt/contracts/graph/parsed.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,44 @@ def config(self, value):
self._contents['config'] = value


ARCHIVE_CONFIG_CONTRACT = {
'properties': {
'target_database': {
'type': 'string',
},
'target_schema': {
'type': 'string',
},
'unique_key': {
'type': 'string',
},
'strategy': {
'enum': ['timestamp'],
},
},
'required': [
'target_database', 'target_schema', 'unique_key', 'strategy',
],
}


PARSED_ARCHIVE_NODE_CONTRACT = deep_merge(
PARSED_NODE_CONTRACT,
{
'properties': {
'config': ARCHIVE_CONFIG_CONTRACT,
'resource_type': {
'enum': [NodeType.Archive],
},
},
}
)


class ParsedArchiveNode(ParsedNode):
SCHEMA = PARSED_ARCHIVE_NODE_CONTRACT


# The parsed node update is only the 'patch', not the test. The test became a
# regular parsed node. Note that description and columns must be present, but
# may be empty.
Expand Down
18 changes: 17 additions & 1 deletion core/dbt/parser/archives.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from dbt.contracts.graph.unparsed import UnparsedNode
from dbt.contracts.graph.parsed import ParsedArchiveNode
from dbt.node_types import NodeType
from dbt.parser.base import MacrosKnownParser
from dbt.parser.base_sql import BaseSqlParser, SQLParseResult
Expand Down Expand Up @@ -119,7 +120,7 @@ def parse_archives_from_file(self, file_node, tags=None):

@classmethod
def get_compiled_path(cls, name, relative_path):
return os.path.join('archives', relative_path)
return relative_path

@classmethod
def get_fqn(cls, node, package_project_config, extra=[]):
Expand All @@ -131,6 +132,16 @@ def get_fqn(cls, node, package_project_config, extra=[]):

return fqn

@staticmethod
def validate_archives(node):
if node.resource_type == NodeType.Archive:
try:
return ParsedArchiveNode(**node.to_shallow_dict())
except dbt.exceptions.JSONValidationException as exc:
raise dbt.exceptions.CompilationException(str(exc), node)
else:
return node

def parse_sql_nodes(self, nodes, tags=None):
if tags is None:
tags = []
Expand All @@ -145,5 +156,10 @@ def parse_sql_nodes(self, nodes, tags=None):
found = super(ArchiveBlockParser, self).parse_sql_nodes(
nodes=archive_nodes, tags=tags
)
# make sure our blocks are going to work when we try to archive
# them!
found.parsed = {k: self.validate_archives(v) for
k, v in found.parsed.items()}

results.update(found)
return results
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{% archive archive_sad %}

{# missing target_database #}
{{
config(
target_schema=schema,
unique_key='"id" || ' ~ "'-'" ~ ' || "first_name"',
strategy='timestamp',
updated_at='"updated_at"',
)
}}
select * from {{database}}.{{schema}}.seed

{% endarchive %}
24 changes: 24 additions & 0 deletions test/integration/004_simple_archive_test/test_simple_archive.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from nose.plugins.attrib import attr
from test.integration.base import DBTIntegrationTest
import dbt.exceptions


class TestSimpleArchive(DBTIntegrationTest):
Expand Down Expand Up @@ -363,3 +364,26 @@ def project_config(self):

def run_archive(self):
return self.run_dbt(['archive', '--vars', '{{"target_database": {}}}'.format(self.alternative_database)])


class TestBadArchive(DBTIntegrationTest):
@property
def schema(self):
return "simple_archive_004"

@property
def models(self):
return "test/integration/004_simple_archive_test/models"

@property
def project_config(self):
return {
"archive-paths": ['test/integration/004_simple_archive_test/test-archives-invalid'],
}

@attr(type='postgres')
def test__postgres__invalid(self):
with self.assertRaises(dbt.exceptions.CompilationException) as exc:
self.run_dbt(['compile'], expect_pass=False)

self.assertIn('target_database', str(exc.exception))

0 comments on commit f1a5bce

Please sign in to comment.