dvt-core 1.11.0b4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dvt-core might be problematic. Click here for more details.
- dvt/__init__.py +7 -0
- dvt/_pydantic_shim.py +26 -0
- dvt/adapters/__init__.py +16 -0
- dvt/adapters/multi_adapter_manager.py +268 -0
- dvt/artifacts/__init__.py +0 -0
- dvt/artifacts/exceptions/__init__.py +1 -0
- dvt/artifacts/exceptions/schemas.py +31 -0
- dvt/artifacts/resources/__init__.py +116 -0
- dvt/artifacts/resources/base.py +68 -0
- dvt/artifacts/resources/types.py +93 -0
- dvt/artifacts/resources/v1/analysis.py +10 -0
- dvt/artifacts/resources/v1/catalog.py +23 -0
- dvt/artifacts/resources/v1/components.py +275 -0
- dvt/artifacts/resources/v1/config.py +282 -0
- dvt/artifacts/resources/v1/documentation.py +11 -0
- dvt/artifacts/resources/v1/exposure.py +52 -0
- dvt/artifacts/resources/v1/function.py +53 -0
- dvt/artifacts/resources/v1/generic_test.py +32 -0
- dvt/artifacts/resources/v1/group.py +22 -0
- dvt/artifacts/resources/v1/hook.py +11 -0
- dvt/artifacts/resources/v1/macro.py +30 -0
- dvt/artifacts/resources/v1/metric.py +173 -0
- dvt/artifacts/resources/v1/model.py +146 -0
- dvt/artifacts/resources/v1/owner.py +10 -0
- dvt/artifacts/resources/v1/saved_query.py +112 -0
- dvt/artifacts/resources/v1/seed.py +42 -0
- dvt/artifacts/resources/v1/semantic_layer_components.py +72 -0
- dvt/artifacts/resources/v1/semantic_model.py +315 -0
- dvt/artifacts/resources/v1/singular_test.py +14 -0
- dvt/artifacts/resources/v1/snapshot.py +92 -0
- dvt/artifacts/resources/v1/source_definition.py +85 -0
- dvt/artifacts/resources/v1/sql_operation.py +10 -0
- dvt/artifacts/resources/v1/unit_test_definition.py +78 -0
- dvt/artifacts/schemas/__init__.py +0 -0
- dvt/artifacts/schemas/base.py +191 -0
- dvt/artifacts/schemas/batch_results.py +24 -0
- dvt/artifacts/schemas/catalog/__init__.py +12 -0
- dvt/artifacts/schemas/catalog/v1/__init__.py +0 -0
- dvt/artifacts/schemas/catalog/v1/catalog.py +60 -0
- dvt/artifacts/schemas/freshness/__init__.py +1 -0
- dvt/artifacts/schemas/freshness/v3/__init__.py +0 -0
- dvt/artifacts/schemas/freshness/v3/freshness.py +159 -0
- dvt/artifacts/schemas/manifest/__init__.py +2 -0
- dvt/artifacts/schemas/manifest/v12/__init__.py +0 -0
- dvt/artifacts/schemas/manifest/v12/manifest.py +212 -0
- dvt/artifacts/schemas/results.py +148 -0
- dvt/artifacts/schemas/run/__init__.py +2 -0
- dvt/artifacts/schemas/run/v5/__init__.py +0 -0
- dvt/artifacts/schemas/run/v5/run.py +184 -0
- dvt/artifacts/schemas/upgrades/__init__.py +4 -0
- dvt/artifacts/schemas/upgrades/upgrade_manifest.py +174 -0
- dvt/artifacts/schemas/upgrades/upgrade_manifest_dbt_version.py +2 -0
- dvt/artifacts/utils/validation.py +153 -0
- dvt/cli/__init__.py +1 -0
- dvt/cli/context.py +16 -0
- dvt/cli/exceptions.py +56 -0
- dvt/cli/flags.py +558 -0
- dvt/cli/main.py +971 -0
- dvt/cli/option_types.py +121 -0
- dvt/cli/options.py +79 -0
- dvt/cli/params.py +803 -0
- dvt/cli/requires.py +478 -0
- dvt/cli/resolvers.py +32 -0
- dvt/cli/types.py +40 -0
- dvt/clients/__init__.py +0 -0
- dvt/clients/checked_load.py +82 -0
- dvt/clients/git.py +164 -0
- dvt/clients/jinja.py +206 -0
- dvt/clients/jinja_static.py +245 -0
- dvt/clients/registry.py +192 -0
- dvt/clients/yaml_helper.py +68 -0
- dvt/compilation.py +833 -0
- dvt/compute/__init__.py +26 -0
- dvt/compute/base.py +288 -0
- dvt/compute/engines/__init__.py +13 -0
- dvt/compute/engines/duckdb_engine.py +368 -0
- dvt/compute/engines/spark_engine.py +273 -0
- dvt/compute/query_analyzer.py +212 -0
- dvt/compute/router.py +483 -0
- dvt/config/__init__.py +4 -0
- dvt/config/catalogs.py +95 -0
- dvt/config/compute_config.py +406 -0
- dvt/config/profile.py +411 -0
- dvt/config/profiles_v2.py +464 -0
- dvt/config/project.py +893 -0
- dvt/config/renderer.py +232 -0
- dvt/config/runtime.py +491 -0
- dvt/config/selectors.py +209 -0
- dvt/config/utils.py +78 -0
- dvt/connectors/.gitignore +6 -0
- dvt/connectors/README.md +306 -0
- dvt/connectors/catalog.yml +217 -0
- dvt/connectors/download_connectors.py +300 -0
- dvt/constants.py +29 -0
- dvt/context/__init__.py +0 -0
- dvt/context/base.py +746 -0
- dvt/context/configured.py +136 -0
- dvt/context/context_config.py +350 -0
- dvt/context/docs.py +82 -0
- dvt/context/exceptions_jinja.py +179 -0
- dvt/context/macro_resolver.py +195 -0
- dvt/context/macros.py +171 -0
- dvt/context/manifest.py +73 -0
- dvt/context/providers.py +2198 -0
- dvt/context/query_header.py +14 -0
- dvt/context/secret.py +59 -0
- dvt/context/target.py +74 -0
- dvt/contracts/__init__.py +0 -0
- dvt/contracts/files.py +413 -0
- dvt/contracts/graph/__init__.py +0 -0
- dvt/contracts/graph/manifest.py +1904 -0
- dvt/contracts/graph/metrics.py +98 -0
- dvt/contracts/graph/model_config.py +71 -0
- dvt/contracts/graph/node_args.py +42 -0
- dvt/contracts/graph/nodes.py +1806 -0
- dvt/contracts/graph/semantic_manifest.py +233 -0
- dvt/contracts/graph/unparsed.py +812 -0
- dvt/contracts/project.py +417 -0
- dvt/contracts/results.py +53 -0
- dvt/contracts/selection.py +23 -0
- dvt/contracts/sql.py +86 -0
- dvt/contracts/state.py +69 -0
- dvt/contracts/util.py +46 -0
- dvt/deprecations.py +347 -0
- dvt/deps/__init__.py +0 -0
- dvt/deps/base.py +153 -0
- dvt/deps/git.py +196 -0
- dvt/deps/local.py +80 -0
- dvt/deps/registry.py +131 -0
- dvt/deps/resolver.py +149 -0
- dvt/deps/tarball.py +121 -0
- dvt/docs/source/_ext/dbt_click.py +118 -0
- dvt/docs/source/conf.py +32 -0
- dvt/env_vars.py +64 -0
- dvt/event_time/event_time.py +40 -0
- dvt/event_time/sample_window.py +60 -0
- dvt/events/__init__.py +16 -0
- dvt/events/base_types.py +37 -0
- dvt/events/core_types_pb2.py +2 -0
- dvt/events/logging.py +109 -0
- dvt/events/types.py +2534 -0
- dvt/exceptions.py +1487 -0
- dvt/flags.py +89 -0
- dvt/graph/__init__.py +11 -0
- dvt/graph/cli.py +248 -0
- dvt/graph/graph.py +172 -0
- dvt/graph/queue.py +213 -0
- dvt/graph/selector.py +375 -0
- dvt/graph/selector_methods.py +976 -0
- dvt/graph/selector_spec.py +223 -0
- dvt/graph/thread_pool.py +18 -0
- dvt/hooks.py +21 -0
- dvt/include/README.md +49 -0
- dvt/include/__init__.py +3 -0
- dvt/include/global_project.py +4 -0
- dvt/include/starter_project/.gitignore +4 -0
- dvt/include/starter_project/README.md +15 -0
- dvt/include/starter_project/__init__.py +3 -0
- dvt/include/starter_project/analyses/.gitkeep +0 -0
- dvt/include/starter_project/dvt_project.yml +36 -0
- dvt/include/starter_project/macros/.gitkeep +0 -0
- dvt/include/starter_project/models/example/my_first_dbt_model.sql +27 -0
- dvt/include/starter_project/models/example/my_second_dbt_model.sql +6 -0
- dvt/include/starter_project/models/example/schema.yml +21 -0
- dvt/include/starter_project/seeds/.gitkeep +0 -0
- dvt/include/starter_project/snapshots/.gitkeep +0 -0
- dvt/include/starter_project/tests/.gitkeep +0 -0
- dvt/internal_deprecations.py +27 -0
- dvt/jsonschemas/__init__.py +3 -0
- dvt/jsonschemas/jsonschemas.py +309 -0
- dvt/jsonschemas/project/0.0.110.json +4717 -0
- dvt/jsonschemas/project/0.0.85.json +2015 -0
- dvt/jsonschemas/resources/0.0.110.json +2636 -0
- dvt/jsonschemas/resources/0.0.85.json +2536 -0
- dvt/jsonschemas/resources/latest.json +6773 -0
- dvt/links.py +4 -0
- dvt/materializations/__init__.py +0 -0
- dvt/materializations/incremental/__init__.py +0 -0
- dvt/materializations/incremental/microbatch.py +235 -0
- dvt/mp_context.py +8 -0
- dvt/node_types.py +37 -0
- dvt/parser/__init__.py +23 -0
- dvt/parser/analysis.py +21 -0
- dvt/parser/base.py +549 -0
- dvt/parser/common.py +267 -0
- dvt/parser/docs.py +52 -0
- dvt/parser/fixtures.py +51 -0
- dvt/parser/functions.py +30 -0
- dvt/parser/generic_test.py +100 -0
- dvt/parser/generic_test_builders.py +334 -0
- dvt/parser/hooks.py +119 -0
- dvt/parser/macros.py +137 -0
- dvt/parser/manifest.py +2204 -0
- dvt/parser/models.py +574 -0
- dvt/parser/partial.py +1179 -0
- dvt/parser/read_files.py +445 -0
- dvt/parser/schema_generic_tests.py +423 -0
- dvt/parser/schema_renderer.py +111 -0
- dvt/parser/schema_yaml_readers.py +936 -0
- dvt/parser/schemas.py +1467 -0
- dvt/parser/search.py +149 -0
- dvt/parser/seeds.py +28 -0
- dvt/parser/singular_test.py +20 -0
- dvt/parser/snapshots.py +44 -0
- dvt/parser/sources.py +557 -0
- dvt/parser/sql.py +63 -0
- dvt/parser/unit_tests.py +622 -0
- dvt/plugins/__init__.py +20 -0
- dvt/plugins/contracts.py +10 -0
- dvt/plugins/exceptions.py +2 -0
- dvt/plugins/manager.py +164 -0
- dvt/plugins/manifest.py +21 -0
- dvt/profiler.py +20 -0
- dvt/py.typed +1 -0
- dvt/runners/__init__.py +2 -0
- dvt/runners/exposure_runner.py +7 -0
- dvt/runners/no_op_runner.py +46 -0
- dvt/runners/saved_query_runner.py +7 -0
- dvt/selected_resources.py +8 -0
- dvt/task/__init__.py +0 -0
- dvt/task/base.py +504 -0
- dvt/task/build.py +197 -0
- dvt/task/clean.py +57 -0
- dvt/task/clone.py +162 -0
- dvt/task/compile.py +151 -0
- dvt/task/compute.py +366 -0
- dvt/task/debug.py +650 -0
- dvt/task/deps.py +280 -0
- dvt/task/docs/__init__.py +3 -0
- dvt/task/docs/generate.py +408 -0
- dvt/task/docs/index.html +250 -0
- dvt/task/docs/serve.py +28 -0
- dvt/task/freshness.py +323 -0
- dvt/task/function.py +122 -0
- dvt/task/group_lookup.py +46 -0
- dvt/task/init.py +374 -0
- dvt/task/list.py +237 -0
- dvt/task/printer.py +176 -0
- dvt/task/profiles.py +256 -0
- dvt/task/retry.py +175 -0
- dvt/task/run.py +1146 -0
- dvt/task/run_operation.py +142 -0
- dvt/task/runnable.py +802 -0
- dvt/task/seed.py +104 -0
- dvt/task/show.py +150 -0
- dvt/task/snapshot.py +57 -0
- dvt/task/sql.py +111 -0
- dvt/task/test.py +464 -0
- dvt/tests/fixtures/__init__.py +1 -0
- dvt/tests/fixtures/project.py +620 -0
- dvt/tests/util.py +651 -0
- dvt/tracking.py +529 -0
- dvt/utils/__init__.py +3 -0
- dvt/utils/artifact_upload.py +151 -0
- dvt/utils/utils.py +408 -0
- dvt/version.py +249 -0
- dvt_core-1.11.0b4.dist-info/METADATA +252 -0
- dvt_core-1.11.0b4.dist-info/RECORD +261 -0
- dvt_core-1.11.0b4.dist-info/WHEEL +5 -0
- dvt_core-1.11.0b4.dist-info/entry_points.txt +2 -0
- dvt_core-1.11.0b4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from datetime import timedelta
|
|
4
|
+
from typing import Any, Dict, List, Optional, Union
|
|
5
|
+
|
|
6
|
+
from dvt.artifacts.resources.base import Docs, FileHash, GraphResource
|
|
7
|
+
from dvt.artifacts.resources.types import NodeType, TimePeriod
|
|
8
|
+
from dvt.artifacts.resources.v1.config import NodeConfig
|
|
9
|
+
|
|
10
|
+
from dbt_common.contracts.config.base import BaseConfig, MergeBehavior
|
|
11
|
+
from dbt_common.contracts.config.properties import AdditionalPropertiesMixin
|
|
12
|
+
from dbt_common.contracts.constraints import ColumnLevelConstraint
|
|
13
|
+
from dbt_common.contracts.util import Mergeable
|
|
14
|
+
from dbt_common.dataclass_schema import ExtensibleDbtClassMixin, dbtClassMixin
|
|
15
|
+
from dbt_semantic_interfaces.type_enums import TimeGranularity
|
|
16
|
+
|
|
17
|
+
NodeVersion = Union[str, float]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _backcompat_doc_blocks(doc_blocks: Any) -> List[str]:
|
|
21
|
+
"""
|
|
22
|
+
Make doc_blocks backwards-compatible for scenarios where a user specifies `doc_blocks` on a model or column.
|
|
23
|
+
Mashumaro will raise a serialization error if the specified `doc_blocks` isn't a list of strings.
|
|
24
|
+
In such a scenario, this method returns an empty list to avoid a serialization error.
|
|
25
|
+
Further along, `_get_doc_blocks` in `manifest.py` populates the correct `doc_blocks` for the happy path.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
if isinstance(doc_blocks, list) and all(isinstance(x, str) for x in doc_blocks):
|
|
29
|
+
return doc_blocks
|
|
30
|
+
|
|
31
|
+
return []
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class MacroDependsOn(dbtClassMixin):
|
|
36
|
+
macros: List[str] = field(default_factory=list)
|
|
37
|
+
|
|
38
|
+
# 'in' on lists is O(n) so this is O(n^2) for # of macros
|
|
39
|
+
def add_macro(self, value: str):
|
|
40
|
+
if value not in self.macros:
|
|
41
|
+
self.macros.append(value)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class DependsOn(MacroDependsOn):
|
|
46
|
+
nodes: List[str] = field(default_factory=list)
|
|
47
|
+
|
|
48
|
+
def add_node(self, value: str):
|
|
49
|
+
if value not in self.nodes:
|
|
50
|
+
self.nodes.append(value)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class RefArgs(dbtClassMixin):
|
|
55
|
+
name: str
|
|
56
|
+
package: Optional[str] = None
|
|
57
|
+
version: Optional[NodeVersion] = None
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def positional_args(self) -> List[str]:
|
|
61
|
+
if self.package:
|
|
62
|
+
return [self.package, self.name]
|
|
63
|
+
else:
|
|
64
|
+
return [self.name]
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def keyword_args(self) -> Dict[str, Optional[NodeVersion]]:
|
|
68
|
+
if self.version:
|
|
69
|
+
return {"version": self.version}
|
|
70
|
+
else:
|
|
71
|
+
return {}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass
|
|
75
|
+
class ColumnConfig(BaseConfig):
|
|
76
|
+
meta: Dict[str, Any] = field(default_factory=dict, metadata=MergeBehavior.Update.meta())
|
|
77
|
+
tags: List[str] = field(default_factory=list)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass
|
|
81
|
+
class ColumnInfo(AdditionalPropertiesMixin, ExtensibleDbtClassMixin):
|
|
82
|
+
"""Used in all ManifestNodes and SourceDefinition"""
|
|
83
|
+
|
|
84
|
+
name: str
|
|
85
|
+
description: str = ""
|
|
86
|
+
meta: Dict[str, Any] = field(default_factory=dict)
|
|
87
|
+
data_type: Optional[str] = None
|
|
88
|
+
constraints: List[ColumnLevelConstraint] = field(default_factory=list)
|
|
89
|
+
quote: Optional[bool] = None
|
|
90
|
+
config: ColumnConfig = field(default_factory=ColumnConfig)
|
|
91
|
+
tags: List[str] = field(default_factory=list)
|
|
92
|
+
_extra: Dict[str, Any] = field(default_factory=dict)
|
|
93
|
+
granularity: Optional[TimeGranularity] = None
|
|
94
|
+
doc_blocks: List[str] = field(default_factory=list)
|
|
95
|
+
|
|
96
|
+
def __post_serialize__(self, dct: Dict, context: Optional[Dict] = None) -> dict:
|
|
97
|
+
dct = super().__post_serialize__(dct, context)
|
|
98
|
+
dct["doc_blocks"] = _backcompat_doc_blocks(dct["doc_blocks"])
|
|
99
|
+
return dct
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@dataclass
|
|
103
|
+
class InjectedCTE(dbtClassMixin):
|
|
104
|
+
"""Used in CompiledNodes as part of ephemeral model processing"""
|
|
105
|
+
|
|
106
|
+
id: str
|
|
107
|
+
sql: str
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dataclass
|
|
111
|
+
class Contract(dbtClassMixin):
|
|
112
|
+
enforced: bool = False
|
|
113
|
+
alias_types: bool = True
|
|
114
|
+
checksum: Optional[str] = None
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@dataclass
|
|
118
|
+
class Quoting(dbtClassMixin, Mergeable):
|
|
119
|
+
database: Optional[bool] = None
|
|
120
|
+
schema: Optional[bool] = None
|
|
121
|
+
identifier: Optional[bool] = None
|
|
122
|
+
column: Optional[bool] = None
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass
|
|
126
|
+
class Time(dbtClassMixin, Mergeable):
|
|
127
|
+
count: Optional[int] = None
|
|
128
|
+
period: Optional[TimePeriod] = None
|
|
129
|
+
|
|
130
|
+
def exceeded(self, actual_age: float) -> bool:
|
|
131
|
+
if self.period is None or self.count is None:
|
|
132
|
+
return False
|
|
133
|
+
kwargs: Dict[str, int] = {self.period.plural(): self.count}
|
|
134
|
+
difference = timedelta(**kwargs).total_seconds()
|
|
135
|
+
return actual_age > difference
|
|
136
|
+
|
|
137
|
+
def __bool__(self):
|
|
138
|
+
return self.count is not None and self.period is not None
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass
|
|
142
|
+
class FreshnessThreshold(dbtClassMixin, Mergeable):
|
|
143
|
+
warn_after: Optional[Time] = field(default_factory=Time)
|
|
144
|
+
error_after: Optional[Time] = field(default_factory=Time)
|
|
145
|
+
filter: Optional[str] = None
|
|
146
|
+
|
|
147
|
+
def status(self, age: float) -> "dbt.artifacts.schemas.results.FreshnessStatus": # type: ignore # noqa F821
|
|
148
|
+
from dvt.artifacts.schemas.results import FreshnessStatus
|
|
149
|
+
|
|
150
|
+
if self.error_after and self.error_after.exceeded(age):
|
|
151
|
+
return FreshnessStatus.Error
|
|
152
|
+
elif self.warn_after and self.warn_after.exceeded(age):
|
|
153
|
+
return FreshnessStatus.Warn
|
|
154
|
+
else:
|
|
155
|
+
return FreshnessStatus.Pass
|
|
156
|
+
|
|
157
|
+
def __bool__(self):
|
|
158
|
+
return bool(self.warn_after) or bool(self.error_after)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@dataclass
|
|
162
|
+
class HasRelationMetadata(dbtClassMixin):
|
|
163
|
+
database: Optional[str]
|
|
164
|
+
schema: str
|
|
165
|
+
|
|
166
|
+
# Can't set database to None like it ought to be
|
|
167
|
+
# because it messes up the subclasses and default parameters
|
|
168
|
+
# so hack it here
|
|
169
|
+
@classmethod
|
|
170
|
+
def __pre_deserialize__(cls, data):
|
|
171
|
+
data = super().__pre_deserialize__(data)
|
|
172
|
+
if "database" not in data:
|
|
173
|
+
data["database"] = None
|
|
174
|
+
return data
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def quoting_dict(self) -> Dict[str, bool]:
|
|
178
|
+
if hasattr(self, "quoting"):
|
|
179
|
+
return self.quoting.to_dict(omit_none=True)
|
|
180
|
+
else:
|
|
181
|
+
return {}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@dataclass
|
|
185
|
+
class DeferRelation(HasRelationMetadata):
|
|
186
|
+
alias: str
|
|
187
|
+
relation_name: Optional[str]
|
|
188
|
+
# The rest of these fields match RelationConfig protocol exactly
|
|
189
|
+
resource_type: NodeType
|
|
190
|
+
name: str
|
|
191
|
+
description: str
|
|
192
|
+
compiled_code: Optional[str]
|
|
193
|
+
meta: Dict[str, Any]
|
|
194
|
+
tags: List[str]
|
|
195
|
+
config: Optional[NodeConfig]
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def identifier(self):
|
|
199
|
+
return self.alias
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@dataclass
|
|
203
|
+
class ParsedResourceMandatory(GraphResource, HasRelationMetadata):
|
|
204
|
+
alias: str
|
|
205
|
+
checksum: FileHash
|
|
206
|
+
config: NodeConfig = field(default_factory=NodeConfig)
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def identifier(self):
|
|
210
|
+
return self.alias
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@dataclass
|
|
214
|
+
class ParsedResource(ParsedResourceMandatory):
|
|
215
|
+
tags: List[str] = field(default_factory=list)
|
|
216
|
+
description: str = field(default="")
|
|
217
|
+
columns: Dict[str, ColumnInfo] = field(default_factory=dict)
|
|
218
|
+
meta: Dict[str, Any] = field(default_factory=dict)
|
|
219
|
+
group: Optional[str] = None
|
|
220
|
+
docs: Docs = field(default_factory=Docs)
|
|
221
|
+
patch_path: Optional[str] = None
|
|
222
|
+
build_path: Optional[str] = None
|
|
223
|
+
unrendered_config: Dict[str, Any] = field(default_factory=dict)
|
|
224
|
+
created_at: float = field(default_factory=lambda: time.time())
|
|
225
|
+
config_call_dict: Dict[str, Any] = field(default_factory=dict)
|
|
226
|
+
unrendered_config_call_dict: Dict[str, Any] = field(default_factory=dict)
|
|
227
|
+
relation_name: Optional[str] = None
|
|
228
|
+
raw_code: str = ""
|
|
229
|
+
doc_blocks: List[str] = field(default_factory=list)
|
|
230
|
+
|
|
231
|
+
def __post_serialize__(self, dct: Dict, context: Optional[Dict] = None):
|
|
232
|
+
dct = super().__post_serialize__(dct, context)
|
|
233
|
+
|
|
234
|
+
if context and context.get("artifact") and "config_call_dict" in dct:
|
|
235
|
+
del dct["config_call_dict"]
|
|
236
|
+
if context and context.get("artifact") and "unrendered_config_call_dict" in dct:
|
|
237
|
+
del dct["unrendered_config_call_dict"]
|
|
238
|
+
|
|
239
|
+
dct["doc_blocks"] = _backcompat_doc_blocks(dct["doc_blocks"])
|
|
240
|
+
|
|
241
|
+
return dct
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@dataclass
|
|
245
|
+
class CompiledResource(ParsedResource):
|
|
246
|
+
"""Contains attributes necessary for SQL files and nodes with refs, sources, etc,
|
|
247
|
+
so all ManifestNodes except SeedNode."""
|
|
248
|
+
|
|
249
|
+
language: str = "sql"
|
|
250
|
+
refs: List[RefArgs] = field(default_factory=list)
|
|
251
|
+
sources: List[List[str]] = field(default_factory=list)
|
|
252
|
+
metrics: List[List[str]] = field(default_factory=list)
|
|
253
|
+
functions: List[List[str]] = field(default_factory=list)
|
|
254
|
+
depends_on: DependsOn = field(default_factory=DependsOn)
|
|
255
|
+
compiled_path: Optional[str] = None
|
|
256
|
+
compiled: bool = False
|
|
257
|
+
compiled_code: Optional[str] = None
|
|
258
|
+
extra_ctes_injected: bool = False
|
|
259
|
+
extra_ctes: List[InjectedCTE] = field(default_factory=list)
|
|
260
|
+
_pre_injected_sql: Optional[str] = None
|
|
261
|
+
contract: Contract = field(default_factory=Contract)
|
|
262
|
+
|
|
263
|
+
def __post_serialize__(self, dct: Dict, context: Optional[Dict] = None):
|
|
264
|
+
dct = super().__post_serialize__(dct, context)
|
|
265
|
+
if "_pre_injected_sql" in dct:
|
|
266
|
+
del dct["_pre_injected_sql"]
|
|
267
|
+
# Remove compiled attributes
|
|
268
|
+
if "compiled" in dct and dct["compiled"] is False:
|
|
269
|
+
del dct["compiled"]
|
|
270
|
+
del dct["extra_ctes_injected"]
|
|
271
|
+
del dct["extra_ctes"]
|
|
272
|
+
# "omit_none" means these might not be in the dictionary
|
|
273
|
+
if "compiled_code" in dct:
|
|
274
|
+
del dct["compiled_code"]
|
|
275
|
+
return dct
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Any, Dict, List, Optional, Union
|
|
4
|
+
|
|
5
|
+
from dvt import hooks
|
|
6
|
+
from dvt.artifacts.resources.base import Docs
|
|
7
|
+
from dvt.artifacts.resources.types import ModelHookType
|
|
8
|
+
from dvt.artifacts.utils.validation import validate_color
|
|
9
|
+
from mashumaro.jsonschema.annotations import Pattern
|
|
10
|
+
from typing_extensions import Annotated
|
|
11
|
+
|
|
12
|
+
from dbt_common.contracts.config.base import BaseConfig, CompareBehavior, MergeBehavior
|
|
13
|
+
from dbt_common.contracts.config.materialization import OnConfigurationChangeOption
|
|
14
|
+
from dbt_common.contracts.config.metadata import Metadata, ShowBehavior
|
|
15
|
+
from dbt_common.dataclass_schema import ValidationError, dbtClassMixin
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def list_str() -> List[str]:
|
|
19
|
+
return []
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Severity(str):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def metas(*metas: Metadata) -> Dict[str, Any]:
|
|
27
|
+
existing: Dict[str, Any] = {}
|
|
28
|
+
for m in metas:
|
|
29
|
+
existing = m.meta(existing)
|
|
30
|
+
return existing
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class ContractConfig(dbtClassMixin):
|
|
35
|
+
enforced: bool = False
|
|
36
|
+
alias_types: bool = True
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class Hook(dbtClassMixin):
|
|
41
|
+
sql: str
|
|
42
|
+
transaction: bool = True
|
|
43
|
+
index: Optional[int] = None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class NodeAndTestConfig(BaseConfig):
|
|
48
|
+
enabled: bool = True
|
|
49
|
+
# these fields are included in serialized output, but are not part of
|
|
50
|
+
# config comparison (they are part of database_representation)
|
|
51
|
+
alias: Optional[str] = field(
|
|
52
|
+
default=None,
|
|
53
|
+
metadata=CompareBehavior.Exclude.meta(),
|
|
54
|
+
)
|
|
55
|
+
schema: Optional[str] = field(
|
|
56
|
+
default=None,
|
|
57
|
+
metadata=CompareBehavior.Exclude.meta(),
|
|
58
|
+
)
|
|
59
|
+
database: Optional[str] = field(
|
|
60
|
+
default=None,
|
|
61
|
+
metadata=CompareBehavior.Exclude.meta(),
|
|
62
|
+
)
|
|
63
|
+
tags: Union[List[str], str] = field(
|
|
64
|
+
default_factory=list_str,
|
|
65
|
+
metadata=metas(ShowBehavior.Hide, MergeBehavior.Append, CompareBehavior.Exclude),
|
|
66
|
+
)
|
|
67
|
+
meta: Dict[str, Any] = field(
|
|
68
|
+
default_factory=dict,
|
|
69
|
+
metadata=MergeBehavior.Update.meta(),
|
|
70
|
+
)
|
|
71
|
+
group: Optional[str] = field(
|
|
72
|
+
default=None,
|
|
73
|
+
metadata=CompareBehavior.Exclude.meta(),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass
|
|
78
|
+
class NodeConfig(NodeAndTestConfig):
|
|
79
|
+
# Note: if any new fields are added with MergeBehavior, also update the
|
|
80
|
+
# 'mergebehavior' dictionary
|
|
81
|
+
materialized: str = "view"
|
|
82
|
+
incremental_strategy: Optional[str] = None
|
|
83
|
+
batch_size: Any = None
|
|
84
|
+
lookback: Any = 1
|
|
85
|
+
begin: Any = None
|
|
86
|
+
persist_docs: Dict[str, Any] = field(default_factory=dict)
|
|
87
|
+
post_hook: List[Hook] = field(
|
|
88
|
+
default_factory=list,
|
|
89
|
+
metadata={"merge": MergeBehavior.Append, "alias": "post-hook"},
|
|
90
|
+
)
|
|
91
|
+
pre_hook: List[Hook] = field(
|
|
92
|
+
default_factory=list,
|
|
93
|
+
metadata={"merge": MergeBehavior.Append, "alias": "pre-hook"},
|
|
94
|
+
)
|
|
95
|
+
quoting: Dict[str, Any] = field(
|
|
96
|
+
default_factory=dict,
|
|
97
|
+
metadata=MergeBehavior.Update.meta(),
|
|
98
|
+
)
|
|
99
|
+
# This is actually only used by seeds. Should it be available to others?
|
|
100
|
+
# That would be a breaking change!
|
|
101
|
+
column_types: Dict[str, Any] = field(
|
|
102
|
+
default_factory=dict,
|
|
103
|
+
metadata=MergeBehavior.Update.meta(),
|
|
104
|
+
)
|
|
105
|
+
full_refresh: Optional[bool] = None
|
|
106
|
+
# 'unique_key' doesn't use 'Optional' because typing.get_type_hints was
|
|
107
|
+
# sometimes getting the Union order wrong, causing serialization failures.
|
|
108
|
+
unique_key: Union[str, List[str], None] = None
|
|
109
|
+
on_schema_change: Optional[str] = "ignore"
|
|
110
|
+
on_configuration_change: OnConfigurationChangeOption = field(
|
|
111
|
+
default_factory=OnConfigurationChangeOption.default
|
|
112
|
+
)
|
|
113
|
+
grants: Dict[str, Any] = field(
|
|
114
|
+
default_factory=dict, metadata=MergeBehavior.DictKeyAppend.meta()
|
|
115
|
+
)
|
|
116
|
+
packages: List[str] = field(
|
|
117
|
+
default_factory=list,
|
|
118
|
+
metadata=MergeBehavior.Append.meta(),
|
|
119
|
+
)
|
|
120
|
+
docs: Docs = field(
|
|
121
|
+
default_factory=Docs,
|
|
122
|
+
metadata=MergeBehavior.Update.meta(),
|
|
123
|
+
)
|
|
124
|
+
contract: ContractConfig = field(
|
|
125
|
+
default_factory=ContractConfig,
|
|
126
|
+
metadata=MergeBehavior.Update.meta(),
|
|
127
|
+
)
|
|
128
|
+
event_time: Any = None
|
|
129
|
+
concurrent_batches: Any = None
|
|
130
|
+
|
|
131
|
+
# DVT-specific configuration fields
|
|
132
|
+
compute_engine: Optional[str] = field(
|
|
133
|
+
default=None,
|
|
134
|
+
metadata=MergeBehavior.Clobber.meta(),
|
|
135
|
+
)
|
|
136
|
+
pushdown_enabled: Optional[bool] = field(
|
|
137
|
+
default=None,
|
|
138
|
+
metadata=MergeBehavior.Clobber.meta(),
|
|
139
|
+
)
|
|
140
|
+
target_profile: Optional[str] = field(
|
|
141
|
+
default=None,
|
|
142
|
+
metadata=MergeBehavior.Clobber.meta(),
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def __post_init__(self):
|
|
146
|
+
# we validate that node_color has a suitable value to prevent dbt-docs from crashing
|
|
147
|
+
if self.docs.node_color:
|
|
148
|
+
node_color = self.docs.node_color
|
|
149
|
+
if not validate_color(node_color):
|
|
150
|
+
raise ValidationError(
|
|
151
|
+
f"Invalid color name for docs.node_color: {node_color}. "
|
|
152
|
+
"It is neither a valid HTML color name nor a valid HEX code."
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
if (
|
|
156
|
+
self.contract.enforced
|
|
157
|
+
and self.materialized == "incremental"
|
|
158
|
+
and self.on_schema_change not in ("append_new_columns", "fail")
|
|
159
|
+
):
|
|
160
|
+
raise ValidationError(
|
|
161
|
+
f"Invalid value for on_schema_change: {self.on_schema_change}. Models "
|
|
162
|
+
"materialized as incremental with contracts enabled must set "
|
|
163
|
+
"on_schema_change to 'append_new_columns' or 'fail'"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
@classmethod
|
|
167
|
+
def __pre_deserialize__(cls, data):
|
|
168
|
+
data = super().__pre_deserialize__(data)
|
|
169
|
+
for key in ModelHookType:
|
|
170
|
+
if key in data:
|
|
171
|
+
data[key] = [hooks.get_hook_dict(h) for h in data[key]]
|
|
172
|
+
return data
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
SEVERITY_PATTERN = r"^([Ww][Aa][Rr][Nn]|[Ee][Rr][Rr][Oo][Rr])$"
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@dataclass
|
|
179
|
+
class TestConfig(NodeAndTestConfig):
|
|
180
|
+
__test__ = False
|
|
181
|
+
|
|
182
|
+
# this is repeated because of a different default
|
|
183
|
+
schema: Optional[str] = field(
|
|
184
|
+
default="dbt_test__audit",
|
|
185
|
+
metadata=CompareBehavior.Exclude.meta(),
|
|
186
|
+
)
|
|
187
|
+
materialized: str = "test"
|
|
188
|
+
# Annotated is used by mashumaro for jsonschema generation
|
|
189
|
+
severity: Annotated[Severity, Pattern(SEVERITY_PATTERN)] = Severity("ERROR")
|
|
190
|
+
store_failures: Optional[bool] = None
|
|
191
|
+
store_failures_as: Optional[str] = None
|
|
192
|
+
where: Optional[str] = None
|
|
193
|
+
limit: Optional[int] = None
|
|
194
|
+
fail_calc: str = "count(*)"
|
|
195
|
+
warn_if: str = "!= 0"
|
|
196
|
+
error_if: str = "!= 0"
|
|
197
|
+
|
|
198
|
+
def finalize_and_validate(self):
|
|
199
|
+
"""
|
|
200
|
+
The presence of a setting for `store_failures_as` overrides any existing setting for `store_failures`,
|
|
201
|
+
regardless of level of granularity. If `store_failures_as` is not set, then `store_failures` takes effect.
|
|
202
|
+
At the time of implementation, `store_failures = True` would always create a table; the user could not
|
|
203
|
+
configure this. Hence, if `store_failures = True` and `store_failures_as` is not specified, then it
|
|
204
|
+
should be set to "table" to mimic the existing functionality.
|
|
205
|
+
|
|
206
|
+
A side effect of this overriding functionality is that `store_failures_as="view"` at the project
|
|
207
|
+
level cannot be turned off at the model level without setting both `store_failures_as` and
|
|
208
|
+
`store_failures`. The former would cascade down and override `store_failures=False`. The proposal
|
|
209
|
+
is to include "ephemeral" as a value for `store_failures_as`, which effectively sets
|
|
210
|
+
`store_failures=False`.
|
|
211
|
+
|
|
212
|
+
The exception handling for this is tricky. If we raise an exception here, the entire run fails at
|
|
213
|
+
parse time. We would rather well-formed models run successfully, leaving only exceptions to be rerun
|
|
214
|
+
if necessary. Hence, the exception needs to be raised in the test materialization. In order to do so,
|
|
215
|
+
we need to make sure that we go down the `store_failures = True` route with the invalid setting for
|
|
216
|
+
`store_failures_as`. This results in the `.get()` defaulted to `True` below, instead of a normal
|
|
217
|
+
dictionary lookup as is done in the `if` block. Refer to the test materialization for the
|
|
218
|
+
exception that is raise as a result of an invalid value.
|
|
219
|
+
|
|
220
|
+
The intention of this block is to behave as if `store_failures_as` is the only setting,
|
|
221
|
+
but still allow for backwards compatibility for `store_failures`.
|
|
222
|
+
See https://github.com/dbt-labs/dbt-core/issues/6914 for more information.
|
|
223
|
+
"""
|
|
224
|
+
super().finalize_and_validate()
|
|
225
|
+
|
|
226
|
+
# if `store_failures_as` is not set, it gets set by `store_failures`
|
|
227
|
+
# the settings below mimic existing behavior prior to `store_failures_as`
|
|
228
|
+
get_store_failures_as_map = {
|
|
229
|
+
True: "table",
|
|
230
|
+
False: "ephemeral",
|
|
231
|
+
None: None,
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
# if `store_failures_as` is set, it dictates what `store_failures` gets set to
|
|
235
|
+
# the settings below overrides whatever `store_failures` is set to by the user
|
|
236
|
+
get_store_failures_map = {
|
|
237
|
+
"ephemeral": False,
|
|
238
|
+
"table": True,
|
|
239
|
+
"view": True,
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if self.store_failures_as is None:
|
|
243
|
+
self.store_failures_as = get_store_failures_as_map[self.store_failures]
|
|
244
|
+
else:
|
|
245
|
+
self.store_failures = get_store_failures_map.get(self.store_failures_as, True)
|
|
246
|
+
|
|
247
|
+
return self
|
|
248
|
+
|
|
249
|
+
@classmethod
|
|
250
|
+
def same_contents(cls, unrendered: Dict[str, Any], other: Dict[str, Any]) -> bool:
|
|
251
|
+
"""This is like __eq__, except it explicitly checks certain fields."""
|
|
252
|
+
modifiers = [
|
|
253
|
+
"severity",
|
|
254
|
+
"where",
|
|
255
|
+
"limit",
|
|
256
|
+
"fail_calc",
|
|
257
|
+
"warn_if",
|
|
258
|
+
"error_if",
|
|
259
|
+
"store_failures",
|
|
260
|
+
"store_failures_as",
|
|
261
|
+
]
|
|
262
|
+
|
|
263
|
+
seen = set()
|
|
264
|
+
for _, target_name in cls._get_fields():
|
|
265
|
+
key = target_name
|
|
266
|
+
seen.add(key)
|
|
267
|
+
if key in modifiers:
|
|
268
|
+
if not cls.compare_key(unrendered, other, key):
|
|
269
|
+
return False
|
|
270
|
+
return True
|
|
271
|
+
|
|
272
|
+
@classmethod
|
|
273
|
+
def validate(cls, data):
|
|
274
|
+
if data.get("severity") and not re.match(SEVERITY_PATTERN, data.get("severity")):
|
|
275
|
+
raise ValidationError(
|
|
276
|
+
f"Severity must be either 'warn' or 'error'. Got '{data.get('severity')}'"
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
super().validate(data)
|
|
280
|
+
|
|
281
|
+
if data.get("materialized") and data.get("materialized") != "test":
|
|
282
|
+
raise ValidationError("A test must have a materialized value of 'test'")
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
from dvt.artifacts.resources.base import BaseResource
|
|
5
|
+
from dvt.artifacts.resources.types import NodeType
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Documentation(BaseResource):
|
|
10
|
+
resource_type: Literal[NodeType.Documentation]
|
|
11
|
+
block_contents: str
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Any, Dict, List, Literal, Optional
|
|
4
|
+
|
|
5
|
+
from dvt.artifacts.resources.base import GraphResource
|
|
6
|
+
from dvt.artifacts.resources.types import NodeType
|
|
7
|
+
from dvt.artifacts.resources.v1.components import DependsOn, RefArgs
|
|
8
|
+
from dvt.artifacts.resources.v1.owner import Owner
|
|
9
|
+
|
|
10
|
+
from dbt_common.contracts.config.base import BaseConfig
|
|
11
|
+
from dbt_common.dataclass_schema import StrEnum
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ExposureType(StrEnum):
|
|
15
|
+
Dashboard = "dashboard"
|
|
16
|
+
Notebook = "notebook"
|
|
17
|
+
Analysis = "analysis"
|
|
18
|
+
ML = "ml"
|
|
19
|
+
Application = "application"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MaturityType(StrEnum):
|
|
23
|
+
Low = "low"
|
|
24
|
+
Medium = "medium"
|
|
25
|
+
High = "high"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class ExposureConfig(BaseConfig):
|
|
30
|
+
enabled: bool = True
|
|
31
|
+
tags: List[str] = field(default_factory=list)
|
|
32
|
+
meta: Dict[str, Any] = field(default_factory=dict)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class Exposure(GraphResource):
|
|
37
|
+
type: ExposureType
|
|
38
|
+
owner: Owner
|
|
39
|
+
resource_type: Literal[NodeType.Exposure]
|
|
40
|
+
description: str = ""
|
|
41
|
+
label: Optional[str] = None
|
|
42
|
+
maturity: Optional[MaturityType] = None
|
|
43
|
+
meta: Dict[str, Any] = field(default_factory=dict)
|
|
44
|
+
tags: List[str] = field(default_factory=list)
|
|
45
|
+
config: ExposureConfig = field(default_factory=ExposureConfig)
|
|
46
|
+
unrendered_config: Dict[str, Any] = field(default_factory=dict)
|
|
47
|
+
url: Optional[str] = None
|
|
48
|
+
depends_on: DependsOn = field(default_factory=DependsOn)
|
|
49
|
+
refs: List[RefArgs] = field(default_factory=list)
|
|
50
|
+
sources: List[List[str]] = field(default_factory=list)
|
|
51
|
+
metrics: List[List[str]] = field(default_factory=list)
|
|
52
|
+
created_at: float = field(default_factory=lambda: time.time())
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import List, Literal, Optional
|
|
3
|
+
|
|
4
|
+
from dvt.artifacts.resources.types import FunctionType, FunctionVolatility, NodeType
|
|
5
|
+
from dvt.artifacts.resources.v1.components import CompiledResource
|
|
6
|
+
from dvt.artifacts.resources.v1.config import NodeConfig
|
|
7
|
+
|
|
8
|
+
from dbt_common.dataclass_schema import dbtClassMixin
|
|
9
|
+
|
|
10
|
+
# =============
|
|
11
|
+
# Function config, and supporting classes
|
|
12
|
+
# =============
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class FunctionConfig(NodeConfig):
|
|
17
|
+
# The fact that this is a property, that can be changed, seems wrong.
|
|
18
|
+
# A function's materialization should never be changed, so why allow for it?
|
|
19
|
+
materialized: str = "function"
|
|
20
|
+
type: FunctionType = FunctionType.Scalar
|
|
21
|
+
volatility: Optional[FunctionVolatility] = None
|
|
22
|
+
runtime_version: Optional[str] = None
|
|
23
|
+
entry_point: Optional[str] = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# =============
|
|
27
|
+
# Function resource, and supporting classes
|
|
28
|
+
# =============
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class FunctionArgument(dbtClassMixin):
|
|
33
|
+
name: str
|
|
34
|
+
data_type: str
|
|
35
|
+
description: Optional[str] = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class FunctionReturns(dbtClassMixin):
|
|
40
|
+
data_type: str
|
|
41
|
+
description: Optional[str] = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class FunctionMandatory(dbtClassMixin):
|
|
46
|
+
returns: FunctionReturns
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class Function(CompiledResource, FunctionMandatory):
|
|
51
|
+
resource_type: Literal[NodeType.Function]
|
|
52
|
+
config: FunctionConfig
|
|
53
|
+
arguments: List[FunctionArgument] = field(default_factory=list)
|