Skip to content

Commit e419bc5

Browse files
Sujay JayakarConvex, Inc.
authored andcommitted
feat(components): Add enum to component metadata for distinguishing the root app from child components (#26097)
This moves to an enum so we don't have synthesize `name` and `args` for the app. We can let these two variants diverge more over time. One interesting bit is that I left the `path: ComponentDefinitionPath` field alone, which means that the root app has definition path `""`, which makes sense as a relative path from `"convex/"`. GitOrigin-RevId: b4d085cc7b4fd576557bb710298ae08b3e538b7c
1 parent b5471a6 commit e419bc5

File tree

5 files changed

+154
-67
lines changed

5 files changed

+154
-67
lines changed

crates/application/src/tests/components.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ use common::{
66
ComponentArgument,
77
ComponentArgumentValidator,
88
ComponentDefinitionMetadata,
9+
ComponentDefinitionType,
910
ComponentExport,
1011
ComponentInstantiation,
1112
},
1213
ComponentMetadata,
14+
ComponentType,
1315
},
1416
components::{
1517
ComponentFunctionPath,
@@ -138,8 +140,7 @@ bar.invokeAction = async (requestId, argsStr) => {
138140
let root_component_id = {
139141
let definition = ComponentDefinitionMetadata {
140142
path: "".parse()?,
141-
name: "app".parse()?,
142-
args: btreemap! {},
143+
definition_type: ComponentDefinitionType::App,
143144
child_components: vec![ComponentInstantiation {
144145
name: "chatWaitlist".parse()?,
145146
path: "../node_modules/waitlist".parse()?,
@@ -159,8 +160,7 @@ bar.invokeAction = async (requestId, argsStr) => {
159160

160161
let component = ComponentMetadata {
161162
definition_id: definition_id.internal_id(),
162-
parent_and_name: None,
163-
args: btreemap! {},
163+
component_type: ComponentType::App,
164164
};
165165
let component_id = SystemMetadataModel::new_global(&mut tx)
166166
.insert(&COMPONENTS_TABLE, component.try_into()?)
@@ -171,9 +171,11 @@ bar.invokeAction = async (requestId, argsStr) => {
171171
{
172172
let definition = ComponentDefinitionMetadata {
173173
path: "../node_modules/waitlist".parse()?,
174-
name: "waitlist".parse()?,
175-
args: btreemap! {
176-
"maxLength".parse()? => ComponentArgumentValidator::Value(Validator::Float64),
174+
definition_type: ComponentDefinitionType::ChildComponent {
175+
name: "waitlist".parse()?,
176+
args: btreemap! {
177+
"maxLength".parse()? => ComponentArgumentValidator::Value(Validator::Float64),
178+
},
177179
},
178180
child_components: vec![],
179181
exports: btreemap! {
@@ -188,9 +190,12 @@ bar.invokeAction = async (requestId, argsStr) => {
188190

189191
let component = ComponentMetadata {
190192
definition_id: definition_id.internal_id(),
191-
parent_and_name: Some((root_component_id, "chatWaitlist".parse()?)),
192-
args: btreemap! {
193-
"maxLength".parse()? => Resource::Value(ConvexValue::Float64(10.)),
193+
component_type: ComponentType::ChildComponent {
194+
parent: root_component_id,
195+
name: "chatWaitlist".parse()?,
196+
args: btreemap! {
197+
"maxLength".parse()? => Resource::Value(ConvexValue::Float64(10.)),
198+
},
194199
},
195200
};
196201
SystemMetadataModel::new_global(&mut tx)

crates/common/src/bootstrap_model/components/definition.rs

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,21 @@ use crate::{
2424
#[cfg_attr(any(test, feature = "testing"), derive(proptest_derive::Arbitrary))]
2525
pub struct ComponentDefinitionMetadata {
2626
pub path: ComponentDefinitionPath,
27-
pub name: ComponentName,
28-
pub args: BTreeMap<Identifier, ComponentArgumentValidator>,
27+
pub definition_type: ComponentDefinitionType,
2928
pub child_components: Vec<ComponentInstantiation>,
3029
pub exports: BTreeMap<Identifier, ComponentExport>,
3130
}
3231

32+
#[derive(Debug, Clone, Eq, PartialEq)]
33+
#[cfg_attr(any(test, feature = "testing"), derive(proptest_derive::Arbitrary))]
34+
pub enum ComponentDefinitionType {
35+
App,
36+
ChildComponent {
37+
name: ComponentName,
38+
args: BTreeMap<Identifier, ComponentArgumentValidator>,
39+
},
40+
}
41+
3342
#[derive(Debug, Clone, Eq, PartialEq)]
3443
pub struct ComponentInstantiation {
3544
pub name: ComponentName,
@@ -56,14 +65,24 @@ pub enum ComponentArgument {
5665
}
5766

5867
#[derive(Debug, Serialize, Deserialize)]
68+
#[serde(rename_all = "camelCase")]
5969
struct SerializedComponentDefinitionMetadata {
6070
path: String,
61-
name: String,
62-
inputs: Vec<(String, SerializedComponentArgumentValidator)>,
71+
definition_type: SerializedComponentDefinitionType,
6372
child_components: Vec<SerializedComponentInstantiation>,
6473
exports: SerializedComponentExport,
6574
}
6675

76+
#[derive(Debug, Serialize, Deserialize)]
77+
#[serde(tag = "type", rename_all = "camelCase")]
78+
enum SerializedComponentDefinitionType {
79+
App {},
80+
ChildComponent {
81+
name: String,
82+
args: Vec<(String, SerializedComponentArgumentValidator)>,
83+
},
84+
}
85+
6786
#[derive(Debug, Serialize, Deserialize)]
6887
#[serde(tag = "type", rename_all = "camelCase")]
6988
enum SerializedComponentArgumentValidator {
@@ -101,12 +120,7 @@ impl TryFrom<ComponentDefinitionMetadata> for SerializedComponentDefinitionMetad
101120
fn try_from(m: ComponentDefinitionMetadata) -> anyhow::Result<Self> {
102121
Ok(Self {
103122
path: String::from(m.path),
104-
name: String::from(m.name),
105-
inputs: m
106-
.args
107-
.into_iter()
108-
.map(|(k, v)| anyhow::Ok((String::from(k), v.try_into()?)))
109-
.try_collect()?,
123+
definition_type: m.definition_type.try_into()?,
110124
child_components: m
111125
.child_components
112126
.into_iter()
@@ -126,12 +140,7 @@ impl TryFrom<SerializedComponentDefinitionMetadata> for ComponentDefinitionMetad
126140
};
127141
Ok(Self {
128142
path: m.path.parse()?,
129-
name: m.name.parse()?,
130-
args: m
131-
.inputs
132-
.into_iter()
133-
.map(|(k, v)| anyhow::Ok((k.parse()?, v.try_into()?)))
134-
.try_collect()?,
143+
definition_type: m.definition_type.try_into()?,
135144
child_components: m
136145
.child_components
137146
.into_iter()
@@ -142,6 +151,42 @@ impl TryFrom<SerializedComponentDefinitionMetadata> for ComponentDefinitionMetad
142151
}
143152
}
144153

154+
impl TryFrom<ComponentDefinitionType> for SerializedComponentDefinitionType {
155+
type Error = anyhow::Error;
156+
157+
fn try_from(t: ComponentDefinitionType) -> anyhow::Result<Self> {
158+
Ok(match t {
159+
ComponentDefinitionType::App => Self::App {},
160+
ComponentDefinitionType::ChildComponent { name, args } => Self::ChildComponent {
161+
name: name.to_string(),
162+
args: args
163+
.into_iter()
164+
.map(|(k, v)| anyhow::Ok((String::from(k), v.try_into()?)))
165+
.try_collect()?,
166+
},
167+
})
168+
}
169+
}
170+
171+
impl TryFrom<SerializedComponentDefinitionType> for ComponentDefinitionType {
172+
type Error = anyhow::Error;
173+
174+
fn try_from(t: SerializedComponentDefinitionType) -> anyhow::Result<Self> {
175+
Ok(match t {
176+
SerializedComponentDefinitionType::App {} => Self::App,
177+
SerializedComponentDefinitionType::ChildComponent { name, args } => {
178+
Self::ChildComponent {
179+
name: name.parse()?,
180+
args: args
181+
.into_iter()
182+
.map(|(k, v)| anyhow::Ok((k.parse()?, v.try_into()?)))
183+
.try_collect()?,
184+
}
185+
},
186+
})
187+
}
188+
}
189+
145190
impl TryFrom<ComponentInstantiation> for SerializedComponentInstantiation {
146191
type Error = anyhow::Error;
147192

crates/common/src/bootstrap_model/components/mod.rs

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,35 +22,50 @@ use crate::components::{
2222
#[cfg_attr(any(test, feature = "testing"), derive(proptest_derive::Arbitrary))]
2323
pub struct ComponentMetadata {
2424
pub definition_id: InternalId,
25-
pub parent_and_name: Option<(InternalId, ComponentName)>,
26-
pub args: BTreeMap<Identifier, Resource>,
25+
pub component_type: ComponentType,
26+
}
27+
28+
#[derive(Debug, Clone, Eq, PartialEq)]
29+
#[cfg_attr(any(test, feature = "testing"), derive(proptest_derive::Arbitrary))]
30+
pub enum ComponentType {
31+
App,
32+
ChildComponent {
33+
parent: InternalId,
34+
name: ComponentName,
35+
args: BTreeMap<Identifier, Resource>,
36+
},
2737
}
2838

2939
#[derive(Debug, Serialize, Deserialize)]
40+
#[serde(rename_all = "camelCase")]
3041
struct SerializedComponentMetadata {
3142
pub definition_id: String,
3243
pub parent: Option<String>,
3344
pub name: Option<String>,
34-
pub args: Vec<(String, SerializedResource)>,
45+
pub args: Option<Vec<(String, SerializedResource)>>,
3546
}
3647

3748
impl TryFrom<ComponentMetadata> for SerializedComponentMetadata {
3849
type Error = anyhow::Error;
3950

4051
fn try_from(m: ComponentMetadata) -> anyhow::Result<Self> {
41-
let (parent, name) = match m.parent_and_name {
42-
Some((parent, name)) => (Some(parent.to_string()), Some(name.to_string())),
43-
None => (None, None),
52+
let (parent, name, args) = match m.component_type {
53+
ComponentType::App => (None, None, None),
54+
ComponentType::ChildComponent { parent, name, args } => (
55+
Some(parent.to_string()),
56+
Some(name.to_string()),
57+
Some(
58+
args.into_iter()
59+
.map(|(k, v)| anyhow::Ok((k.to_string(), v.try_into()?)))
60+
.try_collect()?,
61+
),
62+
),
4463
};
4564
Ok(Self {
4665
definition_id: m.definition_id.to_string(),
4766
parent,
4867
name,
49-
args: m
50-
.args
51-
.into_iter()
52-
.map(|(k, v)| anyhow::Ok((String::from(k), v.try_into()?)))
53-
.try_collect()?,
68+
args,
5469
})
5570
}
5671
}
@@ -59,18 +74,21 @@ impl TryFrom<SerializedComponentMetadata> for ComponentMetadata {
5974
type Error = anyhow::Error;
6075

6176
fn try_from(m: SerializedComponentMetadata) -> anyhow::Result<Self> {
77+
let component_type = match (m.parent, m.name, m.args) {
78+
(None, None, None) => ComponentType::App,
79+
(Some(parent), Some(name), Some(args)) => ComponentType::ChildComponent {
80+
parent: parent.parse()?,
81+
name: name.parse()?,
82+
args: args
83+
.into_iter()
84+
.map(|(k, v)| Ok((k.parse()?, v.try_into()?)))
85+
.collect::<anyhow::Result<_>>()?,
86+
},
87+
_ => anyhow::bail!("Invalid component type"),
88+
};
6289
Ok(Self {
6390
definition_id: m.definition_id.parse()?,
64-
parent_and_name: match (m.parent, m.name) {
65-
(Some(parent), Some(name)) => Some((parent.parse()?, name.parse()?)),
66-
(None, None) => None,
67-
_ => anyhow::bail!("expected both parent and name or neither"),
68-
},
69-
args: m
70-
.args
71-
.into_iter()
72-
.map(|(k, v)| anyhow::Ok((k.parse()?, v.try_into()?)))
73-
.try_collect()?,
91+
component_type,
7492
})
7593
}
7694
}

crates/database/src/bootstrap_model/components/mod.rs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use common::{
77
bootstrap_model::components::{
88
definition::ComponentDefinitionMetadata,
99
ComponentMetadata,
10+
ComponentType,
1011
},
1112
components::{
1213
CanonicalizedComponentFunctionPath,
@@ -161,12 +162,12 @@ impl<'a, RT: Runtime> BootstrapComponentsModel<'a, RT> {
161162
.await?
162163
.with_context(|| format!("component {internal_id} missing"))?
163164
.try_into()?;
164-
component_id = match &component_doc.parent_and_name {
165-
Some((parent_internal_id, name)) => {
165+
component_id = match &component_doc.component_type {
166+
ComponentType::App => ComponentId::Root,
167+
ComponentType::ChildComponent { parent, name, .. } => {
166168
path.push(name.clone());
167-
ComponentId::Child(*parent_internal_id)
169+
ComponentId::Child(*parent)
168170
},
169-
None => ComponentId::Root,
170171
};
171172
}
172173
path.reverse();
@@ -266,9 +267,11 @@ mod tests {
266267
bootstrap_model::components::{
267268
definition::{
268269
ComponentDefinitionMetadata,
270+
ComponentDefinitionType,
269271
ComponentInstantiation,
270272
},
271273
ComponentMetadata,
274+
ComponentType,
272275
},
273276
components::{
274277
ComponentDefinitionPath,
@@ -298,9 +301,11 @@ mod tests {
298301
.insert(
299302
&COMPONENT_DEFINITIONS_TABLE,
300303
ComponentDefinitionMetadata {
301-
name: "child".parse().unwrap(),
302304
path: child_definition_path.clone(),
303-
args: BTreeMap::new(),
305+
definition_type: ComponentDefinitionType::ChildComponent {
306+
name: "child".parse().unwrap(),
307+
args: BTreeMap::new(),
308+
},
304309
child_components: Vec::new(),
305310
exports: BTreeMap::new(),
306311
}
@@ -311,14 +316,13 @@ mod tests {
311316
.insert(
312317
&COMPONENT_DEFINITIONS_TABLE,
313318
ComponentDefinitionMetadata {
314-
name: "root".parse().unwrap(),
315-
path: "convex/app".parse().unwrap(),
319+
path: "".parse().unwrap(),
320+
definition_type: ComponentDefinitionType::App,
316321
child_components: vec![ComponentInstantiation {
317322
name: "child_subcomponent".parse().unwrap(),
318323
path: child_definition_path,
319324
args: BTreeMap::new(),
320325
}],
321-
args: Default::default(),
322326
exports: BTreeMap::new(),
323327
}
324328
.try_into()?,
@@ -329,8 +333,7 @@ mod tests {
329333
&COMPONENTS_TABLE,
330334
ComponentMetadata {
331335
definition_id: root_definition_id.internal_id(),
332-
parent_and_name: None,
333-
args: Default::default(),
336+
component_type: ComponentType::App,
334337
}
335338
.try_into()?,
336339
)
@@ -340,8 +343,11 @@ mod tests {
340343
&COMPONENTS_TABLE,
341344
ComponentMetadata {
342345
definition_id: child_definition_id.internal_id(),
343-
parent_and_name: Some((root_id.internal_id(), "subcomponent_child".parse()?)),
344-
args: Default::default(),
346+
component_type: ComponentType::ChildComponent {
347+
parent: root_id.internal_id(),
348+
name: "subcomponent_child".parse()?,
349+
args: Default::default(),
350+
},
345351
}
346352
.try_into()?,
347353
)

0 commit comments

Comments
 (0)