Skip to main content
Glama
component.rs23.3 kB
use dal::{ AttributeValue, AttributeValueId, Component, DalContext, Schema, SchemaVariant, Ulid, attribute::value::{ DependentValueGraph, dependent_value_graph::DependentValue, }, diagram::{ Diagram, view::View, }, prop::{ Prop, PropPath, }, property_editor::values::PropertyEditorValues, workspace_snapshot::DependentValueRoot, }; use dal_test::{ Result, helpers::{ ChangeSetTestHelpers, component, create_component_for_default_schema_name_in_default_view, create_component_for_schema_variant_on_default_view, schema::variant, }, test, }; use pretty_assertions_sorted::assert_eq; mod autosubscribe; mod connectable_test; mod debug; mod delete; mod duplicate; mod get_code; mod get_diff; mod paste; mod property_order; mod set_type; mod upgrade; #[test] async fn update_and_insert_and_update(ctx: &mut DalContext) -> Result<()> { let component = create_component_for_default_schema_name_in_default_view( ctx, "Docker Image", "a tulip in a cup", ) .await?; let variant_id = Component::schema_variant_id(ctx, component.id()).await?; let property_values = PropertyEditorValues::assemble(ctx, component.id()).await?; let image_prop_id = Prop::find_prop_id_by_path(ctx, variant_id, &PropPath::new(["root", "domain", "image"])) .await?; let exposed_ports_prop_id = Prop::find_prop_id_by_path( ctx, variant_id, &PropPath::new(["root", "domain", "ExposedPorts"]), ) .await?; let exposed_ports_elem_prop_id = Prop::find_prop_id_by_path( ctx, variant_id, &PropPath::new(["root", "domain", "ExposedPorts", "ExposedPort"]), ) .await?; // Update image let image_av_id = property_values .find_by_prop_id(image_prop_id) .expect("can't find default attribute value for ExposedPorts"); let image_value = serde_json::json!("fiona/apple"); AttributeValue::update(ctx, image_av_id, Some(image_value.clone())).await?; let exposed_port_attribute_value_id = property_values .find_by_prop_id(exposed_ports_prop_id) .expect("can't find default attribute value for ExposedPorts"); // Insert it unset first (to mimick frontend) let inserted_av_id = AttributeValue::insert(ctx, exposed_port_attribute_value_id, None, None).await?; // Before sending to the rebaser, confirm the value is there and it's the only one for the // ExposedPorts prop let property_values = PropertyEditorValues::assemble(ctx, component.id()).await?; let (fetched_image_value, image_av_id_again) = property_values .find_with_value_by_prop_id(image_prop_id) .expect("able to get image av id from pvalues"); assert_eq!(image_av_id, image_av_id_again); assert_eq!(image_value, fetched_image_value); let mut inserted_attribute_values: Vec<AttributeValueId> = property_values.list_by_prop_id(exposed_ports_elem_prop_id); assert_eq!(1, inserted_attribute_values.len()); let pvalues_inserted_attribute_value_id = inserted_attribute_values.pop().expect("get our av id"); assert_eq!(inserted_av_id, pvalues_inserted_attribute_value_id); // Rebase! ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; component.view(ctx).await?; // Confirm after rebase let property_values = PropertyEditorValues::assemble(ctx, component.id()).await?; let (fetched_image_value, image_av_id_again) = property_values .find_with_value_by_prop_id(image_prop_id) .expect("able to get image av id from pvalues"); assert_eq!(image_av_id, image_av_id_again); assert_eq!(image_value, fetched_image_value); let mut inserted_attribute_values = property_values.list_with_values_by_prop_id(exposed_ports_elem_prop_id); assert_eq!(1, inserted_attribute_values.len()); let (inserted_value, pvalues_inserted_attribute_value_id) = inserted_attribute_values.pop().expect("get our av id"); assert_eq!(inserted_av_id, pvalues_inserted_attribute_value_id); assert_eq!(inserted_value, serde_json::Value::Null); let value = serde_json::json!("i ran out of white doves feathers"); // Update the value we inserted AttributeValue::update(ctx, inserted_av_id, Some(value.clone())).await?; // Confirm again before rebase let property_values = PropertyEditorValues::assemble(ctx, component.id()).await?; let mut inserted_attribute_values = property_values.list_with_values_by_prop_id(exposed_ports_elem_prop_id); assert_eq!(1, inserted_attribute_values.len()); let (inserted_value, pvalues_inserted_attribute_value_id) = inserted_attribute_values.pop().expect("get our av id"); assert_eq!(inserted_av_id, pvalues_inserted_attribute_value_id); assert_eq!(inserted_value, value.clone()); // Rebase again! ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; let property_values = PropertyEditorValues::assemble(ctx, component.id()).await?; let mut inserted_attribute_values = property_values.list_with_values_by_prop_id(exposed_ports_elem_prop_id); assert_eq!(1, inserted_attribute_values.len()); let (inserted_value, pvalues_inserted_attribute_value_id) = inserted_attribute_values.pop().expect("get our av id"); assert_eq!(inserted_av_id, pvalues_inserted_attribute_value_id); assert_eq!(inserted_value, value.clone()); Ok(()) } #[test] async fn create_and_determine_lineage(ctx: &DalContext) -> Result<()> { // List all schemas in the workspace. Pick the first one alphabetically. let mut schemas: Vec<Schema> = Schema::list(ctx).await?; schemas.sort_by(|a, b| a.name.cmp(&b.name)); let schema = schemas.pop().expect("schemas are empty"); // Ensure we can get it by id. let found_schema = Schema::get_by_id(ctx, schema.id()).await?; assert_eq!( schema.id(), // expected found_schema.id() // actual ); // Pick a schema variant. let mut schema_variants = SchemaVariant::list_for_schema(ctx, found_schema.id()).await?; let schema_variant = schema_variants.pop().expect("schemas are empty"); let schema_variant_id = schema_variant.id(); // Create a component and set geometry. let mut component = create_component_for_schema_variant_on_default_view(ctx, schema_variant_id).await?; let default_view_id = View::get_id_for_default(ctx).await?; component .set_geometry( ctx, default_view_id, 1isize, 1isize, Some(500isize), Some(500isize), ) .await?; // Determine the schema variant from the component. Ensure it is the same as before. let post_creation_schema_variant = component.schema_variant(ctx).await?; assert_eq!( schema_variant_id, // expected post_creation_schema_variant.id() // actual ); // Determine the schema from the schema variant. Ensure it is the same as before. let post_creation_schema = post_creation_schema_variant.schema(ctx).await?; assert_eq!( schema.id(), // expected post_creation_schema.id() // actual ); // Assemble the diagram just to make sure it works. let _diagram = Diagram::assemble_for_default_view(ctx).await?; Ok(()) } #[test] async fn through_the_wormholes_simple(ctx: &mut DalContext) -> Result<()> { let name = "across the universe"; let component = create_component_for_default_schema_name_in_default_view(ctx, "starfield", name).await?; let variant_id = Component::schema_variant_id(ctx, component.id()).await?; ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; let rigid_designator_prop_id = Prop::find_prop_id_by_path( ctx, variant_id, &PropPath::new([ "root", "domain", "possible_world_a", "wormhole_1", "wormhole_2", "wormhole_3", "rigid_designator", ]), ) .await?; let rigid_designator_values = Component::attribute_values_for_prop_id(ctx, component.id(), rigid_designator_prop_id) .await?; assert_eq!(1, rigid_designator_values.len()); let rigid_designator_value_id = rigid_designator_values .first() .copied() .expect("get first value id"); assert_eq!( component.id(), AttributeValue::component_id(ctx, rigid_designator_value_id).await? ); let naming_and_necessity_prop_id = Prop::find_prop_id_by_path( ctx, variant_id, &PropPath::new([ "root", "domain", "possible_world_b", "wormhole_1", "wormhole_2", "wormhole_3", "naming_and_necessity", ]), ) .await?; let naming_and_necessity_value_id = Component::attribute_values_for_prop_id(ctx, component.id(), naming_and_necessity_prop_id) .await? .first() .copied() .expect("get first value id"); let update_graph = DependentValueGraph::new( ctx, vec![DependentValueRoot::Unfinished( rigid_designator_value_id.into(), )], ) .await?; assert!( update_graph.contains_value(DependentValue::AttributeValue( naming_and_necessity_value_id )), "update graph has the value we aren't setting but which depends on the value we are setting" ); assert!( update_graph .direct_dependencies_of(DependentValue::AttributeValue( naming_and_necessity_value_id )) .contains(&DependentValue::AttributeValue(rigid_designator_value_id)), "update graph declares that `naming_and_necessity` value depends on `rigid_designator` value" ); let rigid_designation = serde_json::json!("hesperus"); AttributeValue::update( ctx, rigid_designator_value_id, Some(rigid_designation.to_owned()), ) .await?; let view = AttributeValue::view(ctx, rigid_designator_value_id) .await? .expect("has a view"); assert_eq!(rigid_designation, view); ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; let naming_and_necessity_view = AttributeValue::view(ctx, naming_and_necessity_value_id) .await? .expect("naming and necessity has a value"); // hesperus is phosphorus (the attr func on naming_and_necessity_value_id will return // phosphorus if it receives hesperus) assert_eq!("phosphorus", naming_and_necessity_view); let root_prop_id = Prop::find_prop_id_by_path(ctx, variant_id, &PropPath::new(["root"])).await?; let root_value_id = Component::attribute_values_for_prop_id(ctx, component.id(), root_prop_id) .await? .first() .copied() .expect("a value exists for the root prop"); let root_view = AttributeValue::view(ctx, root_value_id) .await? .expect("there is a value for the root value view"); assert_eq!( serde_json::json!({ "si": { "name": name, "color": "#ffffff", "type": "component" }, "resource_value": {}, "domain": { "name": name, "possible_world_a": { "wormhole_1": { "wormhole_2": { "wormhole_3": { "rigid_designator": rigid_designation } } } }, "possible_world_b": { "wormhole_1": { "wormhole_2": { "wormhole_3": { "naming_and_necessity": "phosphorus" } } } }, "universe": { "galaxies": [] }, } } ), root_view ); Ok(()) } #[test] async fn through_the_wormholes_child_value_reactivity(ctx: &mut DalContext) -> Result<()> { let name = "across the universe"; let component = create_component_for_default_schema_name_in_default_view(ctx, "starfield", name).await?; let variant_id = Component::schema_variant_id(ctx, component.id()).await?; ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; let possible_world_a_prop_id = Prop::find_prop_id_by_path( ctx, variant_id, &PropPath::new(["root", "domain", "possible_world_a"]), ) .await?; let possible_world_values = Component::attribute_values_for_prop_id(ctx, component.id(), possible_world_a_prop_id) .await .expect("able to get attribute value for universe prop"); assert_eq!(1, possible_world_values.len()); let possible_world_a_value_id = possible_world_values .first() .copied() .expect("get first value id"); assert_eq!( component.id(), AttributeValue::component_id(ctx, possible_world_a_value_id).await? ); let naming_and_necessity_prop_id = Prop::find_prop_id_by_path( ctx, variant_id, &PropPath::new([ "root", "domain", "possible_world_b", "wormhole_1", "wormhole_2", "wormhole_3", "naming_and_necessity", ]), ) .await?; let naming_and_necessity_value_id = Component::attribute_values_for_prop_id(ctx, component.id(), naming_and_necessity_prop_id) .await? .first() .copied() .expect("get first value id"); let update_graph = DependentValueGraph::new( ctx, vec![DependentValueRoot::Unfinished( possible_world_a_value_id.into(), )], ) .await?; assert!( update_graph.contains_value(DependentValue::AttributeValue( naming_and_necessity_value_id )), "update graph has the value we aren't setting but which depends on the value we are setting" ); let possible_world_a = serde_json::json!({ "wormhole_1": { "wormhole_2": { "wormhole_3": { "rigid_designator": "hesperus" } } } }); AttributeValue::update( ctx, possible_world_a_value_id, Some(possible_world_a.clone()), ) .await?; let view = AttributeValue::view(ctx, possible_world_a_value_id) .await? .expect("has a view"); assert_eq!(possible_world_a, view); ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; let naming_and_necessity_view = AttributeValue::view(ctx, naming_and_necessity_value_id) .await? .expect("naming and necessity has a value"); // hesperus is phosphorus (the attr func on naming_and_necessity_value_id will return // phosphorus if it receives hesperus) assert_eq!("phosphorus", naming_and_necessity_view); let root_prop_id = Prop::find_prop_id_by_path(ctx, variant_id, &PropPath::new(["root"])).await?; let root_value_id = Component::attribute_values_for_prop_id(ctx, component.id(), root_prop_id) .await? .first() .copied() .expect("a value exists for the root prop"); let root_view = AttributeValue::view(ctx, root_value_id) .await? .expect("there is a value for the root value view"); assert_eq!( serde_json::json!({ "si": { "name": name, "color": "#ffffff", "type": "component" }, "resource_value": {}, "domain": { "name": name, "possible_world_a": possible_world_a, "possible_world_b": { "wormhole_1": { "wormhole_2": { "wormhole_3": { "naming_and_necessity": "phosphorus" } } } }, "universe": { "galaxies": [] }, } } ), root_view ); Ok(()) } #[test] async fn set_the_universe(ctx: &mut DalContext) -> Result<()> { let component = create_component_for_default_schema_name_in_default_view( ctx, "starfield", "across the universe", ) .await?; let variant_id = Component::schema_variant_id(ctx, component.id()).await?; let universe_prop_id = Prop::find_prop_id_by_path( ctx, variant_id, &PropPath::new(["root", "domain", "universe"]), ) .await?; let universe_values = Component::attribute_values_for_prop_id(ctx, component.id(), universe_prop_id).await?; assert_eq!(1, universe_values.len()); let universe_value_id = universe_values .first() .copied() .expect("get first value id"); assert_eq!( component.id(), AttributeValue::component_id(ctx, universe_value_id).await? ); let universe_json = serde_json::json!({ "galaxies": [ { "sun": "sol", "planets": 9 }, { "sun": "champagne supernova", "planets": 9000 }, { "sun": "black hole", "planets": 0 } ] }); AttributeValue::update(ctx, universe_value_id, Some(universe_json.to_owned())).await?; let view = AttributeValue::view(ctx, universe_value_id) .await? .expect("has a view"); assert_eq!(universe_json, view); ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; let view = AttributeValue::view(ctx, universe_value_id) .await? .expect("has a view"); assert_eq!(universe_json, view); Ok(()) } #[test] #[ignore = "this test does not pass on the legacy snapshot because id is not included in the node hash"] async fn change_component_id(ctx: &mut DalContext) -> Result<()> { let component = create_component_for_default_schema_name_in_default_view( ctx, "starfield", "across the universe", ) .await?; let current_component_id = component.id(); let current_lineage_id = ctx .workspace_snapshot()? .get_node_weight(current_component_id) .await .expect("node weight exists") .lineage_id(); ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; let new_component_id = Ulid::new(); ctx.workspace_snapshot()? .update_node_id(current_component_id, new_component_id, current_lineage_id) .await?; ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; assert!( Component::try_get_by_id(ctx, current_component_id) .await? .is_none() ); assert!( Component::try_get_by_id(ctx, new_component_id.into()) .await? .is_some() ); Ok(()) } #[test] async fn default_values_appear_in_views(ctx: &mut DalContext) -> Result<()> { let _subscription_tracker = variant::create( ctx, "default_values", r#" function main() { // Props const labelsProp = new PropBuilder() .setName("labels") .setKind("string") .setHidden(false) .setWidget(new PropWidgetDefinitionBuilder() .setKind("text") .build()) .setValidationFormat(Joi.string().required()) .setDocumentation("User-defined labels (`key/value` pairs) for the Resource.\nFor more information, see \"[Labels](#labels)\".\n") .build(); const nameProp = new PropBuilder() .setName("name") .setKind("string") .setHidden(false) .setDefaultValue("default-name") .setWidget(new PropWidgetDefinitionBuilder() .setKind("text") .build()) .setValidationFormat(Joi.string().required()) .setDocumentation("Name of the Resource. Must be unique per Project.") .build(); const extraProp = new PropBuilder() .setName("extra") .setKind("object") .setHidden(false) .setWidget(new PropWidgetDefinitionBuilder() .setKind("header") .build()) .addChild( new PropBuilder() .setName("endpoint") .setKind("string") .setHidden(false) .setWidget(new PropWidgetDefinitionBuilder() .setKind("text") .build()) .setDefaultValue("ssh_keys") .build() ) .addChild( new PropBuilder() .setName("PropUsageMap") .setKind("string") .setHidden(true) .setWidget(new PropWidgetDefinitionBuilder() .setKind("text") .build()) .setDefaultValue("{\"createOnly\":[\"public_key\"],\"updatable\":[\"labels\",\"name\"]}") .build() ) .build(); return new AssetBuilder() .addProp(labelsProp) .addProp(nameProp) .addProp(extraProp) .build(); } "#, ) .await?; ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; let component_id = component::create(ctx, "default_values", "A").await?; let component = Component::get_by_id(ctx, component_id).await?; let view = component.view(ctx).await?.expect("has a view"); assert_eq!( serde_json::json!({ "si": { "name": "A", "type": "component","color": "FFFFFF" }, "domain": { "name": "default-name", "extra": { "endpoint": "ssh_keys", "PropUsageMap": "{\"createOnly\":[\"public_key\"],\"updatable\":[\"labels\",\"name\"]}" }, } }), view ); ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; let component = Component::get_by_id(ctx, component_id).await?; let view = component.view(ctx).await?.expect("has a view"); // after dvu, the attribute tree is vivified which changes the view assert_eq!( serde_json::json!({ "si": { "name": "A", "type": "component","color": "FFFFFF" }, "domain": { "name": "default-name", "extra": { "endpoint": "ssh_keys", "PropUsageMap": "{\"createOnly\":[\"public_key\"],\"updatable\":[\"labels\",\"name\"]}" }, }, "resource_value": serde_json::json!({}), }), view ); Ok(()) }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server