Skip to main content
Glama
binding.rs41.7 kB
use std::collections::HashMap; use action::ActionBinding; use attribute::AttributeBinding; pub use attribute_argument::{ AttributeArgumentBinding, AttributeFuncArgumentSource, }; use authentication::AuthBinding; use itertools::Itertools; use leaf::LeafBinding; use management::ManagementBinding; use serde::{ Deserialize, Serialize, }; use si_frontend_types::LeafBindingPrototype; use si_id::ManagementPrototypeId; use si_split_graph::SplitGraphError; use strum::{ Display, EnumDiscriminants, }; use telemetry::prelude::*; use thiserror::Error; use crate::{ AttributePrototypeId, ComponentError, ComponentId, DalContext, Func, FuncError, FuncId, InputSocket, InputSocketId, OutputSocket, OutputSocketId, Prop, PropId, SchemaError, SchemaId, SchemaVariant, SchemaVariantError, SchemaVariantId, WorkspaceSnapshotError, WsEventError, action::prototype::{ ActionKind, ActionPrototype, ActionPrototypeError, ActionPrototypeParent, }, attribute::{ prototype::{ AttributePrototypeError, argument::{ AttributePrototypeArgumentError, AttributePrototypeArgumentId, value_source::ValueSource, }, }, value::AttributeValueError, }, func::{ FuncKind, argument::{ FuncArgumentError, FuncArgumentId, }, binding::attribute::AttributeBindingMalformedInput, leaf::LeafKind, }, management::prototype::{ ManagementPrototype, ManagementPrototypeError, }, prop::PropError, schema::leaf::LeafPrototypeError, socket::{ input::InputSocketError, output::OutputSocketError, }, workspace_snapshot::{ dependent_value_root::DependentValueRootError, graph::WorkspaceSnapshotGraphError, }, }; pub mod action; pub mod attribute; pub mod attribute_argument; pub mod authentication; pub mod leaf; pub mod management; #[remain::sorted] #[derive(Error, Debug)] pub enum FuncBindingError { #[error( "action with kind ({0}) already exists for schema variant ({1}), cannot have two non-manual actions for the same kind in the same schema variant" )] ActionKindAlreadyExists(ActionKind, SchemaVariantId), #[error( "action with kind ({0}) already exists for schema ({1}), cannot have two non-manual actions for the same kind in the same schema" )] ActionKindAlreadyExistsForSchema(ActionKind, SchemaId), #[error("action prototype error: {0}")] ActionPrototype(#[from] Box<ActionPrototypeError>), #[error("action prototype missing")] ActionPrototypeMissing, #[error("attribute prototype error: {0}")] AttributePrototype(#[from] Box<AttributePrototypeError>), #[error("attribute prototype argument error: {0}")] AttributePrototypeArgument(#[from] Box<AttributePrototypeArgumentError>), #[error("attribute prototype missing")] AttributePrototypeMissing, #[error("attribute value error: {0}")] AttributeValue(#[from] Box<AttributeValueError>), #[error("cached modules error: {0}")] CachedModules(#[from] crate::cached_module::CachedModuleError), #[error("cannot compile types for func: {0}")] CannotCompileTypes(FuncId), #[error("cannot set intrinsic func for component: {0}")] CannotSetIntrinsicForComponent(ComponentId), #[error("component error: {0}")] ComponentError(#[from] Box<ComponentError>), #[error("dependent value root error: {0}")] DependentValueRoot(#[from] DependentValueRootError), #[error("func error: {0}")] Func(#[from] Box<FuncError>), #[error("func argument error: {0}")] FuncArgument(#[from] Box<FuncArgumentError>), #[error("func argument missing for func arg: {0} with name: {1}")] FuncArgumentMissing(FuncArgumentId, String), #[error("input socket error: {0}")] InputSocket(#[from] Box<InputSocketError>), #[error("invalid attribute prototype argument source: {0}")] InvalidAttributePrototypeArgumentSource(AttributeFuncArgumentSource), #[error("invalid attribute prototype destination: {0}")] InvalidAttributePrototypeDestination(AttributeFuncDestination), #[error("invalid intrinsic binding")] InvalidIntrinsicBinding, #[error("leaf prototype error: {0}")] LeafPrototypeError(#[from] Box<LeafPrototypeError>), #[error("malformed input for binding: {0:?}")] MalformedInput(AttributeBindingMalformedInput), #[error("management prototype error: {0}")] ManagementPrototype(#[from] Box<ManagementPrototypeError>), #[error("management prototype has no parent: {0}")] ManagementPrototypeNoParent(ManagementPrototypeId), #[error("no input location given for attribute prototype id ({0}) and func argument id ({1})")] NoInputLocationGiven(AttributePrototypeId, FuncArgumentId), #[error("no output location given for func: {0}")] NoOutputLocationGiven(FuncId), #[error("no schemas specified as eventual parent of func")] NoSchemas, #[error("output socket error: {0}")] OutputSocket(#[from] Box<OutputSocketError>), #[error("prop error: {0}")] Prop(#[from] Box<PropError>), #[error("schema error: {0}")] Schema(#[from] Box<SchemaError>), #[error("schema variant error: {0}")] SchemaVariant(#[from] Box<SchemaVariantError>), #[error("serde error: {0}")] Serde(#[from] serde_json::Error), #[error("unexpected func binding variant: {0:?} (expected: {1:?})")] UnexpectedFuncBindingVariant(FuncBindingDiscriminants, FuncBindingDiscriminants), #[error("unexpected func kind ({0}) creating attribute func")] UnexpectedFuncKind(FuncKind), #[error("unexpected value source ({0:?}) for attribute prototype argument: {1}")] UnexpectedValueSource(ValueSource, AttributePrototypeArgumentId), #[error("workspace snapshot error: {0}")] WorkspaceSnapshot(#[from] WorkspaceSnapshotError), #[error("ws event error: {0}")] WsEvent(#[from] Box<WsEventError>), } impl FuncBindingError { pub fn is_create_graph_cycle(&self) -> bool { match self { Self::AttributePrototype(err) => matches!( err.as_ref(), AttributePrototypeError::WorkspaceSnapshot( WorkspaceSnapshotError::WorkspaceSnapshotGraph( WorkspaceSnapshotGraphError::CreateGraphCycle, ) | WorkspaceSnapshotError::SplitGraph(SplitGraphError::WouldCreateGraphCycle), ) ), Self::AttributePrototypeArgument(err) => matches!( err.as_ref(), AttributePrototypeArgumentError::WorkspaceSnapshot( WorkspaceSnapshotError::WorkspaceSnapshotGraph( WorkspaceSnapshotGraphError::CreateGraphCycle, ) | WorkspaceSnapshotError::SplitGraph(SplitGraphError::WouldCreateGraphCycle), ) ), Self::SchemaVariant(err) => match err.as_ref() { SchemaVariantError::AttributePrototypeArgument(err) => matches!( err.as_ref(), AttributePrototypeArgumentError::WorkspaceSnapshot( WorkspaceSnapshotError::WorkspaceSnapshotGraph( WorkspaceSnapshotGraphError::CreateGraphCycle, ) | WorkspaceSnapshotError::SplitGraph( SplitGraphError::WouldCreateGraphCycle ), ) ), _ => false, }, _ => false, } } } type FuncBindingResult<T> = Result<T, FuncBindingError>; /// Represents the location where the function ultimately writes to /// We currently only allow Attribute Funcs to be attached to Props /// (or the attribute value in the case of a component) and Output Sockets #[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq, Hash, Display)] pub enum AttributeFuncDestination { Prop(PropId), OutputSocket(OutputSocketId), InputSocket(InputSocketId), } impl AttributeFuncDestination { pub async fn find_schema_variant( &self, ctx: &DalContext, ) -> FuncBindingResult<SchemaVariantId> { let schema_variant_id = match self { AttributeFuncDestination::Prop(prop_id) => { SchemaVariant::find_for_prop_id(ctx, *prop_id).await? } AttributeFuncDestination::OutputSocket(output_socket_id) => { SchemaVariant::find_for_output_socket_id(ctx, *output_socket_id).await? } AttributeFuncDestination::InputSocket(input_socket_id) => { SchemaVariant::find_for_input_socket_id(ctx, *input_socket_id).await? } }; Ok(schema_variant_id) } pub async fn get_name_of_destination(&self, ctx: &DalContext) -> FuncBindingResult<String> { let name = match self { AttributeFuncDestination::Prop(prop_id) => Prop::get_by_id(ctx, *prop_id).await?.name, AttributeFuncDestination::OutputSocket(output_socket_id) => { OutputSocket::get_by_id(ctx, *output_socket_id) .await? .name() .to_string() } AttributeFuncDestination::InputSocket(input_socket_id) => { InputSocket::get_by_id(ctx, *input_socket_id) .await? .name() .to_string() } }; Ok(name) } } /// Represents at what level a given Prototype is attached to #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, Hash)] pub enum EventualParent { SchemaVariant(SchemaVariantId), Component(ComponentId), Schemas(Vec<SchemaId>), } impl EventualParent { /// Returns an error if the [`EventualParent`] is a locked [`SchemaVariant`] pub(crate) async fn error_if_locked(&self, ctx: &DalContext) -> FuncBindingResult<()> { match self { EventualParent::SchemaVariant(schema_variant_id) => { Ok(SchemaVariant::error_if_locked(ctx, *schema_variant_id).await?) } EventualParent::Schemas(_) | EventualParent::Component(_) => Ok(()), } } } impl From<EventualParent> for Option<si_events::ComponentId> { fn from(value: EventualParent) -> Self { match value { EventualParent::SchemaVariant(_) | EventualParent::Schemas(_) => None, EventualParent::Component(component_id) => Some(component_id), } } } impl From<&EventualParent> for Option<si_events::ComponentId> { fn from(value: &EventualParent) -> Self { match value { EventualParent::SchemaVariant(_) | EventualParent::Schemas(_) => None, EventualParent::Component(component_id) => Some(*component_id), } } } impl From<EventualParent> for Option<si_events::SchemaVariantId> { fn from(value: EventualParent) -> Self { match value { EventualParent::SchemaVariant(schema_variant_id) => Some(schema_variant_id), EventualParent::Component(_) | EventualParent::Schemas(_) => None, } } } impl From<&EventualParent> for Option<si_events::SchemaVariantId> { fn from(value: &EventualParent) -> Self { match value { EventualParent::SchemaVariant(schema_variant_id) => Some(*schema_variant_id), EventualParent::Component(_) | EventualParent::Schemas(_) => None, } } } impl From<AttributeFuncDestination> for Option<si_events::PropId> { fn from(value: AttributeFuncDestination) -> Self { match value { AttributeFuncDestination::Prop(prop_id) => Some(prop_id), _ => None, } } } impl From<AttributeFuncDestination> for Option<si_events::OutputSocketId> { fn from(value: AttributeFuncDestination) -> Self { match value { AttributeFuncDestination::OutputSocket(output_socket_id) => Some(output_socket_id), _ => None, } } } impl From<FuncBinding> for si_frontend_types::FuncBinding { fn from(value: FuncBinding) -> Self { match value { FuncBinding::Action(action) => si_frontend_types::FuncBinding::Action { schema_variant_id: Some(action.schema_variant_id), action_prototype_id: Some(action.action_prototype_id), func_id: Some(action.func_id), kind: Some(action.kind.into()), }, FuncBinding::Attribute(attribute) => si_frontend_types::FuncBinding::Attribute { func_id: Some(attribute.func_id), attribute_prototype_id: Some(attribute.attribute_prototype_id), component_id: (&attribute.eventual_parent).into(), schema_variant_id: (&attribute.eventual_parent).into(), prop_id: attribute.output_location.into(), output_socket_id: attribute.output_location.into(), argument_bindings: attribute .argument_bindings .into_iter() .map(|f| f.into_frontend_type()) .collect_vec(), }, FuncBinding::Authentication(auth) => si_frontend_types::FuncBinding::Authentication { schema_variant_id: auth.schema_variant_id, func_id: Some(auth.func_id), }, FuncBinding::Management(mgmt) => si_frontend_types::FuncBinding::Management { schema_ids: mgmt.schema_ids, schema_variant_id: mgmt.schema_variant_id, management_prototype_id: Some(mgmt.management_prototype_id), func_id: Some(mgmt.func_id), }, FuncBinding::CodeGeneration(code_gen) => { si_frontend_types::FuncBinding::CodeGeneration { schema_variant_id: (&code_gen.eventual_parent).into(), component_id: (&code_gen.eventual_parent).into(), func_id: Some(code_gen.func_id), attribute_prototype_id: code_gen.leaf_binding_prototype.into(), leaf_prototype_id: code_gen.leaf_binding_prototype.into(), inputs: code_gen .inputs .into_iter() .map(|input| input.into()) .collect_vec(), } } FuncBinding::Qualification(qualification) => { si_frontend_types::FuncBinding::Qualification { schema_variant_id: (&qualification.eventual_parent).into(), component_id: (&qualification.eventual_parent).into(), func_id: Some(qualification.func_id), attribute_prototype_id: qualification.leaf_binding_prototype.into(), leaf_prototype_id: qualification.leaf_binding_prototype.into(), inputs: qualification .inputs .into_iter() .map(|input| input.into()) .collect_vec(), } } } } } /// A [`FuncBinding`] represents the intersection of a function and the [`SchemaVariant`] (or [`Component`]) /// specific information required to know when to run a function, what it's inputs are, and where the result of /// the function is recorded #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, EnumDiscriminants, Hash)] pub enum FuncBinding { /// An Action function can only (currently) be attached to a [`Schema Variant`] /// The [`ActionPrototypeId`] represents the unique relationship to a particular [`SchemaVariant`] /// Interestingly, the [`ActionKind`] is a property on the [`ActionPrototype`], meaning changing the /// [`ActionKind`] does not require any changes to the [`Func`] itself. Action(ActionBinding), /// An Attribute function is a function that sets values within a [`SchemaVariant`] or [`Component`]'s Prop Tree /// Each Attribute Function has user defined arguments, configured to map to specific Props or InputSockets. /// This intersection of [`FuncArgument`] and the [`Prop`] or [`InputSocket`] mapping is an [`AttributeArgumentBinding`] Attribute(AttributeBinding), /// Auth Funcs only exist on Secret defining schema variants, and have no special configuration data aside from the /// [`SchemaVariantId`] and as such are only created or deleted (detached), they are not updated. Authentication(AuthBinding), /// CodeGen funcs are ultimately just an Attribute Function, but the user can not control where they output to. /// They write to an Attribute Value beneath the Code Gen Root Prop Node CodeGeneration(LeafBinding), Management(ManagementBinding), /// Qualification funcs are ultimately just an Attribute Function, but the user can not control where they output to. /// They write to an Attribute Value beneath the Qualification Root Prop Node Qualification(LeafBinding), } impl FuncBinding { /// Takes the [`self`] [`FuncBinding`] and a provided [`FuncId`], deletes the existing binding and recreates it for the /// given [`FuncId`]. #[instrument( level = "info", skip(self, ctx), name = "func.bindings.port_binding_to_new_func" )] pub async fn port_binding_to_new_func( &self, ctx: &DalContext, new_func_id: FuncId, ) -> FuncBindingResult<Vec<FuncBinding>> { let new_binding = match self { FuncBinding::Action(action) => { action.port_binding_to_new_func(ctx, new_func_id).await? } FuncBinding::Attribute(attribute) => { attribute.port_binding_to_new_func(ctx, new_func_id).await? } FuncBinding::Authentication(auth) => { auth.port_binding_to_new_func(ctx, new_func_id).await? } FuncBinding::CodeGeneration(code_gen) => { LeafBinding::port_binding_to_new_func( ctx, new_func_id, code_gen.leaf_binding_prototype, LeafKind::CodeGeneration, code_gen.eventual_parent.clone(), &code_gen.inputs, ) .await? } FuncBinding::Qualification(qualification) => { LeafBinding::port_binding_to_new_func( ctx, new_func_id, qualification.leaf_binding_prototype, LeafKind::Qualification, qualification.eventual_parent.clone(), &qualification.inputs, ) .await? } FuncBinding::Management(mgmt) => { mgmt.port_binding_to_new_func(ctx, new_func_id).await? } }; Ok(new_binding) } pub fn get_schema_variant(&self) -> Option<SchemaVariantId> { match self { FuncBinding::Action(action) => Some(action.schema_variant_id), FuncBinding::Attribute(attribute) => { if let EventualParent::SchemaVariant(schema_variant_id) = attribute.eventual_parent { Some(schema_variant_id) } else { None } } FuncBinding::Authentication(auth) => Some(auth.schema_variant_id), FuncBinding::CodeGeneration(code_gen) => { if let EventualParent::SchemaVariant(schema_variant_id) = code_gen.eventual_parent { Some(schema_variant_id) } else { None } } FuncBinding::Qualification(qualification) => { if let EventualParent::SchemaVariant(schema_variant_id) = qualification.eventual_parent { Some(schema_variant_id) } else { None } } FuncBinding::Management(mgmt) => mgmt.schema_variant_id, } } /// Returns true if this binding is an overlay (schema-level) binding pub fn is_overlay(&self) -> bool { match self { FuncBinding::Action(_) | FuncBinding::Authentication(_) => { // For Action and Auth, we need to check the parent type // Actions can be overlays, but this method on the binding alone can't tell // Use has_overlay_bindings() instead false } FuncBinding::Attribute(attribute) => { matches!(attribute.eventual_parent, EventualParent::Schemas(_)) } FuncBinding::CodeGeneration(code_gen) => { matches!(code_gen.eventual_parent, EventualParent::Schemas(_)) } FuncBinding::Qualification(qualification) => { matches!(qualification.eventual_parent, EventualParent::Schemas(_)) } FuncBinding::Management(mgmt) => mgmt.schema_variant_id.is_none(), } } pub async fn for_func_id( ctx: &DalContext, func_id: FuncId, ) -> FuncBindingResult<Vec<FuncBinding>> { let func = Func::get_by_id(ctx, func_id).await?; let bindings = match func.kind { FuncKind::Action => ActionBinding::assemble_action_bindings(ctx, func_id).await?, FuncKind::Attribute => { AttributeBinding::assemble_attribute_bindings(ctx, func_id).await? } FuncKind::Authentication => AuthBinding::assemble_auth_bindings(ctx, func_id).await?, FuncKind::CodeGeneration => { LeafBinding::assemble_leaf_func_bindings(ctx, func_id, LeafKind::CodeGeneration) .await? } FuncKind::Qualification => { LeafBinding::assemble_leaf_func_bindings(ctx, func_id, LeafKind::Qualification) .await? } FuncKind::Intrinsic => { AttributeBinding::assemble_intrinsic_bindings(ctx, func_id).await? } FuncKind::SchemaVariantDefinition | FuncKind::Unknown => vec![], FuncKind::Management => { ManagementBinding::assemble_management_bindings(ctx, func_id).await? } FuncKind::Debug => vec![], }; Ok(bindings) } /// Check if a function has any variant-level bindings /// Variant-level bindings take precedence - if they exist, normal lock rules apply /// Only if there are NO variant-level bindings should editing while locked be allowed pub async fn has_variant_bindings( ctx: &DalContext, func_id: FuncId, ) -> FuncBindingResult<bool> { let func = Func::get_by_id(ctx, func_id).await?; match func.kind { FuncKind::Action => { // Check if any ActionPrototypes have SchemaVariant parent (variant-level) let prototype_ids = ActionPrototype::list_for_func_id(ctx, func_id) .await .map_err(Box::new)?; for proto_id in prototype_ids { let parent = ActionPrototype::parentage(ctx, proto_id) .await .map_err(Box::new)?; if matches!(parent, ActionPrototypeParent::SchemaVariant(_)) { return Ok(true); } } Ok(false) } FuncKind::Management => { // Check if any ManagementPrototypes have schema_variant_id set (variant-level) let prototype_ids = ManagementPrototype::list_ids_for_func_id(ctx, func_id) .await .map_err(Box::new)?; for proto_id in prototype_ids { // Check if it has schema_variant_id (variant-level) instead of schema_ids (overlay) if ManagementPrototype::schema_variant_id(ctx, proto_id) .await .map_err(Box::new)? .is_some() { return Ok(true); } } Ok(false) } FuncKind::CodeGeneration | FuncKind::Qualification | FuncKind::Attribute => { // For these, check if any bindings have EventualParent::SchemaVariant or Component let bindings = Self::for_func_id(ctx, func_id).await?; for binding in bindings { match binding { FuncBinding::Attribute(attr) => { if matches!( attr.eventual_parent, EventualParent::SchemaVariant(_) | EventualParent::Component(_) ) { return Ok(true); } } FuncBinding::CodeGeneration(code_gen) => { if matches!( code_gen.eventual_parent, EventualParent::SchemaVariant(_) | EventualParent::Component(_) ) { return Ok(true); } } FuncBinding::Qualification(qual) => { if matches!( qual.eventual_parent, EventualParent::SchemaVariant(_) | EventualParent::Component(_) ) { return Ok(true); } } _ => {} } } Ok(false) } // For all other function types (Authentication, Intrinsic, SchemaVariantDefinition, etc.), // return true to enforce normal lock rules. These don't participate in the overlay system. _ => Ok(true), } } /// Returns a pruned set of bindings, where each binding is for the default variant, or if an unlocked variant exists for the schema, /// only return that one pub async fn get_bindings_for_default_and_unlocked_schema_variants( ctx: &DalContext, func_id: FuncId, ) -> FuncBindingResult<Vec<(SchemaVariantId, FuncBinding)>> { let mut schema_variant_map: HashMap<SchemaId, (SchemaVariantId, FuncBinding)> = HashMap::new(); let bindings = Self::for_func_id(ctx, func_id).await?; for binding in bindings { if let Some(schema_variant_id) = binding.get_schema_variant() { // check the schema for this variant let schema_id = SchemaVariant::schema_id(ctx, schema_variant_id).await?; let maybe_variant = schema_variant_map.get(&schema_id); match maybe_variant { Some(_) => { // if there's a thing in here, it might be the default one. replace if this one's unlocked if !SchemaVariant::is_locked_by_id(ctx, schema_variant_id).await? { schema_variant_map.insert(schema_id, (schema_variant_id, binding)); } } None => { if !SchemaVariant::is_locked_by_id(ctx, schema_variant_id).await? || SchemaVariant::get_by_id(ctx, schema_variant_id) .await? .is_default(ctx) .await? { schema_variant_map.insert(schema_id, (schema_variant_id, binding)); } } } } } Ok(schema_variant_map.into_values().collect_vec()) } /// Prunes the list of bindings for a [`FuncId`] to only include the bindings for the "latest" [`SchemaVariant`]s pub async fn get_bindings_for_default_schema_variants( ctx: &DalContext, func_id: FuncId, ) -> FuncBindingResult<Vec<FuncBinding>> { let mut pruned_bindings = vec![]; let mut schema_default_map: HashMap<SchemaId, SchemaVariantId> = HashMap::new(); let bindings = Self::for_func_id(ctx, func_id).await?; for binding in bindings { if let Some(schema_variant_id) = binding.get_schema_variant() { // check the schema for this variant let schema = SchemaVariant::schema_id(ctx, schema_variant_id).await?; // check the map let maybe_default_for_variant = schema_default_map.get_key_value(&schema); match maybe_default_for_variant { Some((_schema_id, default_schema_variant_id)) => { if schema_variant_id == *default_schema_variant_id { pruned_bindings.push(binding); } } None => { let default_for_schema = SchemaVariant::default_id_for_schema(ctx, schema).await?; schema_default_map.insert(schema, default_for_schema); if default_for_schema == schema_variant_id { pruned_bindings.push(binding); } } } } } Ok(pruned_bindings) } /// Prunes the list of bindings for a [`FuncId`] to only include the latest, unlocked [`SchemaVariant`]s pub async fn get_bindings_for_unlocked_schema_variants( ctx: &DalContext, func_id: FuncId, ) -> FuncBindingResult<Vec<FuncBinding>> { let mut pruned_bindings = vec![]; let bindings = Self::for_func_id(ctx, func_id).await?; for binding in bindings { if let Some(schema_variant_id) = binding.get_schema_variant() { if !SchemaVariant::is_locked_by_id(ctx, schema_variant_id).await? { pruned_bindings.push(binding); } } } Ok(pruned_bindings) } /// Returns all bindings for a [`FuncId`] for a given [`SchemaVariantId`] pub async fn get_bindings_for_schema_variant_id( ctx: &DalContext, func_id: FuncId, schema_variant_id: SchemaVariantId, ) -> FuncBindingResult<Vec<FuncBinding>> { let mut schema_variant_bindings = vec![]; let bindings = Self::for_func_id(ctx, func_id).await?; for binding in bindings { if let Some(current_schema_variant_id) = binding.get_schema_variant() { if current_schema_variant_id == schema_variant_id { schema_variant_bindings.push(binding); } } } Ok(schema_variant_bindings) } /// Removes all existing bindings for a given [`FuncId`] /// Returns an error if any of the bindings are for a locked [`SchemaVariant`] #[instrument( level = "info", skip(ctx), name = "func.binding.delete_all_bindings_for_func_id" )] pub async fn delete_all_bindings_for_func_id( ctx: &DalContext, func_id: FuncId, ) -> FuncBindingResult<()> { let func_bindings = FuncBinding::for_func_id(ctx, func_id).await?; for binding in func_bindings { if let Some(sv_id) = binding.get_schema_variant() { let sv = SchemaVariant::get_by_id(ctx, sv_id).await?; // If the variant is locked, not default and was created on this changeset, it needs to be garbage collected if sv.is_locked() && !sv.is_default(ctx).await? && sv.was_created_on_this_changeset(ctx).await? { SchemaVariant::cleanup_variant(ctx, sv).await?; // Don't delete the binding directly, this will be dealt with by the cleanup at the end continue; } } match binding { FuncBinding::Action(action) => { ActionBinding::delete_action_binding(ctx, action.action_prototype_id).await?; } FuncBinding::Attribute(attribute) => { AttributeBinding::reset_attribute_binding( ctx, attribute.attribute_prototype_id, ) .await?; } FuncBinding::Authentication(auth) => { AuthBinding::delete_auth_binding(ctx, auth.func_id, auth.schema_variant_id) .await?; } FuncBinding::CodeGeneration(binding) | FuncBinding::Qualification(binding) => { match binding.leaf_binding_prototype { LeafBindingPrototype::Attribute(attribute_prototype_id) => { LeafBinding::delete_leaf_func_binding(ctx, attribute_prototype_id) .await?; } LeafBindingPrototype::Overlay(leaf_prototype_id) => { LeafBinding::delete_leaf_overlay_func_binding(ctx, leaf_prototype_id) .await?; } } } FuncBinding::Management(mgmt) => { ManagementBinding::delete_management_binding(ctx, mgmt.management_prototype_id) .await?; } }; } // Remove the bindings from the garbage collected schema variants ctx.workspace_snapshot()?.cleanup().await?; Ok(()) } /// Compile all the types for all of the bindings to return to the front end for type checking pub async fn compile_types(ctx: &DalContext, func_id: FuncId) -> FuncBindingResult<String> { let func = Func::get_by_id(ctx, func_id).await?; let types: String = match func.kind { FuncKind::Action => ActionBinding::compile_action_types(ctx, func_id).await?, FuncKind::CodeGeneration | FuncKind::Qualification => { LeafBinding::compile_leaf_func_types(ctx, func_id).await? } FuncKind::Attribute => AttributeBinding::compile_attribute_types(ctx, func_id).await?, FuncKind::Management => { ManagementBinding::compile_management_types(ctx, func_id).await? } FuncKind::Authentication | FuncKind::Intrinsic | FuncKind::SchemaVariantDefinition | FuncKind::Debug | FuncKind::Unknown => String::new(), }; Ok(types) } /// For a given [`FuncId`], if it's an Attribute Func, returns the [`AttributeBinding`]s pub async fn get_attribute_bindings_for_func_id( ctx: &DalContext, func_id: FuncId, ) -> FuncBindingResult<Vec<AttributeBinding>> { let mut attributes = vec![]; let bindings = Self::for_func_id(ctx, func_id).await?; for binding in bindings { match binding { FuncBinding::Attribute(attribute) => { attributes.push(attribute); } other_binding => { return Err(FuncBindingError::UnexpectedFuncBindingVariant( other_binding.into(), FuncBindingDiscriminants::Authentication, )); } } } Ok(attributes) } /// For a given [`FuncId`], if it's a CodeGen Func, returns the [`LeafBinding`]s pub async fn get_code_gen_bindings_for_func_id( ctx: &DalContext, func_id: FuncId, ) -> FuncBindingResult<Vec<LeafBinding>> { let mut leaves = vec![]; let bindings = Self::for_func_id(ctx, func_id).await?; for binding in bindings { match binding { FuncBinding::CodeGeneration(leaf_binding) => { leaves.push(leaf_binding); } other_binding => { return Err(FuncBindingError::UnexpectedFuncBindingVariant( other_binding.into(), FuncBindingDiscriminants::Authentication, )); } } } Ok(leaves) } /// For a given [`FuncId`], if it's a Qualification Func, returns the [`LeafBinding`]s pub async fn get_qualification_bindings_for_func_id( ctx: &DalContext, func_id: FuncId, ) -> FuncBindingResult<Vec<LeafBinding>> { let mut leaves = vec![]; let bindings = Self::for_func_id(ctx, func_id).await?; for binding in bindings { match binding { FuncBinding::Qualification(leaf_binding) => { leaves.push(leaf_binding); } other_binding => { return Err(FuncBindingError::UnexpectedFuncBindingVariant( other_binding.into(), FuncBindingDiscriminants::Authentication, )); } } } Ok(leaves) } /// For a given [`FuncId`], if it's a Action Func, returns the [`ActionBinding`]s pub async fn get_action_bindings_for_func_id( ctx: &DalContext, func_id: FuncId, ) -> FuncBindingResult<Vec<ActionBinding>> { let mut actions = vec![]; let bindings = Self::for_func_id(ctx, func_id).await?; for binding in bindings { match binding { FuncBinding::Action(action_binding) => { actions.push(action_binding); } other_binding => { return Err(FuncBindingError::UnexpectedFuncBindingVariant( other_binding.into(), FuncBindingDiscriminants::Authentication, )); } } } Ok(actions) } /// For a given [`FuncId`], if it's a Auth Func, returns the [`AuthBinding`]s pub async fn get_auth_bindings_for_func_id( ctx: &DalContext, func_id: FuncId, ) -> FuncBindingResult<Vec<AuthBinding>> { let mut auth_bindings = vec![]; let bindings = Self::for_func_id(ctx, func_id).await?; for binding in bindings { match binding { FuncBinding::Authentication(auth_binding) => { auth_bindings.push(auth_binding); } other_binding => { return Err(FuncBindingError::UnexpectedFuncBindingVariant( other_binding.into(), FuncBindingDiscriminants::Authentication, )); } } } Ok(auth_bindings) } } impl From<ManagementPrototypeError> for FuncBindingError { fn from(value: ManagementPrototypeError) -> Self { Box::new(value).into() } } impl From<AttributeValueError> for FuncBindingError { fn from(value: AttributeValueError) -> Self { Box::new(value).into() } } impl From<ComponentError> for FuncBindingError { fn from(value: ComponentError) -> Self { Box::new(value).into() } } impl From<SchemaVariantError> for FuncBindingError { fn from(value: SchemaVariantError) -> Self { Box::new(value).into() } } impl From<WsEventError> for FuncBindingError { fn from(value: WsEventError) -> Self { Box::new(value).into() } } impl From<ActionPrototypeError> for FuncBindingError { fn from(value: ActionPrototypeError) -> Self { Box::new(value).into() } } impl From<AttributePrototypeError> for FuncBindingError { fn from(value: AttributePrototypeError) -> Self { Box::new(value).into() } } impl From<AttributePrototypeArgumentError> for FuncBindingError { fn from(value: AttributePrototypeArgumentError) -> Self { Box::new(value).into() } } impl From<FuncError> for FuncBindingError { fn from(value: FuncError) -> Self { Box::new(value).into() } } impl From<FuncArgumentError> for FuncBindingError { fn from(value: FuncArgumentError) -> Self { Box::new(value).into() } } impl From<InputSocketError> for FuncBindingError { fn from(value: InputSocketError) -> Self { Box::new(value).into() } } impl From<OutputSocketError> for FuncBindingError { fn from(value: OutputSocketError) -> Self { Box::new(value).into() } } impl From<PropError> for FuncBindingError { fn from(value: PropError) -> Self { Box::new(value).into() } } impl From<SchemaError> for FuncBindingError { fn from(value: SchemaError) -> Self { Box::new(value).into() } }

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