Skip to main content
Glama
json.rs17.1 kB
use std::collections::HashMap; use serde::{ Deserialize, Serialize, }; use serde_json::Value; use si_pkg::{ MapKeyFuncSpec, PropSpec, SchemaSpec, SchemaSpecData, SchemaVariantSpec, SchemaVariantSpecData, SocketSpec, SocketSpecArity, SocketSpecData, SocketSpecKind, }; use crate::{ ComponentType, PropKind, SchemaVariantError, SocketArity, property_editor::schema::WidgetKind, schema::variant::{ DEFAULT_SCHEMA_VARIANT_COLOR, SchemaVariantResult, ValueFrom, value_from::SiPropValueFrom, }, }; #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct SchemaVariantMetadataJson { /// Name for the schema. pub schema_name: String, /// Version of this variant. pub version: String, /// Override for the UI name for this schema variant #[serde(alias = "display_name")] pub display_name: String, /// The category this schema variant belongs to pub category: String, /// The color for the component on the component diagram as a hex string pub color: String, #[serde(alias = "component_type")] pub component_type: ComponentType, pub link: Option<String>, pub description: Option<String>, } impl SchemaVariantMetadataJson { pub fn to_schema_spec(&self, variant: SchemaVariantSpec) -> SchemaVariantResult<SchemaSpec> { let mut builder = SchemaSpec::builder(); builder.name(&self.schema_name); let mut data_builder = SchemaSpecData::builder(); data_builder.name(&self.schema_name); data_builder.category(&self.category); data_builder.category_name(&self.display_name); builder.data(data_builder.build()?); builder.variant(variant); Ok(builder.build()?) } } /// The json definition for a [`SchemaVariant`](crate::SchemaVariant)'s [`Prop`](crate::Prop) tree (and /// more in the future). #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct SchemaVariantJson { /// The immediate child [`Props`](crate::Prop) underneath "/root/domain". #[serde(default)] pub props: Vec<PropDefinition>, /// The immediate child [`Props`](crate::Prop) underneath "/root/secrets". #[serde(default)] pub secret_props: Vec<PropDefinition>, /// The immediate child [`Props`](crate::Prop) underneath "/root/secretsDefinition". #[serde(default, skip_serializing_if = "Option::is_none")] pub secret_definition: Option<Vec<PropDefinition>>, /// The immediate child [`Props`](crate::Prop) underneath "/root/resource_value". #[serde(default)] pub resource_props: Vec<PropDefinition>, /// Identity relationships for [`Props`](crate::Prop) underneath "/root/si". #[serde(default)] pub si_prop_value_froms: Vec<SiPropValueFrom>, /// The input [`Sockets`](crate::Socket) and created for the [`variant`](crate::SchemaVariant). #[serde(default)] pub input_sockets: Vec<SocketDefinition>, /// The output [`Sockets`](crate::Socket) and created for the [`variant`](crate::SchemaVariant). #[serde(default)] pub output_sockets: Vec<SocketDefinition>, /// A map of documentation links to reference. To reference links (values) specify the key via /// the "doc_link_ref" field for a [`PropDefinition`]. #[serde(skip_serializing_if = "Option::is_none")] pub doc_links: Option<HashMap<String, String>>, /// Extra optional UI data we're just passing all the way through #[serde(flatten)] pub ui_optionals: serde_json::Value, } impl SchemaVariantJson { pub fn to_spec( &self, metadata: SchemaVariantMetadataJson, identity_func_unique_id: &str, asset_func_spec_unique_id: &str, ) -> SchemaVariantResult<SchemaVariantSpec> { let SchemaVariantMetadataJson { schema_name: _, // we store this elsewhere category: _, // we store this elsewhere version, display_name, color, component_type, link, description, } = metadata; let mut builder = SchemaVariantSpec::builder(); builder.version(version.clone()); let mut data_builder = SchemaVariantSpecData::builder(); data_builder.version(version.clone()); data_builder.display_name(display_name); data_builder.color(color); data_builder.component_type(component_type); if let Some(link) = link { data_builder.try_link(link.as_str())?; } data_builder.description(description); data_builder.func_unique_id(asset_func_spec_unique_id); builder.data(data_builder.build()?); for si_prop_value_from in &self.si_prop_value_froms { builder.si_prop_func(si_prop_value_from.to_spec(identity_func_unique_id)); } for prop in &self.props { builder.domain_prop(prop.to_spec(identity_func_unique_id)?); } for prop in &self.secret_props { builder.secret_prop(prop.to_spec(identity_func_unique_id)?); } if let Some(props) = &self.secret_definition { for prop in props { builder.secret_definition_prop(prop.to_spec(identity_func_unique_id)?); } } for resource_prop in &self.resource_props { builder.resource_value_prop(resource_prop.to_spec(identity_func_unique_id)?); } for input_socket in &self.input_sockets { builder.socket(input_socket.to_spec(true, identity_func_unique_id)?); } for output_socket in &self.output_sockets { builder.socket(output_socket.to_spec(false, identity_func_unique_id)?); } Ok(builder.build()?) } pub fn metadata_from_spec( schema_spec: SchemaSpec, ) -> SchemaVariantResult<SchemaVariantMetadataJson> { let schema_data = schema_spec.data.unwrap_or(SchemaSpecData { name: schema_spec.name.to_owned(), default_schema_variant: None, category: "".into(), category_name: None, ui_hidden: false, }); let default_variant_spec = match schema_data.default_schema_variant { Some(default_variant_unique_id) => schema_spec .variants .iter() .find(|variant| variant.unique_id.as_deref() == Some(&default_variant_unique_id)) .ok_or(SchemaVariantError::DefaultVariantNotFound( default_variant_unique_id, ))?, None => schema_spec .variants .last() .ok_or(SchemaVariantError::NoVariants)?, }; let variant_spec_data = default_variant_spec .data .to_owned() .unwrap_or(SchemaVariantSpecData { version: "v0".into(), display_name: None, color: None, link: None, component_type: si_pkg::SchemaVariantSpecComponentType::Component, func_unique_id: "0".into(), description: None, }); let metadata = SchemaVariantMetadataJson { version: variant_spec_data.version, schema_name: schema_spec.name.clone(), display_name: schema_data.category_name.unwrap_or(schema_spec.name), category: schema_data.category, color: variant_spec_data .color .to_owned() .unwrap_or(DEFAULT_SCHEMA_VARIANT_COLOR.into()), component_type: variant_spec_data.component_type.into(), link: variant_spec_data.link.as_ref().map(|l| l.to_string()), description: variant_spec_data.description.to_owned(), }; Ok(metadata) } } #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct PropWidgetDefinition { /// The [`kind`](crate::property_editor::schema::WidgetKind) of the [`Prop`](crate::Prop) to be created. kind: WidgetKind, /// The `Option<Value>` of the [`kind`](crate::property_editor::schema::WidgetKind) to be created. #[serde(default)] options: Option<Value>, } #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct MapKeyFunc { pub key: String, #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] pub value_from: Option<ValueFrom>, } impl MapKeyFunc { pub fn to_spec(&self, identity_func_unique_id: &str) -> SchemaVariantResult<MapKeyFuncSpec> { let mut builder = MapKeyFuncSpec::builder(); builder.func_unique_id(identity_func_unique_id); builder.key(&self.key); if let Some(value_from) = &self.value_from { builder.input(value_from.to_spec()); }; Ok(builder.build()?) } } /// The definition for a [`Prop`](crate::Prop) in a [`SchemaVariant`](crate::SchemaVariant). #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct PropDefinition { /// The name of the [`Prop`](crate::Prop) to be created. pub name: String, /// The [`kind`](crate::PropKind) of the [`Prop`](crate::Prop) to be created. pub kind: PropKind, /// An optional reference to a documentation link in the "doc_links" field for the /// [`SchemaVariantJson`] for the [`Prop`](crate::Prop) to be created. #[serde(skip_serializing_if = "Option::is_none")] pub doc_link_ref: Option<String>, /// An optional documentation link for the [`Prop`](crate::Prop) to be created. #[serde(skip_serializing_if = "Option::is_none")] pub doc_link: Option<String>, /// An optional set of inline documentation for the [`Prop`](crate::Prop) to be created. #[serde(skip_serializing_if = "Option::is_none")] pub documentation: Option<String>, /// If our [`kind`](crate::PropKind) is [`Object`](crate::PropKind::Object), specify the /// child definition(s). #[serde(default)] pub children: Vec<PropDefinition>, /// If our [`kind`](crate::PropKind) is [`Array`](crate::PropKind::Array), specify the entry /// definition. #[serde(skip_serializing_if = "Option::is_none")] pub entry: Option<Box<PropDefinition>>, /// The [`WidgetDefinition`](crate::schema::variant::json::PropWidgetDefinition) of the /// [`Prop`](crate::Prop) to be created. #[serde(default, skip_serializing_if = "Option::is_none")] pub widget: Option<PropWidgetDefinition>, #[serde(default, skip_serializing_if = "Option::is_none")] // The source of the information for the prop pub value_from: Option<ValueFrom>, // Whether the prop is hidden from the UI #[serde(default, skip_serializing_if = "Option::is_none")] pub hidden: Option<bool>, #[serde(default, skip_serializing_if = "Option::is_none")] pub validation_format: Option<String>, #[serde(default, skip_serializing_if = "Option::is_none")] pub default_value: Option<serde_json::Value>, #[serde(default, skip_serializing_if = "Option::is_none")] pub map_key_funcs: Option<Vec<MapKeyFunc>>, #[serde(default, skip_serializing_if = "Option::is_none")] pub suggest_sources: Option<Vec<PropSuggestion>>, #[serde(default, skip_serializing_if = "Option::is_none")] pub suggest_as_source_for: Option<Vec<PropSuggestion>>, } /// Suggested property for suggestedSources and suggestAsSourceFor to link to #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct PropSuggestion { /// The name of the schema the property is on, e.g. "AWS::EC2::VPC" pub schema: String, /// The path to the property (e.g. "/resource_value/VpcId") pub prop: String, } impl PropDefinition { pub fn to_spec(&self, identity_func_unique_id: &str) -> SchemaVariantResult<PropSpec> { let PropDefinition { name, kind, doc_link_ref: _, // TODO why is this not being copied doc_link, documentation, children, entry, widget, value_from, hidden, validation_format, default_value, map_key_funcs, suggest_sources, suggest_as_source_for, } = self; let mut builder = PropSpec::builder(); builder.name(name); builder.kind(*kind); builder.has_data(true); if let Some(doc_url) = doc_link { builder.try_doc_link(doc_url.as_str())?; } if let Some(docs) = documentation { builder.documentation(docs); } if let Some(default_value) = &default_value { builder.default_value(default_value.to_owned()); } match kind { PropKind::Array | PropKind::Map => { if let Some(entry) = &entry { builder.type_prop(entry.to_spec(identity_func_unique_id)?); } } PropKind::Object => { for child in children { builder.entry(child.to_spec(identity_func_unique_id)?); } } _ => {} } if let Some(widget) = widget { builder.widget_kind(widget.kind); if let Some(widget_options) = &widget.options { builder.widget_options(widget_options.to_owned()); } } if let Some(value_from) = value_from { builder.func_unique_id(identity_func_unique_id); builder.input(value_from.to_spec()); } if let Some(hidden) = hidden { builder.hidden(*hidden); } if let Some(map_key_funcs) = map_key_funcs { for map_key_func in map_key_funcs { builder.map_key_func(map_key_func.to_spec(identity_func_unique_id)?); } } if let Some(validation_format) = validation_format { builder.validation_format(validation_format); } let mut ui_optionals = HashMap::new(); if let Some(suggest_sources) = suggest_sources { ui_optionals.insert( "suggestSources".to_string(), serde_json::to_value(suggest_sources)?, ); } if let Some(suggest_as_source_for) = suggest_as_source_for { ui_optionals.insert( "suggestAsSourceFor".to_string(), serde_json::to_value(suggest_as_source_for)?, ); } if !ui_optionals.is_empty() { builder.ui_optionals(ui_optionals); } Ok(builder.build()?) } } /// The definition for a [`Socket`](crate::Socket) in a [`SchemaVariant`](crate::SchemaVariant). #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct SocketDefinition { /// The name of the [`Socket`](crate::Socket) to be created. pub name: String, /// The type identifier of the [`Socket`](crate::Socket) to be created. pub connection_annotations: String, /// The [`arity`](https://en.wikipedia.org/wiki/Arity) of the [`Socket`](crate::Socket). /// Defaults to [`SocketArity::Many`](crate::SocketArity::Many) if nothing is provided. #[serde(skip_serializing_if = "Option::is_none")] pub arity: Option<SocketArity>, #[serde(skip_serializing_if = "Option::is_none")] pub ui_hidden: Option<bool>, // The source of the information for the socket #[serde(skip_serializing_if = "Option::is_none")] pub value_from: Option<ValueFrom>, } impl SocketDefinition { pub fn to_spec( &self, is_input: bool, identity_func_unique_id: &str, ) -> SchemaVariantResult<SocketSpec> { let mut builder = SocketSpec::builder(); let mut data_builder = SocketSpecData::builder(); builder.name(&self.name); data_builder.name(&self.name); data_builder.connection_annotations(&self.connection_annotations); if is_input { data_builder.kind(SocketSpecKind::Input); } else { data_builder.kind(SocketSpecKind::Output); } if let Some(arity) = &self.arity { data_builder.arity(arity); } else { data_builder.arity(SocketSpecArity::Many); } if let Some(hidden) = &self.ui_hidden { data_builder.ui_hidden(*hidden); } else { data_builder.ui_hidden(false); } if let Some(value_from) = &self.value_from { data_builder.func_unique_id(identity_func_unique_id); builder.input(value_from.to_spec()); } builder.data(data_builder.build()?); Ok(builder.build()?) } }

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