Skip to main content
Glama
func.rs10.4 kB
use axum::{ Router, http::StatusCode, response::{ IntoResponse, Response, }, routing::{ delete, get, post, put, }, }; use dal::{ ChangeSetError, ComponentError, DalContext, Func, FuncError, FuncId, WsEventError, attribute::value::AttributeValueError, func::{ argument::FuncArgumentError, authoring::FuncAuthoringError, binding::FuncBindingError, runner::FuncRunnerError, }, }; use sdf_core::api_error::ApiError; use si_frontend_types::FuncCode; use si_layer_cache::LayerDbError; use telemetry::prelude::*; use thiserror::Error; use veritech_client::FunctionResultFailureErrorKind; use crate::AppState; pub mod argument; pub mod binding; pub mod create_func; pub mod create_unlocked_copy; pub mod delete_func; pub mod execute_func; pub mod get_code; pub mod get_func_run; pub mod get_func_run_logs; pub mod get_func_run_logs_av; pub mod get_func_runs_paginated; pub mod list_funcs; pub mod save_code; pub mod test_execute; pub mod update_func; #[remain::sorted] #[derive(Debug, Error)] pub enum FuncAPIError { #[error("action prototype error: {0}")] ActionPrototype(#[from] dal::action::prototype::ActionPrototypeError), #[error("attribute value error: {0}")] AttributeValue(#[from] dal::attribute::value::AttributeValueError), #[error("cannot delete binding for func kind")] CannotDeleteBindingForFunc, #[error("cannot delete locked func: {0}")] CannotDeleteLockedFunc(FuncId), #[error("change set error: {0}")] ChangeSet(#[from] ChangeSetError), #[error("component error: {0}")] Component(#[from] ComponentError), #[error("cannot create non-transformation attribute function(")] CreatingAttributeFuncWithoutBinding, #[error("func error: {0}")] Func(#[from] FuncError), #[error("func already unlocked: {0}")] FuncAlreadyUnlocked(FuncId), #[error("func argument error: {0}")] FuncArgument(#[from] FuncArgumentError), #[error("func authoring error: {0}")] FuncAuthoring(#[from] FuncAuthoringError), #[error("func bindings error: {0}")] FuncBinding(#[from] FuncBindingError), #[error("The function name \"{0}\" is reserved")] FuncNameReserved(String), #[error("The function does not exist")] FuncNotFound(FuncId), #[error("hyper error: {0}")] Http(#[from] axum::http::Error), #[error("layer db error: {0}")] LayerDb(#[from] LayerDbError), #[error("missing action kind")] MissingActionKindForActionFunc, #[error("missing action prototype")] MissingActionPrototype, #[error("missing func id")] MissingFuncId, #[error("no input location given")] MissingInputLocationForAttributeFunc, #[error("no input locations given for leaf func")] MissingInputLocationForLeafFunc, #[error("no output location given")] MissingOutputLocationForAttributeFunc, #[error("missing prototype id")] MissingPrototypeId, #[error("missing schema varianta and func id for leaf func")] MissingSchemaVariantAndFunc, #[error("action function with multiple action types")] MultipleActionTypes(FuncId), #[error("function is not an action or asset schema function")] NoActionTypes(FuncId), #[error("prompt override error: {0}")] PromptOverride(#[from] dal::prompt_override::PromptOverrideError), #[error("qualification error: {0}")] Qualification(#[from] dal::qualification::QualificationError), #[error("schema error: {0}")] Schema(#[from] dal::SchemaError), #[error("schema error: {0}")] SchemaVariant(#[from] dal::SchemaVariantError), #[error("serde json error: {0}")] Serde(#[from] serde_json::Error), #[error("cannot set binding on transformation function")] SettingBindingOnTransformationFunction, #[error("transactions error: {0}")] Transactions(#[from] dal::TransactionsError), #[error("workspace snapshot error: {0}")] WorkspaceSnapshotError(#[from] dal::WorkspaceSnapshotError), #[error("wrong function kind for binding")] WrongFunctionKindForBinding, #[error("ws event error: {0}")] WsEvent(#[from] WsEventError), } pub type FuncAPIResult<T> = Result<T, FuncAPIError>; impl IntoResponse for FuncAPIError { fn into_response(self) -> Response { let err_string = self.to_string(); fn func_runner_err_to_status_and_message( err: FuncRunnerError, ) -> (StatusCode, Option<String>) { match err { FuncRunnerError::ResultFailure { kind, message, .. } => match kind { FunctionResultFailureErrorKind::VeritechServer => { (ApiError::DEFAULT_ERROR_STATUS_CODE, None) } FunctionResultFailureErrorKind::InvalidReturnType | FunctionResultFailureErrorKind::KilledExecution | FunctionResultFailureErrorKind::ActionFieldWrongType => { (StatusCode::UNPROCESSABLE_ENTITY, Some(message)) } FunctionResultFailureErrorKind::UserCodeException(lang_server_error_kind) => { let err = format!("{lang_server_error_kind}: {message}"); (StatusCode::UNPROCESSABLE_ENTITY, Some(err)) } }, _ => (ApiError::DEFAULT_ERROR_STATUS_CODE, None), } } let (status_code, maybe_message) = match self { Self::Transactions(dal::TransactionsError::BadWorkspaceAndChangeSet) => { (StatusCode::FORBIDDEN, None) } // these errors represent problems with the shape of the request Self::MissingActionKindForActionFunc | Self::MissingActionPrototype | Self::MissingFuncId | Self::MissingInputLocationForAttributeFunc | Self::MissingOutputLocationForAttributeFunc | Self::MissingPrototypeId | Self::MissingSchemaVariantAndFunc | Self::Func(FuncError::FuncLocked(_)) | Self::SchemaVariant(dal::SchemaVariantError::SchemaVariantLocked(_)) => { (StatusCode::BAD_REQUEST, None) } // Return 404 when the func is not found Self::FuncNotFound(_) | // When a graph node cannot be found for a schema variant, it is not found Self::SchemaVariant(dal::SchemaVariantError::NotFound(_)) => { (StatusCode::NOT_FOUND, None) }, // When the authoring changes requested would result in a cycle Self::FuncBinding(func_binding_error) if func_binding_error.is_create_graph_cycle() => { (StatusCode::UNPROCESSABLE_ENTITY, None) }, // Return 422 if the error is related to the user code being invalid Self::FuncAuthoring(FuncAuthoringError::AttributeValue(err)) => { match *err { AttributeValueError::FuncRunner(err) => { func_runner_err_to_status_and_message(*err) }, _ => { (ApiError::DEFAULT_ERROR_STATUS_CODE, None) }, } } Self::FuncAuthoring(FuncAuthoringError::FuncRunner(err)) => { func_runner_err_to_status_and_message(*err) }, _ => (ApiError::DEFAULT_ERROR_STATUS_CODE, None) }; ApiError::new(status_code, maybe_message.unwrap_or(err_string)).into_response() } } pub fn v2_routes() -> Router<AppState> { Router::new() // Func Stuff .route("/", get(list_funcs::list_funcs)) .route("/code", get(get_code::get_code)) // accepts a list of func_ids .route("/runs/:func_run_id", get(get_func_run::get_func_run)) .route( "/runs/:func_run_id/logs", get(get_func_run_logs::get_func_run_logs), ) .route( "/runs/paginated", get(get_func_runs_paginated::get_func_runs_paginated), ) .route( "/runs/latest_av/:attribute_value_id/logs", get(get_func_run_logs_av::get_func_run_logs_av), ) .route("/", post(create_func::create_func)) .route("/:func_id", put(update_func::update_func)) // only save the func's metadata .route("/:func_id/code", put(save_code::save_code)) // only saves func code .route("/:func_id/test_execute", post(test_execute::test_execute)) .route("/:func_id/execute", post(execute_func::execute_func)) .route( "/:func_id", post(create_unlocked_copy::create_unlocked_copy), ) .route("/:func_id", delete(delete_func::delete_func)) // Func Bindings .route( "/:func_id/bindings", post(binding::create_binding::create_binding), ) .route( "/:func_id/bindings", delete(binding::delete_binding::delete_binding), ) .route( "/:func_id/bindings", put(binding::update_binding::update_binding), ) // Reset Attribute Bindings .route( "/:func_id/reset_attribute_binding", post(binding::attribute::reset_attribute_binding::reset_attribute_binding), ) // Func Arguments .route( "/:func_id/arguments", post(argument::create_argument::create_func_argument), ) .route( "/:func_id/arguments/:func_argument_id", put(argument::update_argument::update_func_argument), ) .route( "/:func_id/arguments/:func_argument_id", delete(argument::delete_argument::delete_func_argument), ) } // helper to assemble the front end struct to return the code and types so SDF can decide when these events need to fire pub async fn get_code_response(ctx: &DalContext, func_id: FuncId) -> FuncAPIResult<FuncCode> { let func = Func::get_by_id_opt(ctx, func_id) .await? .ok_or(FuncAPIError::FuncNotFound(func_id))?; let code = func.code_plaintext()?.unwrap_or("".to_string()); Ok(FuncCode { func_id: func.id, code: code.clone(), }) }

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