Skip to content

Commit 65ee25f

Browse files
jasonlin45fa-assistant
authored andcommitted
Serialize configs from Jinja via resource_types (#4081)
* Serialize configs from Jinja via resource_types * Add docs * Clean up todos GitOrigin-RevId: ce978753cb3a7b4d554cd6c3f1ec9bf5c3e3e014
1 parent bf019bd commit 65ee25f

File tree

5 files changed

+108
-32
lines changed

5 files changed

+108
-32
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
kind: Under the Hood
2+
body: Serialize configs via resource_types
3+
time: 2025-06-23T22:37:06.040844497-07:00

crates/dbt-fusion-adapter/src/bridge_adapter.rs

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use dbt_schemas::schemas::manifest::{
3030
use dbt_schemas::schemas::project::ModelConfig;
3131
use dbt_schemas::schemas::properties::ModelConstraint;
3232
use dbt_schemas::schemas::relations::base::{BaseRelation, ComponentName};
33-
use dbt_schemas::schemas::{CommonAttributes, CommonAttributesWrapper, DbtModel};
33+
use dbt_schemas::schemas::InternalDbtNodeWrapper;
3434
use dbt_xdbc::Connection;
3535
use minijinja::arg_utils::{check_num_args, ArgParser};
3636
use minijinja::dispatch_object::DispatchObject;
@@ -927,18 +927,17 @@ impl BaseAdapter for BridgeAdapter {
927927
)
928928
})?;
929929

930-
let common_attr = CommonAttributes::deserialize(node).map_err(|e| {
930+
let node_wrapper = InternalDbtNodeWrapper::deserialize(node).map_err(|e| {
931931
MinijinjaError::new(
932932
MinijinjaErrorKind::SerdeDeserializeError,
933-
format!("get_table_options: Failed to deserialize common attributes: {e}"),
933+
format!("get_table_options: Failed to deserialize InternalDbtNodeWrapper: {e}"),
934934
)
935935
})?;
936+
let node = node_wrapper.as_internal_node();
936937

937-
let options = self.typed_adapter.get_table_options(
938-
config,
939-
CommonAttributesWrapper(common_attr),
940-
temporary,
941-
)?;
938+
let options = self
939+
.typed_adapter
940+
.get_table_options(config, node.common(), temporary)?;
942941
Ok(Value::from_serialize(options))
943942
}
944943

@@ -957,16 +956,15 @@ impl BaseAdapter for BridgeAdapter {
957956
)
958957
})?;
959958

960-
let common_attr = CommonAttributes::deserialize(node).map_err(|e| {
959+
let node_wrapper = InternalDbtNodeWrapper::deserialize(node).map_err(|e| {
961960
MinijinjaError::new(
962961
MinijinjaErrorKind::SerdeDeserializeError,
963-
format!("get_view_options: Failed to deserialize common attributes: {e}"),
962+
format!("get_table_options: Failed to deserialize InternalDbtNodeWrapper: {e}"),
964963
)
965964
})?;
965+
let node = node_wrapper.as_internal_node();
966966

967-
let options = self
968-
.typed_adapter
969-
.get_view_options(config, CommonAttributesWrapper(common_attr))?;
967+
let options = self.typed_adapter.get_view_options(config, node.common())?;
970968
Ok(Value::from_serialize(options))
971969
}
972970

@@ -1181,13 +1179,22 @@ impl BaseAdapter for BridgeAdapter {
11811179
let config = ModelConfig::deserialize(config).map_err(|e| {
11821180
MinijinjaError::new(MinijinjaErrorKind::SerdeDeserializeError, e.to_string())
11831181
})?;
1184-
let model = DbtModel::deserialize(model).map_err(|e| {
1185-
MinijinjaError::new(MinijinjaErrorKind::SerdeDeserializeError, e.to_string())
1182+
1183+
let node = InternalDbtNodeWrapper::deserialize(model).map_err(|e| {
1184+
MinijinjaError::new(
1185+
MinijinjaErrorKind::SerdeDeserializeError,
1186+
format!(
1187+
"adapter.compute_external_path expected an InternalDbtNodeWrapper: {}",
1188+
e
1189+
),
1190+
)
11861191
})?;
11871192

1188-
let result = self
1189-
.typed_adapter
1190-
.compute_external_path(config, model, is_incremental)?;
1193+
let result = self.typed_adapter.compute_external_path(
1194+
config,
1195+
node.as_internal_node(),
1196+
is_incremental,
1197+
)?;
11911198
Ok(Value::from(result))
11921199
}
11931200

@@ -1348,12 +1355,27 @@ impl BaseAdapter for BridgeAdapter {
13481355
check_num_args(current_function_name!(), &parser, 1, 1)?;
13491356

13501357
let model = parser.get::<Value>("model")?;
1351-
let model = DbtModel::deserialize(model).map_err(|e| {
1358+
1359+
let deserialized_node = InternalDbtNodeWrapper::deserialize(model).map_err(|e| {
13521360
MinijinjaError::new(
13531361
MinijinjaErrorKind::SerdeDeserializeError,
1354-
format!("adapter.get_config_from_model expected a DbtModel: {}", e),
1362+
format!(
1363+
"adapter.get_config_from_model expected an InternalDbtNodeWrapper: {}",
1364+
e
1365+
),
13551366
)
13561367
})?;
1368+
1369+
let model = match deserialized_node {
1370+
InternalDbtNodeWrapper::Model(model) => model,
1371+
_ => {
1372+
return Err(MinijinjaError::new(
1373+
MinijinjaErrorKind::InvalidOperation,
1374+
"adapter.get_config_from_model expected a DbtModel node".to_string(),
1375+
))
1376+
}
1377+
};
1378+
13571379
Ok(self.typed_adapter.get_config_from_model(&model)?)
13581380
}
13591381

crates/dbt-fusion-adapter/src/typed_adapter.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use dbt_schemas::schemas::dbt_column::DbtColumn;
2525
use dbt_schemas::schemas::manifest::{BigqueryClusterConfig, BigqueryPartitionConfig};
2626
use dbt_schemas::schemas::project::ModelConfig;
2727
use dbt_schemas::schemas::relations::base::{BaseRelation, ComponentName};
28-
use dbt_schemas::schemas::{CommonAttributesWrapper, DbtModel};
28+
use dbt_schemas::schemas::{CommonAttributes, DbtModel, InternalDbtNodeAttributes};
2929
use dbt_xdbc::{Connection, QueryCtx};
3030
use minijinja::{args, State, Value};
3131

@@ -623,7 +623,7 @@ pub trait TypedBaseAdapter: fmt::Debug + Send + Sync + AdapterTyping {
623623
fn get_table_options(
624624
&self,
625625
_config: ModelConfig,
626-
_node: CommonAttributesWrapper,
626+
_node: &CommonAttributes,
627627
_temporary: bool,
628628
) -> AdapterResult<BTreeMap<String, Value>> {
629629
unimplemented!("only available with BigQuery adapter")
@@ -633,7 +633,7 @@ pub trait TypedBaseAdapter: fmt::Debug + Send + Sync + AdapterTyping {
633633
fn get_view_options(
634634
&self,
635635
_config: ModelConfig,
636-
_node: CommonAttributesWrapper,
636+
_node: &CommonAttributes,
637637
) -> AdapterResult<BTreeMap<String, Value>> {
638638
unimplemented!("only available with BigQuery adapter")
639639
}
@@ -680,7 +680,7 @@ pub trait TypedBaseAdapter: fmt::Debug + Send + Sync + AdapterTyping {
680680
fn compute_external_path(
681681
&self,
682682
_config: ModelConfig,
683-
_model: DbtModel,
683+
_node: &dyn InternalDbtNodeAttributes,
684684
_is_incremental: bool,
685685
) -> AdapterResult<String> {
686686
unimplemented!("only available with Databricks adapter")

crates/dbt-schemas/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ pub mod schemas {
2626

2727
mod nodes;
2828
pub use nodes::{
29-
CommonAttributes, CommonAttributesWrapper, DbtModel, DbtSeed, DbtSnapshot, DbtSource,
30-
DbtTest, DbtUnitTest, InternalDbtNode, InternalDbtNodeAttributes, IntrospectionKind,
29+
CommonAttributes, DbtModel, DbtSeed, DbtSnapshot, DbtSource, DbtTest, DbtUnitTest,
30+
InternalDbtNode, InternalDbtNodeAttributes, InternalDbtNodeWrapper, IntrospectionKind,
3131
NodeBaseAttributes, Nodes,
3232
};
3333

crates/dbt-schemas/src/schemas/nodes.rs

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,63 @@ impl Display for IntrospectionKind {
4444
}
4545
}
4646

47+
/// A wrapper enum that represents different types of dbt nodes.
48+
///
49+
/// This enum uses serde's tag-based deserialization to automatically determine
50+
/// the correct variant based on the "resource_type" field in the JSON.
51+
/// The resource_type values are converted to snake_case for matching.
52+
///
53+
/// # Example
54+
///
55+
/// ```rust
56+
///
57+
/// // Deserialize a node from Jinja
58+
/// let node = InternalDbtNodeWrapper::deserialize(value).unwrap();
59+
///
60+
/// // Access the underlying node attributes
61+
/// let attributes = node.as_internal_node();
62+
/// ```
63+
#[derive(Debug, Clone, Serialize, Deserialize)]
64+
#[serde(tag = "resource_type")]
65+
#[serde(rename_all = "snake_case")]
66+
pub enum InternalDbtNodeWrapper {
67+
Model(DbtModel),
68+
Seed(DbtSeed),
69+
Test(DbtTest),
70+
UnitTest(DbtUnitTest),
71+
Source(DbtSource),
72+
Snapshot(DbtSnapshot),
73+
}
74+
75+
impl InternalDbtNodeWrapper {
76+
/// Returns a reference to the underlying node as a trait object.
77+
///
78+
/// This method allows accessing common functionality across all node types
79+
/// through the `InternalDbtNodeAttributes` trait.
80+
///
81+
/// # Returns
82+
///
83+
/// A reference to the node implementing `InternalDbtNodeAttributes`
84+
///
85+
/// # Examples
86+
///
87+
/// ```rust
88+
/// let node = InternalDbtNodeWrapper::Model(some_model);
89+
/// let attributes = node.as_internal_node();
90+
/// println!("Node name: {}", attributes.name());
91+
/// ```
92+
pub fn as_internal_node(&self) -> &dyn InternalDbtNodeAttributes {
93+
match self {
94+
InternalDbtNodeWrapper::Model(model) => model,
95+
InternalDbtNodeWrapper::Seed(seed) => seed,
96+
InternalDbtNodeWrapper::Test(test) => test,
97+
InternalDbtNodeWrapper::UnitTest(unit_test) => unit_test,
98+
InternalDbtNodeWrapper::Source(source) => source,
99+
InternalDbtNodeWrapper::Snapshot(snapshot) => snapshot,
100+
}
101+
}
102+
}
103+
47104
pub trait InternalDbtNode: Any + Send + Sync + fmt::Debug {
48105
fn common(&self) -> &CommonAttributes;
49106
fn base(&self) -> NodeBaseAttributes;
@@ -1199,12 +1256,6 @@ pub struct CommonAttributes {
11991256
pub description: Option<String>,
12001257
}
12011258

1202-
// Workaround to larger issue: https://github.com/dbt-labs/fs/issues/4068
1203-
// We need to serialize configs from Jinja flexibly to access type attributes
1204-
// without a wrapper type or enum.
1205-
#[derive(Debug, Clone)]
1206-
pub struct CommonAttributesWrapper(pub CommonAttributes);
1207-
12081259
#[skip_serializing_none]
12091260
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
12101261
#[serde(rename_all = "snake_case")]

0 commit comments

Comments
 (0)