Skip to main content
Glama
create.rs12.8 kB
use base64::{ Engine, engine::general_purpose, }; use si_id::SchemaId; use telemetry::prelude::*; use super::{ FuncAuthoringError, FuncAuthoringResult, }; use crate::{ Component, DalContext, Func, FuncBackendKind, FuncBackendResponseType, Schema, SchemaVariant, SchemaVariantId, action::prototype::{ ActionKind, ActionPrototype, }, func::{ binding::{ AttributeArgumentBinding, AttributeFuncDestination, EventualParent, action::ActionBinding, attribute::AttributeBinding, authentication::AuthBinding, leaf::LeafBinding, management::ManagementBinding, }, leaf::{ LeafInputLocation, LeafKind, }, }, generate_name, management::prototype::ManagementPrototypeParent, }; static DEFAULT_CODE_HANDLER: &str = "main"; static DEFAULT_ATTRIBUTE_CODE: &str = include_str!("data/defaults/attribute.ts"); static DEFAULT_CODE_GENERATION_CODE: &str = include_str!("data/defaults/code_generation.ts"); static DEFAULT_QUALIFICATION_CODE: &str = include_str!("data/defaults/qualification.ts"); static DEFAULT_ACTION_CODE: &str = include_str!("data/defaults/action.ts"); static DEFAULT_AUTHENTICATION_CODE: &str = include_str!("data/defaults/authentication.ts"); static DEFAULT_MGMT_CODE: &str = include_str!("data/defaults/management.ts"); #[allow(dead_code)] static DEFAULT_VALIDATION_CODE: &str = include_str!("data/defaults/validation.ts"); #[instrument( name = "func.authoring.create_func.create.management", level = "debug", skip(ctx) )] pub(crate) async fn create_management_func( ctx: &DalContext, name: Option<String>, parent: ManagementPrototypeParent, ) -> FuncAuthoringResult<Func> { let name = name.unwrap_or(generate_name()); match &parent { // An overlay can have any name ManagementPrototypeParent::Schemas(schema_ids) => { for schema_id in schema_ids { fail_if_func_name_already_used_on_schema(ctx, &name, *schema_id).await?; } } ManagementPrototypeParent::SchemaVariant(variant_id) => { fail_if_func_name_already_used_on_variant(ctx, &name, *variant_id).await? } } let func = create_func_stub( ctx, name.clone(), FuncBackendKind::Management, FuncBackendResponseType::Management, DEFAULT_MGMT_CODE, DEFAULT_CODE_HANDLER, false, ) .await?; match parent { ManagementPrototypeParent::Schemas(schema_ids) => { ManagementBinding::create_management_binding(ctx, func.id, Some(schema_ids), None) .await?; } ManagementPrototypeParent::SchemaVariant(schema_variant_id) => { ManagementBinding::create_management_binding( ctx, func.id, None, Some(schema_variant_id), ) .await?; } } Ok(func) } #[instrument( name = "func.authoring.create_func.create.action", level = "debug", skip(ctx) )] pub(crate) async fn create_action_func_overlay( ctx: &DalContext, name: Option<String>, action_kind: ActionKind, schema_id: SchemaId, ) -> FuncAuthoringResult<Func> { let name = name.unwrap_or(generate_name()); fail_if_func_name_already_used_on_schema(ctx, &name, schema_id).await?; // need to see if there's already an action func of this particular kind for the schema variant // before doing anything else let func = create_func_stub( ctx, name, FuncBackendKind::JsAction, FuncBackendResponseType::Action, DEFAULT_ACTION_CODE, DEFAULT_CODE_HANDLER, false, ) .await?; if let Err(err) = ActionBinding::create_action_binding_overlay(ctx, func.id, action_kind, schema_id).await { if let crate::func::binding::FuncBindingError::ActionKindAlreadyExistsForSchema(_, _) = &err { Func::delete_by_id(ctx, func.id).await?; } return Err(err)?; } Ok(func) } #[instrument( name = "func.authoring.create_func.create.action", level = "debug", skip(ctx) )] pub(crate) async fn create_action_func( ctx: &DalContext, name: Option<String>, action_kind: ActionKind, schema_variant_id: SchemaVariantId, ) -> FuncAuthoringResult<Func> { let name = name.unwrap_or(generate_name()); fail_if_func_name_already_used_on_variant(ctx, &name, schema_variant_id).await?; // need to see if there's already an action func of this particular kind for the schema variant // before doing anything else let existing_actions_for_variant = ActionPrototype::for_variant(ctx, schema_variant_id).await?; for action in existing_actions_for_variant { if action.kind == action_kind && action_kind != ActionKind::Manual { return Err(FuncAuthoringError::ActionKindAlreadyExists( action_kind, schema_variant_id, )); } } let func = create_func_stub( ctx, name, FuncBackendKind::JsAction, FuncBackendResponseType::Action, DEFAULT_ACTION_CODE, DEFAULT_CODE_HANDLER, false, ) .await?; ActionBinding::create_action_binding(ctx, func.id, action_kind, schema_variant_id).await?; Ok(func) } #[instrument( name = "func.authoring.create_func.create.leaf_overlay", level = "debug", skip(ctx) )] pub(crate) async fn create_leaf_overlay_func( ctx: &DalContext, name: Option<String>, leaf_kind: LeafKind, schema_id: SchemaId, inputs: &[LeafInputLocation], ) -> FuncAuthoringResult<Func> { let eventual_parent = EventualParent::Schemas(vec![schema_id]); let name = name.unwrap_or(generate_name()); fail_if_name_already_used_on_eventual_parent(ctx, &name, &eventual_parent).await?; let (code, handler, backend_kind, backend_response_type) = match leaf_kind { LeafKind::CodeGeneration => ( DEFAULT_CODE_GENERATION_CODE, DEFAULT_CODE_HANDLER, FuncBackendKind::JsAttribute, FuncBackendResponseType::CodeGeneration, ), LeafKind::Qualification => ( DEFAULT_QUALIFICATION_CODE, DEFAULT_CODE_HANDLER, FuncBackendKind::JsAttribute, FuncBackendResponseType::Qualification, ), }; let func = create_func_stub( ctx, name, backend_kind, backend_response_type, code, handler, false, ) .await?; LeafBinding::create_leaf_func_binding(ctx, func.id, eventual_parent, leaf_kind, inputs).await?; Ok(func) } #[instrument( name = "func.authoring.create_func.create.leaf", level = "debug", skip(ctx) )] pub(crate) async fn create_leaf_func( ctx: &DalContext, name: Option<String>, leaf_kind: LeafKind, eventual_parent: EventualParent, inputs: &[LeafInputLocation], ) -> FuncAuthoringResult<Func> { let name = name.unwrap_or(generate_name()); fail_if_name_already_used_on_eventual_parent(ctx, &name, &eventual_parent).await?; let (code, handler, backend_kind, backend_response_type) = match leaf_kind { LeafKind::CodeGeneration => ( DEFAULT_CODE_GENERATION_CODE, DEFAULT_CODE_HANDLER, FuncBackendKind::JsAttribute, FuncBackendResponseType::CodeGeneration, ), LeafKind::Qualification => ( DEFAULT_QUALIFICATION_CODE, DEFAULT_CODE_HANDLER, FuncBackendKind::JsAttribute, FuncBackendResponseType::Qualification, ), }; let func = create_func_stub( ctx, name, backend_kind, backend_response_type, code, handler, false, ) .await?; LeafBinding::create_leaf_func_binding(ctx, func.id, eventual_parent, leaf_kind, inputs).await?; Ok(func) } #[instrument( name = "func.authoring.create_func.create.attribute", level = "debug", skip(ctx) )] pub(crate) async fn create_attribute_func( ctx: &DalContext, name: Option<String>, eventual_parent: Option<EventualParent>, output_location: Option<AttributeFuncDestination>, argument_bindings: Vec<AttributeArgumentBinding>, is_transformation: bool, ) -> FuncAuthoringResult<Func> { let name = name.unwrap_or(generate_name()); if let Some(eventual_parent) = &eventual_parent { fail_if_name_already_used_on_eventual_parent(ctx, &name, eventual_parent).await?; } let (code, handler, backend_kind, backend_response_type) = ( DEFAULT_ATTRIBUTE_CODE, DEFAULT_CODE_HANDLER, FuncBackendKind::JsAttribute, FuncBackendResponseType::Unset, ); let func = create_func_stub( ctx, name, backend_kind, backend_response_type, code, handler, is_transformation, ) .await?; if let Some(output_location) = output_location { AttributeBinding::upsert_attribute_binding( ctx, func.id, eventual_parent, output_location, argument_bindings, ) .await?; } Ok(func) } #[instrument( name = "func.authoring.create_func.create.authentication", level = "debug", skip(ctx) )] pub(crate) async fn create_authentication_func( ctx: &DalContext, name: Option<String>, schema_variant_id: SchemaVariantId, ) -> FuncAuthoringResult<Func> { let name = name.unwrap_or(generate_name()); fail_if_func_name_already_used_on_variant(ctx, &name, schema_variant_id).await?; let func = create_func_stub( ctx, name, FuncBackendKind::JsAuthentication, FuncBackendResponseType::Void, DEFAULT_AUTHENTICATION_CODE, DEFAULT_CODE_HANDLER, false, ) .await?; AuthBinding::create_auth_binding(ctx, func.id, schema_variant_id).await?; Ok(func) } async fn create_func_stub( ctx: &DalContext, name: String, backend_kind: FuncBackendKind, backend_response_type: FuncBackendResponseType, code: &str, handler: &str, is_transformation: bool, ) -> FuncAuthoringResult<Func> { let code_base64 = general_purpose::STANDARD_NO_PAD.encode(code); let func = Func::new( ctx, name, None::<String>, None::<String>, None::<String>, false, false, backend_kind, backend_response_type, Some(handler), Some(code_base64), is_transformation, ) .await?; Ok(func) } async fn fail_if_func_name_already_used_on_variant( ctx: &DalContext, name: impl AsRef<str>, schema_variant_id: SchemaVariantId, ) -> FuncAuthoringResult<()> { if SchemaVariant::all_funcs_without_intrinsics(ctx, schema_variant_id) .await? .iter() .any(|f| f.name == name.as_ref()) { return Err(FuncAuthoringError::FuncNameExistsOnVariant( name.as_ref().to_string(), schema_variant_id, )); } Ok(()) } async fn fail_if_func_name_already_used_on_schema( ctx: &DalContext, name: impl AsRef<str>, schema_id: SchemaId, ) -> FuncAuthoringResult<()> { let func_ids = Schema::all_overlay_func_ids(ctx, schema_id).await?; for func_id in func_ids { let func = Func::get_by_id(ctx, func_id).await?; if func.name == name.as_ref() { return Err(FuncAuthoringError::FuncNameExistsOnSchema( name.as_ref().to_string(), schema_id, )); } } Ok(()) } async fn fail_if_name_already_used_on_eventual_parent( ctx: &DalContext, name: impl AsRef<str>, eventual_parent: &EventualParent, ) -> FuncAuthoringResult<()> { // Check if name is already used on the same variant, if applicable match eventual_parent { EventualParent::SchemaVariant(variant_id) => { fail_if_func_name_already_used_on_variant(ctx, &name, *variant_id).await? } EventualParent::Component(component_id) => { let variant_id = Component::schema_variant_id(ctx, *component_id).await?; fail_if_func_name_already_used_on_variant(ctx, &name, variant_id).await?; } EventualParent::Schemas(schema_ids) => { for schema_id in schema_ids { fail_if_func_name_already_used_on_schema(ctx, &name, *schema_id).await?; } } } 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