Compare commits

...

10 Commits

Author SHA1 Message Date
Emily Rockman
734b6429c7 update to use Docs object 2022-07-27 13:34:23 -05:00
Benoit Perigaud
a75b2c0a90 Make docs a dataclass instead of a Dict 2022-07-26 19:38:22 +02:00
Benoit Perigaud
6b6ae22434 Merge branch 'feature/custom-node-colors-dbt_project' of github.com:dbt-labs/dbt-core into feature/custom-node-colors-dbt_project 2022-07-20 12:51:29 +02:00
Benoit Perigaud
287f443ec9 Make docs a Dict to avoid parsing errors 2022-07-20 12:44:01 +02:00
Benoit Perigaud
aea2c4a29b Add node_color to Docs 2022-07-20 12:43:09 +02:00
Benoit Perigaud
21ffe31270 Handle when docs is both under docs and config.docs 2022-07-20 12:42:25 +02:00
Sung Won Chung
70c9074625 Merge branch 'main' of https://github.com/dbt-labs/dbt into feature/custom-node-colors-dbt_project 2022-07-19 14:27:18 -05:00
Benoit Perigaud
9fca33cb29 Add docs config and input validation 2022-06-21 09:10:15 +02:00
Benoit Perigaud
6360247d39 Remove node_color from the original docs config 2022-06-21 09:09:35 +02:00
Matt Winkler
f0fbb0e551 add Optional node_color config in Docs dataclass 2022-06-07 09:17:15 -06:00
6 changed files with 191 additions and 11 deletions

View File

@@ -7,7 +7,8 @@ from dbt.dataclass_schema import (
ValidationError, ValidationError,
register_pattern, register_pattern,
) )
from dbt.contracts.graph.unparsed import AdditionalPropertiesAllowed from dbt.contracts.graph.unparsed import AdditionalPropertiesAllowed, Docs
from dbt.contracts.graph.utils import validate_color
from dbt.exceptions import InternalException, CompilationException from dbt.exceptions import InternalException, CompilationException
from dbt.contracts.util import Replaceable, list_str from dbt.contracts.util import Replaceable, list_str
from dbt import hooks from dbt import hooks
@@ -285,7 +286,7 @@ class BaseConfig(AdditionalPropertiesAllowed, Replaceable):
# 'meta' moved here from node # 'meta' moved here from node
mergebehavior = { mergebehavior = {
"append": ["pre-hook", "pre_hook", "post-hook", "post_hook", "tags"], "append": ["pre-hook", "pre_hook", "post-hook", "post_hook", "tags"],
"update": ["quoting", "column_types", "meta"], "update": ["quoting", "column_types", "meta", "docs"],
"dict_key_append": ["grants"], "dict_key_append": ["grants"],
} }
@@ -460,6 +461,20 @@ class NodeConfig(NodeAndTestConfig):
grants: Dict[str, Any] = field( grants: Dict[str, Any] = field(
default_factory=dict, metadata=MergeBehavior.DictKeyAppend.meta() default_factory=dict, metadata=MergeBehavior.DictKeyAppend.meta()
) )
docs: Docs = field(
default_factory=lambda: Docs(show=True),
metadata=MergeBehavior.Update.meta(),
)
# we validate that node_color has a suitable value to prevent dbt-docs from crashing
def __post_init__(self):
if self.docs.node_color:
node_color = self.docs.node_color
if not validate_color(node_color):
raise ValidationError(
f"Invalid color name for docs.node_color: {node_color}. "
"It is neither a valid HTML color name nor a valid HEX code."
)
@classmethod @classmethod
def __pre_deserialize__(cls, data): def __pre_deserialize__(cls, data):

View File

@@ -157,7 +157,6 @@ class ParsedNodeMixins(dbtClassMixin):
self.created_at = time.time() self.created_at = time.time()
self.description = patch.description self.description = patch.description
self.columns = patch.columns self.columns = patch.columns
self.docs = patch.docs
def get_materialization(self): def get_materialization(self):
return self.config.materialized return self.config.materialized
@@ -203,7 +202,7 @@ class ParsedNodeDefaults(NodeInfoMixin, ParsedNodeMandatory):
description: str = field(default="") description: str = field(default="")
columns: Dict[str, ColumnInfo] = field(default_factory=dict) columns: Dict[str, ColumnInfo] = field(default_factory=dict)
meta: Dict[str, Any] = field(default_factory=dict) meta: Dict[str, Any] = field(default_factory=dict)
docs: Docs = field(default_factory=Docs) docs: Docs = field(default_factory=lambda: Docs(show=True))
patch_path: Optional[str] = None patch_path: Optional[str] = None
compiled_path: Optional[str] = None compiled_path: Optional[str] = None
build_path: Optional[str] = None build_path: Optional[str] = None

View File

@@ -76,6 +76,7 @@ class UnparsedRunHook(UnparsedNode):
@dataclass @dataclass
class Docs(dbtClassMixin, Replaceable): class Docs(dbtClassMixin, Replaceable):
show: bool = True show: bool = True
node_color: Optional[str] = None
@dataclass @dataclass

View File

@@ -0,0 +1,153 @@
import re
HTML_COLORS = [
"aliceblue",
"antiquewhite",
"aqua",
"aquamarine",
"azure",
"beige",
"bisque",
"black",
"blanchedalmond",
"blue",
"blueviolet",
"brown",
"burlywood",
"cadetblue",
"chartreuse",
"chocolate",
"coral",
"cornflowerblue",
"cornsilk",
"crimson",
"cyan",
"darkblue",
"darkcyan",
"darkgoldenrod",
"darkgray",
"darkgreen",
"darkkhaki",
"darkmagenta",
"darkolivegreen",
"darkorange",
"darkorchid",
"darkred",
"darksalmon",
"darkseagreen",
"darkslateblue",
"darkslategray",
"darkturquoise",
"darkviolet",
"deeppink",
"deepskyblue",
"dimgray",
"dodgerblue",
"firebrick",
"floralwhite",
"forestgreen",
"fuchsia",
"gainsboro",
"ghostwhite",
"gold",
"goldenrod",
"gray",
"green",
"greenyellow",
"honeydew",
"hotpink",
"indianred",
"indigo",
"ivory",
"khaki",
"lavender",
"lavenderblush",
"lawngreen",
"lemonchiffon",
"lightblue",
"lightcoral",
"lightcyan",
"lightgoldenrodyellow",
"lightgray",
"lightgreen",
"lightpink",
"lightsalmon",
"lightsalmon",
"lightseagreen",
"lightskyblue",
"lightslategray",
"lightsteelblue",
"lightyellow",
"lime",
"limegreen",
"linen",
"magenta",
"maroon",
"mediumaquamarine",
"mediumblue",
"mediumorchid",
"mediumpurple",
"mediumseagreen",
"mediumslateblue",
"mediumslateblue",
"mediumspringgreen",
"mediumturquoise",
"mediumvioletred",
"midnightblue",
"mintcream",
"mistyrose",
"moccasin",
"navajowhite",
"navy",
"oldlace",
"olive",
"olivedrab",
"orange",
"orangered",
"orchid",
"palegoldenrod",
"palegreen",
"paleturquoise",
"palevioletred",
"papayawhip",
"peachpuff",
"peru",
"pink",
"plum",
"powderblue",
"purple",
"rebeccapurple",
"red",
"rosybrown",
"royalblue",
"saddlebrown",
"salmon",
"sandybrown",
"seagreen",
"seashell",
"sienna",
"silver",
"skyblue",
"slateblue",
"slategray",
"snow",
"springgreen",
"steelblue",
"tan",
"teal",
"thistle",
"tomato",
"turquoise",
"violet",
"wheat",
"white",
"whitesmoke",
"yellow",
"yellowgreen",
]
def validate_color(color: str) -> bool:
match_hex = re.search(r"^#(?:[0-9a-f]{3}){1,2}$", color.lower())
match_html_color_name = color.lower() in HTML_COLORS
return bool(match_hex or match_html_color_name)

View File

@@ -17,7 +17,7 @@ from dbt.config import Project, RuntimeConfig
from dbt.context.context_config import ContextConfig from dbt.context.context_config import ContextConfig
from dbt.contracts.graph.manifest import Manifest from dbt.contracts.graph.manifest import Manifest
from dbt.contracts.graph.parsed import HasUniqueID, ManifestNodes from dbt.contracts.graph.parsed import HasUniqueID, ManifestNodes
from dbt.contracts.graph.unparsed import UnparsedNode from dbt.contracts.graph.unparsed import UnparsedNode, Docs
from dbt.exceptions import ParsingException, validator_error_message, InternalException from dbt.exceptions import ParsingException, validator_error_message, InternalException
from dbt import hooks from dbt import hooks
from dbt.node_types import NodeType from dbt.node_types import NodeType
@@ -287,6 +287,11 @@ class ConfiguredParser(
if "meta" in config_dict and config_dict["meta"]: if "meta" in config_dict and config_dict["meta"]:
parsed_node.meta = config_dict["meta"] parsed_node.meta = config_dict["meta"]
# If we have docs in the config, copy to node level, for backwards
# compatibility with earlier node-only config.
if "docs" in config_dict and config_dict["docs"]:
parsed_node.docs = Docs(config_dict["docs"])
# unrendered_config is used to compare the original database/schema/alias # unrendered_config is used to compare the original database/schema/alias
# values and to handle 'same_config' and 'same_contents' calls # values and to handle 'same_config' and 'same_contents' calls
parsed_node.unrendered_config = config.build_config_dict(rendered=False) parsed_node.unrendered_config = config.build_config_dict(rendered=False)

View File

@@ -797,6 +797,7 @@ class NonSourceParser(YamlDocsReader, Generic[NonSourceTarget, Parsed]):
if self.key != "macros": if self.key != "macros":
# macros don't have the 'config' key support yet # macros don't have the 'config' key support yet
self.normalize_meta_attribute(data, path) self.normalize_meta_attribute(data, path)
self.normalize_docs_attribute(data, path)
node = self._target_type().from_dict(data) node = self._target_type().from_dict(data)
except (ValidationError, JSONValidationException) as exc: except (ValidationError, JSONValidationException) as exc:
msg = error_context(path, self.key, data, exc) msg = error_context(path, self.key, data, exc)
@@ -804,21 +805,27 @@ class NonSourceParser(YamlDocsReader, Generic[NonSourceTarget, Parsed]):
else: else:
yield node yield node
# We want to raise an error if 'meta' is in two places, and move 'meta' # We want to raise an error if some attributes are in two places, and move them
# from toplevel to config if necessary # from toplevel to config if necessary
def normalize_meta_attribute(self, data, path): def normalize_attribute(self, data, path, attribute):
if "meta" in data: if attribute in data:
if "config" in data and "meta" in data["config"]: if "config" in data and attribute in data["config"]:
raise ParsingException( raise ParsingException(
f""" f"""
In {path}: found meta dictionary in 'config' dictionary and as top-level key. In {path}: found {attribute} dictionary in 'config' dictionary and as top-level key.
Remove the top-level key and define it under 'config' dictionary only. Remove the top-level key and define it under 'config' dictionary only.
""".strip() """.strip()
) )
else: else:
if "config" not in data: if "config" not in data:
data["config"] = {} data["config"] = {}
data["config"]["meta"] = data.pop("meta") data["config"][attribute] = data.pop(attribute)
def normalize_meta_attribute(self, data, path):
return self.normalize_attribute(data, path, "meta")
def normalize_docs_attribute(self, data, path):
return self.normalize_attribute(data, path, "docs")
def patch_node_config(self, node, patch): def patch_node_config(self, node, patch):
# Get the ContextConfig that's used in calculating the config # Get the ContextConfig that's used in calculating the config